Power Curves¶
Simulation-based power analysis reads a raw 0/1 indicator observable, such as
"the simulated trial rejected the null", from replicated run_grid rows.
power_curve orders the per-design success probability along one size factor,
separately for every combination of the other factors. minimum_adequate then
reports the smallest scanned size whose power meets a target.
curves = power_curve(raw_results, "reject", "n_participants", adjustment="bonferroni")
for choice in minimum_adequate(curves, 0.8):
print(choice.stratum, choice.value, choice.monotone_above)
Each interval is a fixed-sample
proportion interval for independent replicates; the caller
establishes common, independent replication. Without adjustment, the bound
at the selected size is marginal. Choosing the first crossing from a scanned
curve is a selection, so adjustment="bonferroni" widens each interval to keep
the nominal level simultaneously across the stratum's scanned values.
monotone_above=False reports a curve that falls back below the target at a
larger size; curves are never smoothed or interpolated, and the result is only
as precise as the scanned grid.
trade_study.PowerCurve(indicator, along, stratum, values, intervals, confidence, family_confidence, adjustment)
dataclass
¶
Success-probability intervals for one stratum along a size factor.
Attributes:
| Name | Type | Description |
|---|---|---|
indicator |
str
|
Name of the 0/1 indicator observable. |
along |
str
|
Name of the ordered size factor. |
stratum |
dict[str, Any]
|
Values of every other configuration factor. |
values |
tuple[Any, ...]
|
Sorted, distinct values of |
intervals |
tuple[ProportionInterval, ...]
|
Fixed-sample interval at each value, in |
confidence |
float
|
Two-sided level used for each interval. |
family_confidence |
float
|
Nominal simultaneous level across this stratum's
values; equals |
adjustment |
str | None
|
|
trade_study.AdequateDesign(stratum, along, value, interval, target, rule, monotone_above)
dataclass
¶
Smallest size along a power curve that meets a target.
Attributes:
| Name | Type | Description |
|---|---|---|
stratum |
dict[str, Any]
|
Values of every other configuration factor. |
along |
str
|
Name of the ordered size factor. |
value |
Any
|
Selected size, or |
interval |
ProportionInterval | None
|
Interval at the selected size, or |
target |
float
|
Required success probability. |
rule |
str
|
|
monotone_above |
bool
|
Whether every larger scanned size also qualifies.
Always |
trade_study.power_curve(results, indicator, along, *, confidence=0.95, method='wilson', adjustment=None)
¶
Build per-stratum power curves from raw replicate rows.
Rows are grouped by design point exactly as in
:meth:~trade_study.protocols.ResultsTable.aggregate_replicates, which
validates the 0/1 indicator and unique replicate identities. Each
interval is a fixed-sample interval for i.i.d. replicates. With
adjustment="bonferroni" each stratum's intervals use level
1 - (1 - confidence) / m for its m scanned values, so that
selecting a size from the curve keeps nominal confidence
simultaneously across those values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results
|
ResultsTable
|
Raw (unaggregated) replicate rows from |
required |
indicator
|
str
|
Name of a 0/1 indicator observable. |
required |
along
|
str
|
Configuration factor that orders the curve (for example a sample size). Values must be mutually orderable. |
required |
confidence
|
float
|
Two-sided family level in (0, 1). |
0.95
|
method
|
str
|
|
'wilson'
|
adjustment
|
Literal['bonferroni'] | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
list[PowerCurve]
|
One curve per combination of the remaining factors, in first-seen |
list[PowerCurve]
|
order. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import numpy as np
>>> from trade_study import ResultsTable
>>> rows = [(n, r) for n in (10, 20) for r in range(4)]
>>> table = ResultsTable(
... [{"n": n} for n, _ in rows],
... np.array([[float(n == 20 or r < 2)] for n, r in rows]),
... ["reject"],
... metadata=[{"design_point": n, "rep": r} for n, r in rows],
... )
>>> [c.estimate for c in power_curve(table, "reject", "n")[0].intervals]
[0.5, 1.0]
Source code in src/trade_study/power.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
trade_study.minimum_adequate(curves, target, *, rule='lower_bound')
¶
Select the smallest size on each curve whose power meets target.
"lower_bound" requires the interval's lower bound to reach the
target; "estimate" uses the observed proportion. A curve that
dips below the target above the selection is reported through
monotone_above=False rather than smoothed. Unless the curves were
built with adjustment="bonferroni", the lower bound is marginal at
the selected size only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
curves
|
list[PowerCurve]
|
Output of :func: |
required |
target
|
float
|
Required success probability in [0, 1]. |
required |
rule
|
Literal['lower_bound', 'estimate']
|
|
'lower_bound'
|
Returns:
| Type | Description |
|---|---|
list[AdequateDesign]
|
One selection per curve, in the same order. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import numpy as np
>>> from trade_study import ResultsTable
>>> rows = [(n, r) for n in (10, 20) for r in range(4)]
>>> table = ResultsTable(
... [{"n": n} for n, _ in rows],
... np.array([[float(n == 20 or r < 2)] for n, r in rows]),
... ["reject"],
... metadata=[{"design_point": n, "rep": r} for n, r in rows],
... )
>>> curves = power_curve(table, "reject", "n")
>>> minimum_adequate(curves, 0.8, rule="estimate")[0].value
20