Description
Patsy encodes a bare pandas datetime formula factor as categorical levels. CausalPy should instead interpret bare datetime predictors as continuous elapsed time, while retaining C(date) as the explicit syntax for date fixed effects. This policy should apply consistently to every CausalPy experiment that accepts a Patsy formula, not only ITS.
This is especially visible in PiecewiseITS, whose documentation currently shows date as a baseline term. The documented formula fits date dummies instead of a continuous baseline trend. In InterruptedTimeSeries, the same formula fails when predicting post-treatment observations because their datetime levels were not present in the pre-treatment design matrix.
SyntheticControl and SyntheticDifferenceInDifferences are out of scope: they use DatetimeIndex and treatment_time, not Patsy formulas.
Steps to Reproduce
- Create monthly data with a datetime
date column and an outcome y.
- Fit
PiecewiseITS with formula="y ~ 1 + date + step(date, '2020-05-01') + ramp(date, '2020-05-01')" and inspect result.labels.
- Fit
InterruptedTimeSeries on the same data indexed by date with formula="y ~ 1 + date" and a treatment date before the final observation.
Expected Behavior
- A bare datetime predictor such as
date is represented by one continuous column, measured as elapsed days from a stable training-data origin.
C(date) retains Patsy's normal categorical expansion for explicit date fixed effects.
- The stored origin is reused for out-of-sample design matrices, so ITS pre/post features remain compatible.
step(date, ...) and ramp(date, ...) retain their existing datetime behavior.
Actual Behavior
Patsy expands date into one dummy column per timestamp. PiecewiseITS can therefore fit a near-saturated date-level model instead of a baseline time trend. InterruptedTimeSeries builds the pre-period categories first and then raises PatsyError for unseen post-period dates.
The custom step(date, ...) and ramp(date, ...) transforms are not affected: they handle datetimes correctly, with ramp expressed in elapsed days.
Scope
Apply the policy to all formula-based experiments: InterruptedTimeSeries, PiecewiseITS, DifferenceInDifferences, StaggeredDifferenceInDifferences, PanelRegression, PrePostNEGD, RegressionDiscontinuity, RegressionKink, InversePropensityWeighting, and InstrumentalVariable. Existing intentional fixed-effect usages such as C(time) remain categorical by explicit user choice.
Environment
- CausalPy source: current main worktree
- Python: 3.14
- OS: macOS 26 (Darwin 25.5.0)
Proposed Solution
Add shared formula preprocessing/design-matrix support that recognizes bare datetime factors and encodes them as elapsed days from a persisted origin. Preserve the original datetime values where they are passed to datetime-aware transforms such as step() and ramp(), and do not change explicitly categorical expressions such as C(date).
Update the PiecewiseITS documentation to show the new supported behavior, add release notes for the intentional behavior change, and test continuous encoding, explicit C(date) encoding, and ITS out-of-sample prediction with monthly and irregular datetime series.
Additional Context
The documented arXiv example already avoids the problem by constructing numeric t and passing date only to ramp(). The requested behavior would make a direct date term safe and unsurprising while preserving an explicit categorical escape hatch.
Description
Patsy encodes a bare pandas datetime formula factor as categorical levels. CausalPy should instead interpret bare datetime predictors as continuous elapsed time, while retaining
C(date)as the explicit syntax for date fixed effects. This policy should apply consistently to every CausalPy experiment that accepts a Patsy formula, not only ITS.This is especially visible in PiecewiseITS, whose documentation currently shows
dateas a baseline term. The documented formula fits date dummies instead of a continuous baseline trend. In InterruptedTimeSeries, the same formula fails when predicting post-treatment observations because their datetime levels were not present in the pre-treatment design matrix.SyntheticControl and SyntheticDifferenceInDifferences are out of scope: they use
DatetimeIndexandtreatment_time, not Patsy formulas.Steps to Reproduce
datecolumn and an outcomey.PiecewiseITSwithformula="y ~ 1 + date + step(date, '2020-05-01') + ramp(date, '2020-05-01')"and inspectresult.labels.InterruptedTimeSerieson the same data indexed by date withformula="y ~ 1 + date"and a treatment date before the final observation.Expected Behavior
dateis represented by one continuous column, measured as elapsed days from a stable training-data origin.C(date)retains Patsy's normal categorical expansion for explicit date fixed effects.step(date, ...)andramp(date, ...)retain their existing datetime behavior.Actual Behavior
Patsy expands
dateinto one dummy column per timestamp. PiecewiseITS can therefore fit a near-saturated date-level model instead of a baseline time trend. InterruptedTimeSeries builds the pre-period categories first and then raisesPatsyErrorfor unseen post-period dates.The custom
step(date, ...)andramp(date, ...)transforms are not affected: they handle datetimes correctly, withrampexpressed in elapsed days.Scope
Apply the policy to all formula-based experiments: InterruptedTimeSeries, PiecewiseITS, DifferenceInDifferences, StaggeredDifferenceInDifferences, PanelRegression, PrePostNEGD, RegressionDiscontinuity, RegressionKink, InversePropensityWeighting, and InstrumentalVariable. Existing intentional fixed-effect usages such as
C(time)remain categorical by explicit user choice.Environment
Proposed Solution
Add shared formula preprocessing/design-matrix support that recognizes bare datetime factors and encodes them as elapsed days from a persisted origin. Preserve the original datetime values where they are passed to datetime-aware transforms such as
step()andramp(), and do not change explicitly categorical expressions such asC(date).Update the PiecewiseITS documentation to show the new supported behavior, add release notes for the intentional behavior change, and test continuous encoding, explicit
C(date)encoding, and ITS out-of-sample prediction with monthly and irregular datetime series.Additional Context
The documented arXiv example already avoids the problem by constructing numeric
tand passingdateonly toramp(). The requested behavior would make a directdateterm safe and unsurprising while preserving an explicit categorical escape hatch.