PlaceboInTime#
- class causalpy.checks.placebo_in_time.PlaceboInTime[source]#
Placebo-in-time sensitivity check with hierarchical null model.
Shifts the treatment time backward into the pre-intervention period to create
n_foldsplacebo experiments. Extracts the posterior cumulative impact from each fold, then fits a hierarchical Bayesian model to characterise the “status quo” distribution of effects when no intervention occurred. The actual intervention’s cumulative effect is compared against this learned null.When
expected_effect_priorandrope_half_widthare provided, additionally computes exact closed-form Bayesian assurance (operating characteristics).- Parameters:
n_folds (
int) – Number of placebo folds to create. Must be >= 1. Each fold consumes oneintervention_lengthof pre-treatment history, so on a short pre-period a largen_foldsproduces ineligible folds that are skipped; shortenintervention_lengthto fit more folds.selection_method (
Literal['sequential','random']) –How to choose placebo windows.
"sequential"— evenly-spaced sliding windows stepping backward from the treatment time (original behaviour)."random"— randomly sample eligible windows from the pre-intervention period, subject tomin_training_pct,min_gap, andexclude_periodsconstraints.
Every placebo fold must have at least one full intervention window of observed pre-treatment history. Sequential folds that do not meet this rule are skipped with a warning and deterministic
skipped_foldsmetadata; random selection excludes them from its candidate pool.min_training_pct (
float) –(random mode only) Minimum fraction of total pre-period observations that must precede each candidate placebo window.
Note: the eligible pre-period is further shortened because a candidate’s pseudo-intervention window must also end before the actual treatment. With the derived default
intervention_length(see below) that window is roughly the post-period length, which can make the effective eligible window much smaller than(1 - min_training_pct)suggests; pass an explicitintervention_lengthto widen it.min_gap (
int) – (random mode only) Minimum number of pre-intervention observations between any two selected folds, measured as positions in the sorted pre-period index. The default of1only forbids picking the same candidate twice; use a larger value to spread folds further apart. Whenallow_overlapisFalse(the default) non-overlap of pseudo-intervention windows is enforced independently ofmin_gap.allow_overlap (
bool) – (random mode only) IfFalse(the default), selected pseudo-intervention windows are required to be non-overlapping in index/time units. Two folds at timest_aandt_bare considered non-overlapping whenabs(t_a - t_b) >= intervention_length. Set toTrueto allow overlapping windows, which relaxes the constraint at the cost of violating the exchangeability assumption of the hierarchical status-quo model (each fold mean is treated as an independent draw from a commonmu_status_quo).exclude_periods (
set[str] |None) – (random mode only) Set of period labels to exclude from candidate selection. For datetime-indexed data, use"YYYY-MM"strings; for numeric indices, use string representations of the index values.experiment_factory (
Any|None) – Custom factory(data, treatment_time) -> BaseExperiment. IfNone(default), the factory is derived from the pipeline’sexperiment_config. Required for standalone (non-pipeline) use. This is the escape hatch for adapting the model to the eligible placebo-fold data; custom factories remain responsible for any model-specific randomness they introduce.sample_kwargs (
dict[str,Any] |None) – MCMC settings for the hierarchical status-quo model. Defaults to{"draws": 1000, "chains": 4, "target_accept": 0.97}.threshold (
float) – Finite probability cutoff in(0, 1). Used both forpassed(P(actual effect outside null) must exceed this) and for the ROPE decision rule when computing assurance.prior_scale (
float) – Multiplier for auto-computed prior widths on the hierarchical model. The priors aremu ~ Normal(center, 5 * prior_scale * data_scale)andtau ~ HalfNormal(2 * prior_scale * data_scale).expected_effect_prior (
Any|None) – Prior belief about the true total effect under the alternative hypothesis. Accepts a frozen distribution exposing.cdfand.sf, any object with an.rvs(n)method (PreliZ, scipy), or a numpy array of pre-drawn samples. Frozen distributions and arrays are evaluated directly by the exact closed-form assurance calculation. Whenrandom_seedis set, RVS-only distributions exposingrandom_statereceive a derived Generator; legacy.rvs(n)distributions remain supported but emit a reproducibility warning and are recorded in result metadata. Provided together withrope_half_width, assurance analysis runs automatically.rope_half_width (
float|None) – Finite nonnegative half-width of the ROPE interval[-rope, +rope]. Required whenexpected_effect_prioris provided.n_design_replications (
int|None) – Number of prior samples drawn only for RVS-only expected-effect priors. Defaults to the number of status-quo samples. It has no effect for numpy arrays or frozen priors with.cdfand.sf.random_seed (
int|None) – posterior predictive sampling, and RVS-only expected-effect-prior sampling. It also seeds the hierarchicalpm.samplecall unlesssample_kwargs["random_seed"]is explicitly supplied, which takes precedence for that call only.intervention_length (
Any|None) –Length of each placebo intervention window, in index units. When
None(default) the length is derived from the experiment:treatment_end_time - treatment_timewhen the experiment defines an explicit intervention window, otherwisedata.index.max() - treatment_time(roughly the post-period length).Set this explicitly to fit more well-supported folds into a short pre-period. The derived default consumes one post-period worth of history per fold, so when the pre-period is only a few times longer than the post-period the earliest folds fail the eligibility rule above and are skipped. A shorter
intervention_lengthshortens both the placebo window and the history each fold requires, so more folds become eligible.The actual effect is still summarised over the full post-intervention period, so a window materially shorter than that period compares a long actual cumulative impact against a null built from short windows, which inflates
P(actual outside null). Both observation counts are recorded inmetadata["comparison_window"]and a warning is emitted when they disagree by more than the one-observation half-open-window artefact of the derived default.make_figures (
bool) – Whetherrun()appends the calibration figure produced byplot_calibration()toCheckResult.figures. Every run produces one, sofigures[0]is safe to read; a run that reaches no verdict gets the annotated placeholder instead of the panels.
Examples
>>> import causalpy as cp >>> check = cp.checks.PlaceboInTime(n_folds=3)
Random selection with constraints:
>>> check = cp.checks.PlaceboInTime( ... n_folds=4, ... selection_method="random", ... min_training_pct=0.30, ... min_gap=2, ... random_seed=42, ... )
Methods
Apply a ROPE-based Bayesian decision rule.
PlaceboInTime.plot_calibration(check_result)Plot the three-panel calibration diagnostic for a placebo run.
PlaceboInTime.run(experiment[, context])Run placebo-in-time analysis with hierarchical null model.
PlaceboInTime.validate(experiment)Check the experiment is compatible with PlaceboInTime.
Attributes
applicable_methods- __init__(n_folds=3, selection_method='sequential', min_training_pct=0.3, min_gap=1, allow_overlap=False, exclude_periods=None, experiment_factory=None, sample_kwargs=None, threshold=0.95, prior_scale=1.0, expected_effect_prior=None, rope_half_width=None, n_design_replications=None, random_seed=None, intervention_length=None, make_figures=True)[source]#
- Parameters:
n_folds (int)
selection_method (Literal['sequential', 'random'])
min_training_pct (float)
min_gap (int)
allow_overlap (bool)
experiment_factory (Any | None)
threshold (float)
prior_scale (float)
expected_effect_prior (Any | None)
rope_half_width (float | None)
n_design_replications (int | None)
random_seed (int | None)
intervention_length (Any | None)
make_figures (bool)
- Return type:
None
- classmethod __new__(*args, **kwargs)#