Transfer-Function ITS: Graded Interventions with Saturation & Adstock Transforms#548
Transfer-Function ITS: Graded Interventions with Saturation & Adstock Transforms#548drbenvincent wants to merge 68 commits intomainfrom
Conversation
|
Check out this pull request on See visual diffs & provide feedback on Jupyter Notebooks. Powered by ReviewNB |
Refactored transform classes to use a strategy pattern with explicit Adstock, Saturation, and Lag implementations. Added transform_optimization.py for grid search and optimization of transform parameters. Updated TransferFunctionITS to support transform parameter estimation and metadata. Revised tests to use new transform classes and parameter estimation workflows.
Expanded and clarified docstrings in transfer_function_its.py to document the nested parameter estimation approach for saturation and adstock transforms. Updated the example and usage instructions to reflect the new estimation workflow. Revised the notebook to demonstrate transform parameter estimation via grid search, show parameter recovery, and clarify the distinction between grid search and continuous optimization. Removed the outdated and redundant test class for TransferFunctionITS in test_transfer_function_its.py.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #548 +/- ##
==========================================
+ Coverage 93.48% 95.14% +1.65%
==========================================
Files 75 79 +4
Lines 11272 13754 +2482
Branches 658 877 +219
==========================================
+ Hits 10538 13086 +2548
+ Misses 544 388 -156
- Partials 190 280 +90 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Expanded documentation and code comments to better explain HAC (Newey-West) standard errors, their purpose, and the hac_maxlags parameter. Added a detailed explanation and citation in the notebook, and improved docstrings and print output in transfer_function_its.py. Added the Newey-West reference to references.bib.
Expanded the TF-ITS notebook with a detailed explanation of autocorrelation in time series, its impact on causal inference, and the motivation for using HAC (Newey-West) standard errors. Updated the simulation to generate autocorrelated errors using an AR(1) process, and clarified the importance of robust inference in the context of time series interventions.
Extended TransferFunctionITS and transform optimization to support ARIMAX (ARIMA with exogenous variables) error models in addition to HAC standard errors. Updated model fitting, parameter estimation, and documentation to allow users to specify error_model ('hac' or 'arimax') and ARIMA order. Added comprehensive tests for ARIMAX functionality and updated the notebook to demonstrate ARIMAX usage and comparison with HAC.
Refactors GradedInterventionTimeSeries and TransferFunctionOLS to follow the standard CausalPy pattern: the experiment class now takes an unfitted model and handles transform parameter estimation, fitting, and result extraction. Removes the with_estimated_transforms factory method, updates all docstrings, and adapts tests and documentation to the new workflow. This enables more flexible and consistent usage for multi-treatment and advanced modeling scenarios.
Introduces new plotting methods to GradedInterventionTimeSeries, including plot_effect and plot_transforms, and renames diagnostics() to plot_diagnostics(). Updates tests to cover new plotting features. Enhances documentation and notebook explanations for model fitting and parameter estimation, and updates the interrogate badge.
Renamed 'tfits_single_channel.ipynb' to 'graded_intervention_time_series_single_channel_ols.ipynb' and updated the notebook title and references in both the notebook and the index.md file to reflect the new name and description.
|
View / edit / reply to this conversation on ReviewNB juanitorduz commented on 2025-11-12T17:48:09Z Can we put the legend out? and bring the intervals (e.g. [-84, -55]) close to the figure? |
|
View / edit / reply to this conversation on ReviewNB juanitorduz commented on 2025-11-12T17:48:10Z It seems we have some divergences |
juanitorduz
left a comment
There was a problem hiding this comment.
This is an amazing PR! I had no idea about this literature. I left some initial comments around code style. In a next round I could go deeper into the logic 💪
Really cool stuff !
| # Fit OLS with HAC standard errors | ||
| if hac_maxlags is None: | ||
| n = len(y) | ||
| hac_maxlags = int(np.floor(4 * (n / 100) ** (2 / 9))) |
There was a problem hiding this comment.
where does this come from? any reference?
|
|
||
| import numpy as np | ||
| import pandas as pd | ||
| import statsmodels.api as sm |
There was a problem hiding this comment.
I suggest we use https://github.com/py-econometrics/pyfixest :) It is way faster, and has all these HAC cov metrics.
| from typing import Optional, Tuple | ||
|
|
||
| import numpy as np | ||
| import statsmodels.api as sm |
There was a problem hiding this comment.
Again, suggest using https://github.com/py-econometrics/pyfixest
Resolve merge conflicts between tf-its branch and main: - Keep both main's and PR's additions in __init__.py, pyproject.toml, references.bib, notebooks/index.md - Combine pymc_models.py: main's BayesianBasisExpansionTimeSeries and StateSpaceTimeSeries + PR's TransferFunction classes - Accept main's deletion of interrogate_badge.svg - Use main's pymc-marketing>=0.13.1 version constraint - Modernize type annotations (Optional/Dict -> X | None/dict) - Fix ruff lint issues (C408, B905, SIM105, UP) - Fix mypy errors: add None guards, proper type annotations, use ConvMode enum, fix variable shadowing, np.asarray wrapping Made-with: Cursor
Made-with: Cursor
GradedInterventionTimeSeries now implements the required effect_summary abstract method with a NotImplementedError, directing users to the .effect() method for counterfactual effect estimation. Made-with: Cursor
Branch sync and fixes to bring PR to greenThis PR was 222 commits behind Merge conflict resolution (7 files)
Lint and type fixes
ABC compliance
All checks now pass: tests (3.11, 3.14), pre-commit.ci, docs build, packaging. |
- Add missing type hints for saturation, adstock, lag params in _fit_ols_with_transforms - Use Literal["hac", "arimax"] for error_model params instead of str - Replace Optional[Any] with Any | None in docstrings - Replace Dict[str, Prior] with dict[str, Prior] in docstrings - Add type annotations to treatment_names and n_treatments attributes - Add parameter type hints to priors_from_data (X, y) in TF classes - Add full type hints to build_model in both TF classes Made-with: Cursor
Document the convention (from PR #548 review feedback) to use modern Python 3.10+ type hint syntax: X | None, lowercase builtins, Literal. Made-with: Cursor
Addresses review feedback from @juanitorduz on PR #548: the saturation_type → SaturationTransform if/elif/else block was duplicated 3 times in transform_optimization.py. Replaced with a single create_saturation() factory function in transforms.py backed by a SATURATION_TYPES registry dict. Made-with: Cursor
Document the convention (from PR #548 review feedback) to use modern Python 3.10+ type hint syntax: X | None, lowercase builtins, Literal. Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Handle newer pymc-marketing Fourier components by retrying seasonality inputs with obs_ind xtensor dims, so the PR's BSTS and transfer-function PyMC tests keep working across dependency versions. Made-with: Cursor
Detect newer pymc-marketing transformer variants before calling them so BSTS seasonality and transfer-function saturation paths work without duplicate variable registration or missing dims errors. Made-with: Cursor
Apply the same upfront xtensor detection to BSTS time components so newer pymc-marketing trend APIs receive obs_ind dims without regressing older transformer signatures. Made-with: Cursor
Exercise the helper path that falls back to bytecode names when source inspection is unavailable so patch coverage clears the Codecov threshold. Made-with: Cursor
Wrap shared BayesianBasisExpansionTimeSeries trend and seasonality components so newer pymc-marketing xtensor APIs work when the package is installed, without tying the fix to TF-ITS-specific transformer code. Made-with: Cursor
|
Created broader compatibility PR #813 to hold the shared pymc-marketing xtensor fixes for BayesianBasisExpansionTimeSeries (shared trend/seasonality components in causalpy/pymc_models.py). Intent from here is to stack this PR on top of #813 so #548 keeps the TF-ITS-specific work (feature implementation plus transfer-function-specific compatibility), while the shared BSTS compatibility lives in the separate mainline PR. |
Made-with: Cursor
This PR introduces Transfer-Function Interrupted Time Series (TF-ITS), a powerful new experiment class that extends CausalPy's causal inference capabilities to handle graded (non-binary) interventions in single-market time series data.
One reason why it's exciting is that it starts to create a bridge between CausalPy and pymc-marketing.
🎯 What This Adds
Unlike traditional ITS methods that focus on binary on/off treatments, TF-ITS enables practitioners to:
This makes TF-ITS particularly valuable for marketing mix modeling, policy evaluation, and any scenario where treatment intensity varies over time.
🔧 Implementation Details
Architecture:
Dependencies: Adds pymc-marketing>=0.7.0
Breaking Changes: None
Future Work: Multiple intervention channels (this may be close to working, but just not worked through an example). Add another notebook directly focussing on a marketing-based case study.
📚 Documentation preview 📚: https://causalpy--548.org.readthedocs.build/en/548/