A controlled interrupted series subtracts the comparison series’ level and slope breaks from the treated series’ breaks at the same intervention date.
Controlled interrupted time series with a comparison series
Choose a comparison that shares shocks
North’s support queue receives the September routing rule; a separate South queue does not. Both face the same billing release and seasonal ticket surge. If South is measured the same way and remains unexposed, its trajectory may reveal changes that a North-only model would wrongly assign to the rule. Selection is a design argument, not a correlation contest. A South queue with shared overflow routing could receive an indirect effect and fail as a control. The spillover lesson screens that risk.
Subtract changes, not levels
Fit or derive a level and slope break for North and South at the same September cutoff. The controlled level contrast is North’s level change minus South’s level change; the controlled slope contrast is the corresponding difference in slope changes. A constant difference in baseline rates does not by itself invalidate this contrast. The harder question is whether the untreated breaks would have been comparable in the absence of the policy.
Check the earlier relationship
Plot the treated-minus-control rate difference over the pre-policy months. A drifting difference means the two queues did not track in a simple way; a controlled segmented model can include group-specific pretrends, but extrapolation becomes more fragile. Check calendar alignment, denominators, case mix and prior policy changes. A comparison affected by a different concurrent policy may introduce bias instead of removing it. Pretrend audits provide complementary checks.
Interpret the worked contrast
In the fixture, North’s own immediate break is -0.06 rate points and South’s is -0.02, producing a controlled level contrast of -0.04. North’s slope change is -0.003 per month and South’s is +0.001, producing a controlled slope contrast of -0.004 per month. The code performs the final contrast on estimated inputs; the actual line fits and their uncertainty must be done upstream. Segmented regression explains those inputs.
Do not let a control create certainty
One control cannot guarantee shared counterfactual shocks. The two series may respond differently to holidays or ticket mix, and residual errors can be correlated within and across time. Report a clear comparison selection rule, all observed and modeled paths, and a sensitivity check with another eligible control where possible. The review project treats absent comparison support as a limitation rather than inventing a match.
Implementation
def controlled_break(treated_break, comparison_break):
required = {"level", "slope"}
if set(treated_break) != required or set(comparison_break) != required:
raise ValueError("both fitted breaks need level and slope")
return {measure: treated_break[measure] - comparison_break[measure]
for measure in required}
treated = {"level": -.06, "slope": -.003}
comparison = {"level": -.02, "slope": .001}
net_break = controlled_break(treated, comparison)
assert abs(net_break["level"] - (-.04)) < 1e-12
assert abs(net_break["slope"] - (-.004)) < 1e-12
def gap_at_horizon(breaks, months_after_first_post):
if months_after_first_post < 0:
raise ValueError("negative horizon")
return breaks["level"] + months_after_first_post * breaks["slope"]
assert abs(gap_at_horizon(net_break, 3) - (-.052)) < 1e-12Performance and operating cost
The final subtraction and horizon calculation are O(1) time and space. Fitting G group series over T observations costs at least O(G × T) data access; uncertainty estimation must respect serial dependence and any shared shocks.
Common Mistakes
- Do not select a comparison using post-policy outcomes.
- Do not use a queue indirectly exposed through shared routing as untreated.
- Do not confuse a difference in baseline levels with a difference in breaks.
