
Monte Carlo power analysis for two-condition demand designs
Source:R/power-demand.R
power_demand.RdEstimates statistical power to detect a single fixed-effect difference in a
demand parameter (Q0 or alpha) between two conditions, by simulation:
each replicate (1) simulates a two-condition dataset from the mixed-effects
demand model in .simulate_within_subject_demand() under assumed
population parameters plus the effect delta – either a within-subject
design (every subject observed in both conditions) or, with design_type = "between", a two-arm between-subject design (each subject in one
condition) – (2) refits it with fit_demand_tmb(), and (3) tests the
condition contrast on the estimation (natural log) scale with a Wald test
at level alpha, referred to a t distribution with df degrees of freedom
(see the df argument). Power is the proportion of usable fits
(converged, positive-definite Hessian, finite standard error) that reject.
Because the power estimate is a proportion from finitely many replicates,
it is reported with a Wilson score confidence interval (power_mc_ci).
Both a p-value verdict (p < alpha) and a confidence-interval verdict
(Wald CI excludes 0) are recorded per replicate; they use the same
standard error and reference distribution, so they coincide by
construction, and both rates are returned.
Arguments
- n_subjects
Number of simulated subjects per replicate. For
design_type = "within"each subject is observed at every price in both conditions; for"between"this is the total sample, splitceiling(n_subjects / 2)to condition 1 and the rest to condition 2 (an odd total therefore gives arms differing by one subject).- effect
Named list supplying exactly one of
delta_q0ordelta_alpha: the true condition shift on natural-log Q0 (or natural-log alpha) for condition 2 relative to condition 1.0is allowed (useful for Type I error checks). E.g.delta_q0 = log(1.5)means condition 2's Q0 is 1.5 times condition 1's.- design
Named list of data-generating settings, merged over the simulator defaults:
prices(vector),log_q0_pop,log_alpha_pop,sigma_b(per-condition subject SD on log Q0),sigma_d(same for log alpha),rho_bd(must be 0 in this version), andsigma_e(residual SD on log consumption). All SDs must be strictly positive.- n_sim
Number of Monte Carlo replicates. 500 (default) is suitable for interactive exploration; use 2000+ for grant-quality precision (see
vignette("power-analysis")).- alpha
Nominal two-sided test level.
- df
Degrees of freedom for the Wald test's t reference distribution.
NULL(default) usesn_subjects - 1fordesign_type = "within"andn_subjects - 2fordesign_type = "between"(the two-sample df). This is an empirically calibrated small-sample correction, not a model-derived df (the TMB fit has no exact t sampling theory): the asymptotic z-test was measurably anticonservative in the package's Type I calibration battery (empirical rate 0.089 at nominal .05 with 15 subjects), while the t reference passes the battery's null checks across the tested sample sizes, target parameters, residual-SD settings, and both designs.Infgives the asymptotic z-test.- seed
Optional integer seed; identical seeds give identical results. The caller's RNG state is restored on exit.
- equation
Demand equation passed to
fit_demand_tmb(). The default"simplified"shares the simulator's mean function, so the simulateddeltaand the fitted contrast share the same scale. Note the error model is a working model: the simulator draws multiplicative lognormal errors while"simplified"fits additive Gaussian errors on raw consumption; the approximation is closest at smallsigma_e, and Type I calibration is verified by the test suite at the default and a 3x-largersigma_e. Other equations are sensitivity analyses with a different estimand – their contrasts are not on the scale of the simulated delta.- random_effects
Random-effects specification passed to
fit_demand_tmb().NULL(default) resolves to a specification matchingdesign_type:nlme::pdDiag(Q0 + alpha ~ condition - 1)for"within"(independent per-condition subject effects on both parameters, matching the simulator's data-generating process whenrho_bd = 0), andnlme::pdDiag(Q0 + alpha ~ 1)for"between"(per-subject intercepts, which are correctly specified because each subject appears in only one condition). Supply a specification to override.- multi_start
Passed to
fit_demand_tmb(). Defaults toFALSEfor speed (roughly 3x fewer optimizations); non-convergent replicates are excluded and surfaced rather than biasing the estimate.- verbose
Logical; show a progress bar.
- design_type
Either
"within"(default) or"between"."within"simulates the two-condition within-subject design (every subject observed at every price in both conditions) and tests the within-subject condition contrast."between"assigns each subject to exactly one of two arms (ceiling(n/2)to condition 1, the rest to condition 2) and tests the group difference; the two-arm dataset is composed from the same simulator run once per arm with a single condition, so no new data-generating process is introduced (see Details). Thedfandrandom_effectsdefaults trackdesign_type.- ...
Additional arguments passed to
fit_demand_tmb()(e.g.tmb_control).
Value
An object of class beezdemand_power: a list with
- power
Estimated power: proportion of usable replicates whose Wald CI excludes 0 (equal to
hit_rate_ci).NAif no replicate was usable.- power_mc_ci
Wilson 95% confidence interval on
power, reflecting Monte Carlo uncertainty fromn_usedreplicates.- hit_rate_p
Proportion of usable replicates with
p < alpha.- hit_rate_ci
Proportion of usable replicates whose Wald CI excludes 0 (the same decision rule as
hit_rate_p, since both use the same SE and t reference; both reported).- n_sim
Total replicates attempted.
- n_converged
Replicates whose fit converged.
- n_hessian_pd
Replicates with a positive-definite Hessian.
- n_used
Replicates entering the power denominator (converged, positive-definite Hessian, finite SE).
- alpha
Nominal test level.
- df
Degrees of freedom of the t reference distribution actually used (
n_subjects - 1for"within",n_subjects - 2for"between", unless overridden).- effect
The validated effect specification (name and delta).
- target_term
The tested coefficient (e.g.
"Q0:conditionC2").- design
The merged design list actually used.
- n_subjects
As supplied.
- replicates
Tibble with one row per replicate:
sim,status("ok","nonconverged","hessian_not_pd","se_unusable","error"),converged,hessian_pd,estimate,se,statistic,p_value,ci_lower,ci_upper,hit_p,hit_ci, andmessage(error text, if any). Estimates are on the natural-log scale of the simulated delta.- seed
As supplied.
- settings
List of
equation,design_type,multi_start, and the deparsed random-effects specification.- call
The matched call.
Details
A replicate whose fit fails (non-convergence, non-positive-definite
Hessian, unusable standard error, or an error) is excluded from the power
denominator and reported through the n_* counts and $replicates$status
– it is never counted as "no effect detected", which would bias power in
an unpredictable direction. A warning is issued when fewer than 95% of
replicates are usable, since power conditional on convergence can be
selected when convergence depends on the realized data.
The v1 scope is a single fixed-effect delta. Joint Q0 + alpha effects,
power for derived measures (Pmax, Omax), and arbitrary designs are out of
scope; see vignette("power-analysis").
For design_type = "between", the two arms are composed by running the
within-subject simulator once per arm with a single condition, then binding
the arms and refitting with per-subject intercept random effects. Because
each subject appears in only one condition, that random-effects structure
matches the composed data-generating process exactly – unlike the
within-subject default's per-condition effects. The additive-Gaussian
residual likelihood remains a working model for the simulator's
multiplicative-lognormal errors in both designs (closest at small
sigma_e). Type I error is calibrated by the test suite for both designs.
See also
find_n_demand() to search for the smallest adequate sample
size; fit_demand_tmb() for the model being refit.
Other power-analysis:
find_n_demand()
Examples
# \donttest{
# Tiny illustrative run (use n_sim >= 500 for real planning; the
# Monte Carlo interval at n_sim = 5 is uninformative by design)
res <- power_demand(
n_subjects = 12,
effect = list(delta_q0 = log(1.5)),
n_sim = 5, seed = 1, verbose = FALSE
)
print(res)
#> Monte Carlo power analysis (beezdemand)
#> Design: within-subject (2 conditions)
#> Target: Q0:conditionC2 (delta_q0 = 0.4055), two-sided alpha = 0.05, t reference (df = 11)
#> n_subjects = 12, n_sim = 5 (converged 5, usable 5)
#> Power (CI-exclusion): 1.000 [95% MC CI 0.566, 1.000]
#> p-value hit rate: 1.000
# Between-subject design: group difference in Q0 across two arms
res_b <- power_demand(
n_subjects = 16,
effect = list(delta_q0 = log(1.5)),
design_type = "between",
n_sim = 5, seed = 1, verbose = FALSE
)
print(res_b)
#> Monte Carlo power analysis (beezdemand)
#> Design: between-subject (2 conditions)
#> Target: Q0:conditionC2 (delta_q0 = 0.4055), two-sided alpha = 0.05, t reference (df = 14)
#> n_subjects = 16, n_sim = 5 (converged 5, usable 5)
#> Power (CI-exclusion): 0.800 [95% MC CI 0.376, 0.964]
#> p-value hit rate: 0.800
# }