formative-ds 2.2.3__tar.gz → 2.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {formative_ds-2.2.3 → formative_ds-2.2.5}/PKG-INFO +2 -2
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/_base.py +25 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/iv.py +2 -9
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/matching.py +3 -20
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/ols.py +3 -21
- {formative_ds-2.2.3 → formative_ds-2.2.5}/pyproject.toml +3 -3
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/causal/benchmarks.rst.txt +22 -5
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/causal/decisions.rst.txt +69 -24
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/causal/introduction.rst.txt +10 -5
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/documentation_options.js +1 -1
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/causal/benchmarks.html +25 -8
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/causal/dag.html +6 -6
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/causal/decisions.html +74 -29
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/causal/estimands.html +3 -3
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/causal/estimators.html +3 -3
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/causal/introduction.html +16 -12
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/causal/motivation.html +7 -7
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/causal/refutations.html +3 -3
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/causal/wizard.html +3 -3
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/game/decision_rules.html +3 -3
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/game/introduction.html +3 -3
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/game/single_player.html +3 -3
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/genindex.html +3 -3
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/index.html +5 -5
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/search.html +3 -3
- formative_ds-2.2.5/site/2.2/searchindex.js +1 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/uv.lock +1 -1
- formative_ds-2.2.3/site/2.2/searchindex.js +0 -1
- {formative_ds-2.2.3 → formative_ds-2.2.5}/.gitattributes +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/.github/workflows/ci.yml +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/.github/workflows/publish.yml +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/.gitignore +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/CLAUDE.md +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/LICENSE +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/Makefile +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/README.md +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/TODO.md +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/_static/version_switcher.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/_static/version_switcher.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/causal/benchmarks.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/causal/dag.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/causal/decisions.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/causal/estimands.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/causal/estimators.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/causal/introduction.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/causal/motivation.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/causal/refutations.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/causal/wizard.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/conf.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/design/policy_learning.md +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/game/decision_rules.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/game/introduction.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/game/single_player.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/docs/index.rst +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/decision/01_decision_basic.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/did/01_did_basic.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/iv/01_iv_basic.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/iv/02_iv_vs_ols_bias.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/iv/03_iv_with_observed_confounder.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/iv/04_iv_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/matching/01_basic_matching.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/matching/02_matching_vs_naive.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/matching/03_matching_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/ols/01_ols_basic.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/ols/02_ols_unobserved_confounder.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/ols/03_ols_multiple_confounders.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/rct/01_rct_basic.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/rdd/01_rdd_basic.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/examples/rdd/02_rdd_positive_running_var.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/__init__.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/__init__.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/_assumptions.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/_exceptions.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/_explain.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/dag.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/decision.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/__init__.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/_cate.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/did.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/policy.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/rct.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/estimators/rdd.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/__init__.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/_check.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/cate.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/did.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/iv.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/matching.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/ols.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/policy.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/rct.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/causal/refutations/rdd.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/game/__init__.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/game/expected_value.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/game/hurwicz.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/game/laplace.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/game/maximax.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/game/maximin.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/formative/game/minimax.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/notebooks/card_krueger_benchmark.ipynb +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/notebooks/lalonde_benchmark.ipynb +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/scripts/snapshot_docs.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/causal/benchmarks.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/causal/dag.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/causal/decisions.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/causal/estimands.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/causal/estimators.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/causal/introduction.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/causal/motivation.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/causal/refutations.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/causal/wizard.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/game/decision_rules.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/game/introduction.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/game/single_player.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_sources/index.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/_sphinx_javascript_frameworks_compat.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/base-stemmer.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/basic.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/check-solid.svg +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/clipboard.min.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/copy-button.svg +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/copybutton.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/copybutton.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/copybutton_funcs.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/badge_only.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/Roboto-Slab-Bold.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/Roboto-Slab-Bold.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/Roboto-Slab-Regular.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/Roboto-Slab-Regular.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/fontawesome-webfont.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/fontawesome-webfont.svg +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/fontawesome-webfont.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/fontawesome-webfont.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/fontawesome-webfont.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/lato-bold-italic.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/lato-bold-italic.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/lato-bold.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/lato-bold.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/lato-normal-italic.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/lato-normal-italic.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/lato-normal.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/fonts/lato-normal.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/css/theme.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/doctools.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/documentation_options.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/english-stemmer.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/file.png +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-bold.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-bold.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-bold.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-bold.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-bolditalic.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-bolditalic.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-bolditalic.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-bolditalic.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-italic.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-italic.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-italic.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-italic.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-regular.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-regular.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-regular.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/Lato/lato-regular.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/jquery.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/js/badge_only.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/js/theme.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/js/versions.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/language_data.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/minus.png +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/plus.png +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/pygments.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/searchtools.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/sphinx_highlight.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/version_switcher.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/_static/version_switcher.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/causal/benchmarks.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/causal/dag.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/causal/decisions.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/causal/estimands.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/causal/estimators.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/causal/introduction.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/causal/motivation.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/causal/refutations.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/causal/wizard.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/game/decision_rules.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/game/introduction.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/game/single_player.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/genindex.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/index.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/objects.inv +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/search.html +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.1/searchindex.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/causal/dag.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/causal/estimands.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/causal/estimators.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/causal/motivation.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/causal/refutations.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/causal/wizard.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/game/decision_rules.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/game/introduction.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/game/single_player.rst.txt +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_sources/index.rst.txt +1 -1
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/_sphinx_javascript_frameworks_compat.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/base-stemmer.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/basic.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/check-solid.svg +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/clipboard.min.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/copy-button.svg +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/copybutton.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/copybutton.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/copybutton_funcs.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/badge_only.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/Roboto-Slab-Bold.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/Roboto-Slab-Bold.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/Roboto-Slab-Regular.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/Roboto-Slab-Regular.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/fontawesome-webfont.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/fontawesome-webfont.svg +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/fontawesome-webfont.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/fontawesome-webfont.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/fontawesome-webfont.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/lato-bold-italic.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/lato-bold-italic.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/lato-bold.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/lato-bold.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/lato-normal-italic.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/lato-normal-italic.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/lato-normal.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/fonts/lato-normal.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/css/theme.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/doctools.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/english-stemmer.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/file.png +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-bold.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-bold.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-bold.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-bold.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-bolditalic.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-bolditalic.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-bolditalic.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-bolditalic.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-italic.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-italic.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-italic.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-italic.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-regular.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-regular.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-regular.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/Lato/lato-regular.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/jquery.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/js/badge_only.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/js/theme.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/js/versions.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/language_data.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/minus.png +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/plus.png +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/pygments.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/searchtools.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/sphinx_highlight.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/version_switcher.css +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/_static/version_switcher.js +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/2.2/objects.inv +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/site/versions.json +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_cate.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_cate_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_dag.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_decision.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_did.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_did_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_game.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_iv.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_iv_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_matching.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_matching_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_ols.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_ols_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_policy.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_policy_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_rct.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_rct_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_rdd.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/tests/test_rdd_refutation.py +0 -0
- {formative_ds-2.2.3 → formative_ds-2.2.5}/vercel.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: formative-ds
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.5
|
|
4
4
|
Summary: A Python library for quantitative reasoning.
|
|
5
5
|
Project-URL: Homepage, https://getformative.dev
|
|
6
6
|
Project-URL: Documentation, https://docs.getformative.dev
|
|
@@ -9,7 +9,7 @@ Author: Max Pagels
|
|
|
9
9
|
License: MIT
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Keywords: DAG,causal estimation,causal inference,econometrics,statistics
|
|
12
|
-
Classifier: Development Status ::
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Science/Research
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -1,6 +1,31 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from .._assumptions import Assumption
|
|
4
|
+
from .._exceptions import IdentificationError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _backdoor_confounders(dag, treatment: str, outcome: str) -> set[str]:
|
|
8
|
+
"""
|
|
9
|
+
Confounders under the backdoor criterion: common ancestors of treatment and
|
|
10
|
+
outcome that are not descendants of treatment (mediators or post-treatment
|
|
11
|
+
variables).
|
|
12
|
+
"""
|
|
13
|
+
return (dag.ancestors(treatment) & dag.ancestors(outcome)) - dag.descendants(treatment)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _missing_confounders_error(missing: set[str], treatment: str, outcome: str) -> IdentificationError:
|
|
17
|
+
"""``IdentificationError`` for DAG-declared confounders absent from the dataframe."""
|
|
18
|
+
return IdentificationError(
|
|
19
|
+
f"\nDAG confounders not found in dataframe: {sorted(missing)}\n\n"
|
|
20
|
+
f"Your DAG declares these variables as confounders of '{treatment}' and\n"
|
|
21
|
+
f"'{outcome}', but they are absent from the dataframe and cannot be\n"
|
|
22
|
+
f"controlled for. Note: there may also be confounders not modelled in your\n"
|
|
23
|
+
f"DAG at all — formative cannot detect those.\n\n"
|
|
24
|
+
f"Consider:\n"
|
|
25
|
+
f" - Collecting data on {sorted(missing)} and adding it to the dataframe\n"
|
|
26
|
+
f" - IV estimation if you have a valid instrument for '{treatment}'\n"
|
|
27
|
+
f" - DiD or RD if a natural experiment is available"
|
|
28
|
+
)
|
|
4
29
|
|
|
5
30
|
|
|
6
31
|
class _BaseResult:
|
|
@@ -7,7 +7,7 @@ from statsmodels.sandbox.regression.gmm import IV2SLS as _IV2SLS
|
|
|
7
7
|
|
|
8
8
|
from .._assumptions import Assumption
|
|
9
9
|
from ..dag import DAG
|
|
10
|
-
from ._base import _StatsmodelsResult
|
|
10
|
+
from ._base import _backdoor_confounders, _StatsmodelsResult
|
|
11
11
|
|
|
12
12
|
IV_ASSUMPTIONS: list[Assumption] = [
|
|
13
13
|
Assumption("Relevance: the instrument strongly affects treatment", testable=True),
|
|
@@ -228,14 +228,7 @@ class IV2SLS:
|
|
|
228
228
|
Unobserved confounders are handled by the instrument and are not flagged
|
|
229
229
|
as errors — this is the primary use case for IV estimation.
|
|
230
230
|
"""
|
|
231
|
-
|
|
232
|
-
T, Y = self._treatment, self._outcome
|
|
233
|
-
|
|
234
|
-
treatment_ancestors = dag.ancestors(T)
|
|
235
|
-
outcome_ancestors = dag.ancestors(Y)
|
|
236
|
-
treatment_descendants = dag.descendants(T)
|
|
237
|
-
|
|
238
|
-
confounders = (treatment_ancestors & outcome_ancestors) - treatment_descendants
|
|
231
|
+
confounders = _backdoor_confounders(self._dag, self._treatment, self._outcome)
|
|
239
232
|
confounders.discard(self._instrument)
|
|
240
233
|
|
|
241
234
|
return {c for c in confounders if c in data_columns}
|
|
@@ -6,9 +6,8 @@ import statsmodels.api as sm
|
|
|
6
6
|
import statsmodels.formula.api as smf
|
|
7
7
|
|
|
8
8
|
from .._assumptions import Assumption
|
|
9
|
-
from .._exceptions import IdentificationError
|
|
10
9
|
from ..dag import DAG
|
|
11
|
-
from ._base import _BaseResult
|
|
10
|
+
from ._base import _backdoor_confounders, _BaseResult, _missing_confounders_error
|
|
12
11
|
|
|
13
12
|
MATCHING_ASSUMPTIONS: list[Assumption] = [
|
|
14
13
|
Assumption("Conditional independence: no unobserved confounders given matched variables", testable=False),
|
|
@@ -283,14 +282,7 @@ class PropensityScoreMatching:
|
|
|
283
282
|
raise ValueError("Treatment and outcome must be different variables.")
|
|
284
283
|
|
|
285
284
|
def _identify(self, data_columns: set[str]) -> tuple[set[str], set[str]]:
|
|
286
|
-
|
|
287
|
-
T, Y = self._treatment, self._outcome
|
|
288
|
-
|
|
289
|
-
treatment_ancestors = dag.ancestors(T)
|
|
290
|
-
outcome_ancestors = dag.ancestors(Y)
|
|
291
|
-
treatment_descendants = dag.descendants(T)
|
|
292
|
-
|
|
293
|
-
confounders = (treatment_ancestors & outcome_ancestors) - treatment_descendants
|
|
285
|
+
confounders = _backdoor_confounders(self._dag, self._treatment, self._outcome)
|
|
294
286
|
observed = {c for c in confounders if c in data_columns}
|
|
295
287
|
missing = {c for c in confounders if c not in data_columns}
|
|
296
288
|
return observed, missing
|
|
@@ -328,16 +320,7 @@ class PropensityScoreMatching:
|
|
|
328
320
|
adjustment_set, missing = self._identify(data_columns)
|
|
329
321
|
|
|
330
322
|
if missing:
|
|
331
|
-
raise
|
|
332
|
-
f"\nDAG confounders not found in dataframe: {sorted(missing)}\n\n"
|
|
333
|
-
f"Your DAG declares these variables as confounders of "
|
|
334
|
-
f"'{self._treatment}' and '{self._outcome}', but they are "
|
|
335
|
-
f"absent from the dataframe and cannot be controlled for.\n\n"
|
|
336
|
-
f"Consider:\n"
|
|
337
|
-
f" - Collecting data on {sorted(missing)} and adding it to the dataframe\n"
|
|
338
|
-
f" - IV estimation if you have a valid instrument for '{self._treatment}'\n"
|
|
339
|
-
f" - DiD or RD if a natural experiment is available"
|
|
340
|
-
)
|
|
323
|
+
raise _missing_confounders_error(missing, self._treatment, self._outcome)
|
|
341
324
|
|
|
342
325
|
T, Y = self._treatment, self._outcome
|
|
343
326
|
|
|
@@ -4,9 +4,8 @@ import pandas as pd
|
|
|
4
4
|
import statsmodels.formula.api as smf
|
|
5
5
|
|
|
6
6
|
from .._assumptions import Assumption
|
|
7
|
-
from .._exceptions import IdentificationError
|
|
8
7
|
from ..dag import DAG
|
|
9
|
-
from ._base import _StatsmodelsResult
|
|
8
|
+
from ._base import _backdoor_confounders, _missing_confounders_error, _StatsmodelsResult
|
|
10
9
|
from ._cate import (
|
|
11
10
|
CATE_ASSUMPTIONS,
|
|
12
11
|
_CATEResultMixin,
|
|
@@ -282,14 +281,7 @@ class OLSObservational:
|
|
|
282
281
|
absent from the data. There may be additional unobserved confounders
|
|
283
282
|
not represented in the DAG at all.
|
|
284
283
|
"""
|
|
285
|
-
|
|
286
|
-
T, Y = self._treatment, self._outcome
|
|
287
|
-
|
|
288
|
-
treatment_ancestors = dag.ancestors(T)
|
|
289
|
-
outcome_ancestors = dag.ancestors(Y)
|
|
290
|
-
treatment_descendants = dag.descendants(T)
|
|
291
|
-
|
|
292
|
-
confounders = (treatment_ancestors & outcome_ancestors) - treatment_descendants
|
|
284
|
+
confounders = _backdoor_confounders(self._dag, self._treatment, self._outcome)
|
|
293
285
|
|
|
294
286
|
observed_confounders = {c for c in confounders if c in data_columns}
|
|
295
287
|
dag_confounders_not_in_data = {c for c in confounders if c not in data_columns}
|
|
@@ -335,17 +327,7 @@ class OLSObservational:
|
|
|
335
327
|
adjustment_set, dag_confounders_not_in_data = self._identify(data_columns)
|
|
336
328
|
|
|
337
329
|
if dag_confounders_not_in_data:
|
|
338
|
-
raise
|
|
339
|
-
f"\nDAG confounders not found in dataframe: {sorted(dag_confounders_not_in_data)}\n\n"
|
|
340
|
-
f"Your DAG declares these variables as confounders of '{self._treatment}' and\n"
|
|
341
|
-
f"'{self._outcome}', but they are absent from the dataframe and cannot be\n"
|
|
342
|
-
f"controlled for. Note: there may also be confounders not modelled in your\n"
|
|
343
|
-
f"DAG at all — formative cannot detect those.\n\n"
|
|
344
|
-
f"Consider:\n"
|
|
345
|
-
f" - Collecting data on {sorted(dag_confounders_not_in_data)} and adding it to the dataframe\n"
|
|
346
|
-
f" - IV estimation if you have a valid instrument for '{self._treatment}'\n"
|
|
347
|
-
f" - DiD or RD if a natural experiment is available"
|
|
348
|
-
)
|
|
330
|
+
raise _missing_confounders_error(dag_confounders_not_in_data, self._treatment, self._outcome)
|
|
349
331
|
|
|
350
332
|
unadjusted_result = smf.ols(f"{self._outcome} ~ {self._treatment}", data=data).fit()
|
|
351
333
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "formative-ds"
|
|
7
|
-
version = "2.2.
|
|
7
|
+
version = "2.2.5"
|
|
8
8
|
description = "A Python library for quantitative reasoning."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -12,7 +12,7 @@ license = {text = "MIT"}
|
|
|
12
12
|
authors = [{name = "Max Pagels"}]
|
|
13
13
|
keywords = ["causal inference", "causal estimation", "econometrics", "statistics", "DAG"]
|
|
14
14
|
classifiers = [
|
|
15
|
-
"Development Status ::
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
16
|
"Intended Audience :: Science/Research",
|
|
17
17
|
"Topic :: Scientific/Engineering :: Mathematics",
|
|
18
18
|
"License :: OSI Approved :: MIT License",
|
|
@@ -48,7 +48,7 @@ docs = ["sphinx>=7,<10", "sphinx-rtd-theme>=2,<4", "sphinx-copybutton>=0.5,<0.6"
|
|
|
48
48
|
packages = ["formative"]
|
|
49
49
|
|
|
50
50
|
[tool.bumpversion]
|
|
51
|
-
current_version = "2.2.
|
|
51
|
+
current_version = "2.2.5"
|
|
52
52
|
commit = true
|
|
53
53
|
tag = true
|
|
54
54
|
tag_name = "v{new_version}"
|
|
@@ -118,7 +118,9 @@ treatment/outcome relationship. The observational DAG declares this:
|
|
|
118
118
|
obs_dag.assume(c).causes("treat", "re78")
|
|
119
119
|
obs_dag.assume("treat").causes("re78")
|
|
120
120
|
|
|
121
|
-
result = OLSObservational(
|
|
121
|
+
result = OLSObservational(
|
|
122
|
+
obs_dag, treatment="treat", outcome="re78"
|
|
123
|
+
).fit(observational)
|
|
122
124
|
print(result.summary())
|
|
123
125
|
|
|
124
126
|
.. code-block:: text
|
|
@@ -156,7 +158,9 @@ each treated person only with the most similar controls:
|
|
|
156
158
|
|
|
157
159
|
from formative.causal import PropensityScoreMatching
|
|
158
160
|
|
|
159
|
-
result = PropensityScoreMatching(
|
|
161
|
+
result = PropensityScoreMatching(
|
|
162
|
+
obs_dag, treatment="treat", outcome="re78"
|
|
163
|
+
).fit(observational)
|
|
160
164
|
print(result.summary())
|
|
161
165
|
|
|
162
166
|
.. code-block:: text
|
|
@@ -293,7 +297,10 @@ Table 3 reports four cell means; formative's DiD will be built from exactly thes
|
|
|
293
297
|
|
|
294
298
|
for state, name in [(1, "NJ"), (0, "PA")]:
|
|
295
299
|
sub = raw[raw.STATE == state]
|
|
296
|
-
print(
|
|
300
|
+
print(
|
|
301
|
+
f"{name}: before {sub.fte_before.mean():.2f}, "
|
|
302
|
+
f"after {sub.fte_after.mean():.2f}"
|
|
303
|
+
)
|
|
297
304
|
|
|
298
305
|
.. code-block:: text
|
|
299
306
|
|
|
@@ -317,8 +324,18 @@ binary group and time indicators:
|
|
|
317
324
|
|
|
318
325
|
long = pd.concat(
|
|
319
326
|
[
|
|
320
|
-
pd.DataFrame({
|
|
321
|
-
|
|
327
|
+
pd.DataFrame({
|
|
328
|
+
"store": raw.SHEET,
|
|
329
|
+
"nj": raw.STATE,
|
|
330
|
+
"after": 0,
|
|
331
|
+
"fte": raw.fte_before,
|
|
332
|
+
}),
|
|
333
|
+
pd.DataFrame({
|
|
334
|
+
"store": raw.SHEET,
|
|
335
|
+
"nj": raw.STATE,
|
|
336
|
+
"after": 1,
|
|
337
|
+
"fte": raw.fte_after,
|
|
338
|
+
}),
|
|
322
339
|
],
|
|
323
340
|
ignore_index=True,
|
|
324
341
|
).dropna(subset=["fte"])
|
|
@@ -52,7 +52,11 @@ earnings using OLS with a DAG that encodes family background as a confounder:
|
|
|
52
52
|
training = 0.6 * background + rng.normal(size=N)
|
|
53
53
|
earnings = 3.0 * training + 1.2 * background + rng.normal(size=N)
|
|
54
54
|
|
|
55
|
-
df
|
|
55
|
+
df = pd.DataFrame({
|
|
56
|
+
"background": background,
|
|
57
|
+
"training": training,
|
|
58
|
+
"earnings": earnings,
|
|
59
|
+
})
|
|
56
60
|
dag = DAG()
|
|
57
61
|
dag.assume("background").causes("training", "earnings")
|
|
58
62
|
dag.assume("training").causes("earnings")
|
|
@@ -85,7 +89,7 @@ But how confident are we in that estimate? And is it robust to estimation error?
|
|
|
85
89
|
Decision confidence : 100.0%
|
|
86
90
|
Robust to estimation error : Yes — decision is stable across 95% CI
|
|
87
91
|
|
|
88
|
-
|
|
92
|
+
Robustness
|
|
89
93
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
90
94
|
|
|
91
95
|
``decide()`` uses expected value maximisation: it picks "treat" when
|
|
@@ -96,7 +100,7 @@ treats a certain $37 gain and a 50/50 gamble between $0 and $74 identically.
|
|
|
96
100
|
The ``robust`` flag is a first step toward robustness: it checks whether
|
|
97
101
|
the decision flips anywhere inside the 95% confidence interval. But it
|
|
98
102
|
only returns ``True`` or ``False``, and it is implicitly using the most
|
|
99
|
-
conservative possible standard (the
|
|
103
|
+
conservative possible standard (the confidence interval bounds).
|
|
100
104
|
|
|
101
105
|
For finer control, call ``to_outcomes()`` on the report and pass the result
|
|
102
106
|
to any rule in ``formative.game``:
|
|
@@ -118,8 +122,8 @@ to any rule in ``formative.game``:
|
|
|
118
122
|
|
|
119
123
|
By default, the three scenarios correspond to the 10th, 50th, and 90th
|
|
120
124
|
percentiles of the net-benefit sampling distribution (assumed normal with
|
|
121
|
-
the
|
|
122
|
-
every scenario
|
|
125
|
+
the standard error derived from the 95% confidence interval). The ``"don't treat"`` payoff is 0 in
|
|
126
|
+
every scenario.
|
|
123
127
|
|
|
124
128
|
You can supply your own scenario names and quantiles:
|
|
125
129
|
|
|
@@ -129,17 +133,19 @@ You can supply your own scenario names and quantiles:
|
|
|
129
133
|
scenarios={"bear": 0.05, "base": 0.50, "bull": 0.95}
|
|
130
134
|
)
|
|
131
135
|
|
|
132
|
-
|
|
133
|
-
``maximin
|
|
134
|
-
set to the
|
|
136
|
+
How does this differ from using ``robust``? ``robust=True`` is equivalent to
|
|
137
|
+
``maximin``, returning the same choice when the scenarios are
|
|
138
|
+
set to the confidence interval bounds (quantiles 0.025 and 0.975). ``to_outcomes()``
|
|
135
139
|
generalises that check: different rules express different risk attitudes, and
|
|
136
140
|
you can dial in your own pessimism level via ``hurwicz(alpha=...)``.
|
|
137
141
|
|
|
138
142
|
Should we treat each segment?
|
|
139
143
|
-----------------------------
|
|
140
144
|
|
|
141
|
-
An average effect can hide groups where the treatment is worthless
|
|
142
|
-
it pays for itself several times over.
|
|
145
|
+
An average effect can hide groups where the treatment is worthless, or where
|
|
146
|
+
it pays for itself several times over. It will also cause you to scale up a treatment
|
|
147
|
+
that is not worth it for everyone.
|
|
148
|
+
When an estimator is fitted with an
|
|
143
149
|
``effect_modifier`` (see :doc:`estimands`), ``decide_by_group(cost, benefit)``
|
|
144
150
|
runs the same cost-benefit analysis within each level of the modifier and
|
|
145
151
|
returns a mapping of level → :class:`~formative.DecisionReport`, so different
|
|
@@ -161,19 +167,33 @@ and everyone else not at all:
|
|
|
161
167
|
tenure = rng.choice(["<2y", "2-5y", ">5y"], size=N)
|
|
162
168
|
region = rng.choice(["north", "south"], size=N)
|
|
163
169
|
coaching = rng.integers(0, 2, size=N)
|
|
164
|
-
retention =
|
|
170
|
+
retention = (
|
|
171
|
+
6.0 * coaching * (tenure == "<2y")
|
|
172
|
+
+ 2.0 * (tenure == ">5y")
|
|
173
|
+
+ rng.normal(size=N)
|
|
174
|
+
)
|
|
165
175
|
|
|
166
|
-
df = pd.DataFrame({
|
|
176
|
+
df = pd.DataFrame({
|
|
177
|
+
"tenure": tenure,
|
|
178
|
+
"region": region,
|
|
179
|
+
"coaching": coaching,
|
|
180
|
+
"retention": retention,
|
|
181
|
+
})
|
|
167
182
|
|
|
168
183
|
dag = DAG()
|
|
169
184
|
dag.assume("coaching").causes("retention")
|
|
170
185
|
dag.assume("tenure").causes("retention")
|
|
171
186
|
dag.assume("region").causes("retention")
|
|
172
187
|
|
|
173
|
-
result = RCT(
|
|
188
|
+
result = RCT(
|
|
189
|
+
dag, treatment="coaching", outcome="retention", effect_modifier="tenure"
|
|
190
|
+
).fit(df)
|
|
174
191
|
for level, decision in result.decide_by_group(cost=2.5, benefit=1.0).items():
|
|
175
192
|
lo, hi = decision.net_benefit_ci
|
|
176
|
-
print(
|
|
193
|
+
print(
|
|
194
|
+
f"{level:>5}: {decision.optimal:<12} "
|
|
195
|
+
f"net benefit {decision.net_benefit:+.2f} [{lo:+.2f}, {hi:+.2f}]"
|
|
196
|
+
)
|
|
177
197
|
|
|
178
198
|
.. code-block:: text
|
|
179
199
|
|
|
@@ -203,10 +223,9 @@ one less thing to get wrong.
|
|
|
203
223
|
How it works
|
|
204
224
|
~~~~~~~~~~~~
|
|
205
225
|
|
|
206
|
-
``learn_policy()`` implements doubly robust policy learning
|
|
207
|
-
Athey & Wager (2021):
|
|
226
|
+
``learn_policy()`` implements doubly robust policy learning:
|
|
208
227
|
|
|
209
|
-
1. **Score every unit.** Each unit gets a cross-fitted
|
|
228
|
+
1. **Score every unit.** Each unit gets a cross-fitted score — an
|
|
210
229
|
unbiased estimate of its individual treatment effect. Outcome models are
|
|
211
230
|
fit by OLS on the candidate features over four folds and evaluated on the
|
|
212
231
|
fifth, so no unit is scored by a model that saw it.
|
|
@@ -225,7 +244,7 @@ Athey & Wager (2021):
|
|
|
225
244
|
|
|
226
245
|
Because the tree may split on any level of any candidate feature, features
|
|
227
246
|
must be discrete: bin continuous columns before passing them in. Candidate
|
|
228
|
-
features face the same DAG validation as effect modifiers
|
|
247
|
+
features face the same DAG validation as effect modifiers; each must cause
|
|
229
248
|
the outcome and must not be a descendant of the treatment (targeting on a
|
|
230
249
|
mediator is not actionable at assignment time).
|
|
231
250
|
|
|
@@ -238,7 +257,13 @@ features instead of choosing a segmentation ourselves:
|
|
|
238
257
|
.. code-block:: python
|
|
239
258
|
|
|
240
259
|
result = RCT(dag, treatment="coaching", outcome="retention").fit(df)
|
|
241
|
-
policy = result.learn_policy(
|
|
260
|
+
policy = result.learn_policy(
|
|
261
|
+
df,
|
|
262
|
+
modifiers=["tenure", "region"],
|
|
263
|
+
cost=2.5,
|
|
264
|
+
benefit=1.0,
|
|
265
|
+
max_depth=2
|
|
266
|
+
)
|
|
242
267
|
print(policy.summary())
|
|
243
268
|
|
|
244
269
|
.. code-block:: text
|
|
@@ -286,10 +311,17 @@ it only helps recent hires in the north:
|
|
|
286
311
|
coaching = rng.integers(0, 2, size=N)
|
|
287
312
|
helped = (tenure == "<2y") & (region == "north")
|
|
288
313
|
retention = 7.0 * coaching * helped + 2.0 * (tenure == ">5y") + rng.normal(size=N)
|
|
289
|
-
df = pd.DataFrame({
|
|
314
|
+
df = pd.DataFrame({
|
|
315
|
+
"tenure": tenure,
|
|
316
|
+
"region": region,
|
|
317
|
+
"coaching": coaching,
|
|
318
|
+
"retention": retention,
|
|
319
|
+
})
|
|
290
320
|
|
|
291
321
|
result = RCT(dag, treatment="coaching", outcome="retention").fit(df)
|
|
292
|
-
policy = result.learn_policy(
|
|
322
|
+
policy = result.learn_policy(
|
|
323
|
+
df, modifiers=["tenure", "region"], cost=2.5, benefit=1.0, max_depth=2
|
|
324
|
+
)
|
|
293
325
|
print(policy.summary())
|
|
294
326
|
|
|
295
327
|
.. code-block:: text
|
|
@@ -316,7 +348,9 @@ back to treating all recent hires and burns money on the southern half:
|
|
|
316
348
|
|
|
317
349
|
.. code-block:: python
|
|
318
350
|
|
|
319
|
-
result.learn_policy(
|
|
351
|
+
result.learn_policy(
|
|
352
|
+
df, modifiers=["tenure", "region"], cost=2.5, benefit=1.0, max_depth=1
|
|
353
|
+
).value
|
|
320
354
|
# +0.34 per unit — versus +0.75 at depth 2
|
|
321
355
|
|
|
322
356
|
When the deeper policy's value is clearly higher (as here), ship it; when the
|
|
@@ -339,7 +373,12 @@ segment-2 employees in the north:
|
|
|
339
373
|
training = rng.integers(0, 2, size=N)
|
|
340
374
|
effect = 4.0 * (segment == 0) + 5.0 * ((segment == 2) & (region == "north"))
|
|
341
375
|
earnings = effect * training + 0.5 * segment + rng.normal(size=N)
|
|
342
|
-
df = pd.DataFrame({
|
|
376
|
+
df = pd.DataFrame({
|
|
377
|
+
"segment": segment,
|
|
378
|
+
"region": region,
|
|
379
|
+
"training": training,
|
|
380
|
+
"earnings": earnings,
|
|
381
|
+
})
|
|
343
382
|
|
|
344
383
|
dag = DAG()
|
|
345
384
|
dag.assume("training").causes("earnings")
|
|
@@ -347,7 +386,13 @@ segment-2 employees in the north:
|
|
|
347
386
|
dag.assume("region").causes("earnings")
|
|
348
387
|
|
|
349
388
|
result = RCT(dag, treatment="training", outcome="earnings").fit(df)
|
|
350
|
-
policy = result.learn_policy(
|
|
389
|
+
policy = result.learn_policy(
|
|
390
|
+
df,
|
|
391
|
+
modifiers=["segment", "region"],
|
|
392
|
+
cost=1.0,
|
|
393
|
+
benefit=1.0,
|
|
394
|
+
max_depth=2
|
|
395
|
+
)
|
|
351
396
|
print(policy.summary())
|
|
352
397
|
|
|
353
398
|
.. code-block:: text
|
|
@@ -49,7 +49,7 @@ you choose.
|
|
|
49
49
|
|
|
50
50
|
After you have obtained the result object, you can print a summary of the estimate and
|
|
51
51
|
its assumptions. The assumptions are marked as testable or untestable depending on whether
|
|
52
|
-
formative can check them. Most assumptions in causal inference are untestable by nature
|
|
52
|
+
formative can check them. Most assumptions in causal inference are untestable by nature, i.e.
|
|
53
53
|
things you must argue for based on domain knowledge and theory.
|
|
54
54
|
|
|
55
55
|
.. code-block:: python
|
|
@@ -83,7 +83,7 @@ communicating with non-technical audiences.
|
|
|
83
83
|
**3. Refute**
|
|
84
84
|
|
|
85
85
|
Once you have a result, run statistical checks that probe whether its assumptions hold
|
|
86
|
-
in the data. Each check returns a clear pass or fail
|
|
86
|
+
in the data. Each check returns a clear pass or fail, but no set of tests can guarantee
|
|
87
87
|
validity. Use them as diagnostics, not proof. Causal inference is a judgment call, not a
|
|
88
88
|
mathematical certainty.
|
|
89
89
|
|
|
@@ -104,10 +104,10 @@ mathematical certainty.
|
|
|
104
104
|
|
|
105
105
|
**4. Decide**
|
|
106
106
|
|
|
107
|
-
An estimate is rarely the end goal
|
|
107
|
+
An estimate is rarely the end goal; the question is whether to act on it. Pass the
|
|
108
108
|
cost per unit of treatment applied and the value of one unit of improvement in the
|
|
109
109
|
outcome, and formative turns the estimate and its uncertainty into a treat/don't-treat
|
|
110
|
-
|
|
110
|
+
decision. Suppose a unit of education costs $8 and a unit of income is worth $15:
|
|
111
111
|
|
|
112
112
|
.. code-block:: python
|
|
113
113
|
|
|
@@ -128,7 +128,12 @@ call. Suppose a unit of education costs $8 and a unit of income is worth $15:
|
|
|
128
128
|
Decision confidence : 100.0%
|
|
129
129
|
Robust to estimation error : Yes — decision is stable across 95% CI
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
You may ask why a cost must be passed; is it not enough if the estimate is positive?
|
|
132
|
+
The answer is that a positive estimate does not guarantee that the treatment is worth it.
|
|
133
|
+
If the estimate gives an effect of +1.0, but the treatment costs $10 and the outcome is worth $1,
|
|
134
|
+
then the treatment is not worth it. The decision layer makes this explicit.
|
|
135
|
+
|
|
136
|
+
Depending on the estimator you choose, decisions can go beyond a single call for everyone:
|
|
132
137
|
per-group decisions via an effect modifier, or a learned treatment rule that
|
|
133
138
|
decides *whom* to treat. See :doc:`decisions` for the full decision layer.
|
|
134
139
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
7
|
|
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
9
|
-
<title>Benchmarks — formative 2.2.
|
|
9
|
+
<title>Benchmarks — formative 2.2.4 documentation</title>
|
|
10
10
|
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=b86133f3" />
|
|
11
11
|
<link rel="stylesheet" type="text/css" href="../_static/css/theme.css?v=9edc463e" />
|
|
12
12
|
<link rel="stylesheet" type="text/css" href="../_static/copybutton.css?v=76b2166b" />
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<link rel="canonical" href="https://docs.getformative.dev/causal/benchmarks.html" />
|
|
17
17
|
<script src="../_static/jquery.js?v=5d32c60e"></script>
|
|
18
18
|
<script src="../_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
|
|
19
|
-
<script src="../_static/documentation_options.js?v=
|
|
19
|
+
<script src="../_static/documentation_options.js?v=4e6cfea6"></script>
|
|
20
20
|
<script src="../_static/doctools.js?v=fd6eb6e6"></script>
|
|
21
21
|
<script src="../_static/sphinx_highlight.js?v=6ffebe34"></script>
|
|
22
22
|
<script src="../_static/clipboard.min.js?v=a7894cd8"></script>
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
|
51
51
|
<p class="caption" role="heading"><span class="caption-text">Causal Estimation</span></p>
|
|
52
52
|
<ul class="current">
|
|
53
|
-
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
|
|
54
53
|
<li class="toctree-l1"><a class="reference internal" href="motivation.html">Motivation</a></li>
|
|
54
|
+
<li class="toctree-l1"><a class="reference internal" href="introduction.html">Introduction</a></li>
|
|
55
55
|
<li class="toctree-l1"><a class="reference internal" href="dag.html">DAG</a></li>
|
|
56
56
|
<li class="toctree-l1"><a class="reference internal" href="estimands.html">Estimands</a></li>
|
|
57
57
|
<li class="toctree-l1"><a class="reference internal" href="wizard.html">Choosing an estimator</a></li>
|
|
@@ -210,7 +210,9 @@ treatment/outcome relationship. The observational DAG declares this:</p>
|
|
|
210
210
|
<span class="n">obs_dag</span><span class="o">.</span><span class="n">assume</span><span class="p">(</span><span class="n">c</span><span class="p">)</span><span class="o">.</span><span class="n">causes</span><span class="p">(</span><span class="s2">"treat"</span><span class="p">,</span> <span class="s2">"re78"</span><span class="p">)</span>
|
|
211
211
|
<span class="n">obs_dag</span><span class="o">.</span><span class="n">assume</span><span class="p">(</span><span class="s2">"treat"</span><span class="p">)</span><span class="o">.</span><span class="n">causes</span><span class="p">(</span><span class="s2">"re78"</span><span class="p">)</span>
|
|
212
212
|
|
|
213
|
-
<span class="n">result</span> <span class="o">=</span> <span class="n">OLSObservational</span><span class="p">(</span
|
|
213
|
+
<span class="n">result</span> <span class="o">=</span> <span class="n">OLSObservational</span><span class="p">(</span>
|
|
214
|
+
<span class="n">obs_dag</span><span class="p">,</span> <span class="n">treatment</span><span class="o">=</span><span class="s2">"treat"</span><span class="p">,</span> <span class="n">outcome</span><span class="o">=</span><span class="s2">"re78"</span>
|
|
215
|
+
<span class="p">)</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">observational</span><span class="p">)</span>
|
|
214
216
|
<span class="nb">print</span><span class="p">(</span><span class="n">result</span><span class="o">.</span><span class="n">summary</span><span class="p">())</span>
|
|
215
217
|
</pre></div>
|
|
216
218
|
</div>
|
|
@@ -244,7 +246,9 @@ linear model extrapolates across incomparable people. Propensity score matching
|
|
|
244
246
|
each treated person only with the most similar controls:</p>
|
|
245
247
|
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span><span class="w"> </span><span class="nn">formative.causal</span><span class="w"> </span><span class="kn">import</span> <span class="n">PropensityScoreMatching</span>
|
|
246
248
|
|
|
247
|
-
<span class="n">result</span> <span class="o">=</span> <span class="n">PropensityScoreMatching</span><span class="p">(</span
|
|
249
|
+
<span class="n">result</span> <span class="o">=</span> <span class="n">PropensityScoreMatching</span><span class="p">(</span>
|
|
250
|
+
<span class="n">obs_dag</span><span class="p">,</span> <span class="n">treatment</span><span class="o">=</span><span class="s2">"treat"</span><span class="p">,</span> <span class="n">outcome</span><span class="o">=</span><span class="s2">"re78"</span>
|
|
251
|
+
<span class="p">)</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">observational</span><span class="p">)</span>
|
|
248
252
|
<span class="nb">print</span><span class="p">(</span><span class="n">result</span><span class="o">.</span><span class="n">summary</span><span class="p">())</span>
|
|
249
253
|
</pre></div>
|
|
250
254
|
</div>
|
|
@@ -386,7 +390,10 @@ staff plus managers plus half the part-time staff.</p>
|
|
|
386
390
|
Table 3 reports four cell means; formative’s DiD will be built from exactly these:</p>
|
|
387
391
|
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">state</span><span class="p">,</span> <span class="n">name</span> <span class="ow">in</span> <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="s2">"NJ"</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s2">"PA"</span><span class="p">)]:</span>
|
|
388
392
|
<span class="n">sub</span> <span class="o">=</span> <span class="n">raw</span><span class="p">[</span><span class="n">raw</span><span class="o">.</span><span class="n">STATE</span> <span class="o">==</span> <span class="n">state</span><span class="p">]</span>
|
|
389
|
-
<span class="nb">print</span><span class="p">(</span
|
|
393
|
+
<span class="nb">print</span><span class="p">(</span>
|
|
394
|
+
<span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">: before </span><span class="si">{</span><span class="n">sub</span><span class="o">.</span><span class="n">fte_before</span><span class="o">.</span><span class="n">mean</span><span class="p">()</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2">, "</span>
|
|
395
|
+
<span class="sa">f</span><span class="s2">"after </span><span class="si">{</span><span class="n">sub</span><span class="o">.</span><span class="n">fte_after</span><span class="o">.</span><span class="n">mean</span><span class="p">()</span><span class="si">:</span><span class="s2">.2f</span><span class="si">}</span><span class="s2">"</span>
|
|
396
|
+
<span class="p">)</span>
|
|
390
397
|
</pre></div>
|
|
391
398
|
</div>
|
|
392
399
|
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span>NJ: before 20.44, after 21.03
|
|
@@ -406,8 +413,18 @@ binary group and time indicators:</p>
|
|
|
406
413
|
|
|
407
414
|
<span class="n">long</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">concat</span><span class="p">(</span>
|
|
408
415
|
<span class="p">[</span>
|
|
409
|
-
<span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">({</span
|
|
410
|
-
|
|
416
|
+
<span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">({</span>
|
|
417
|
+
<span class="s2">"store"</span><span class="p">:</span> <span class="n">raw</span><span class="o">.</span><span class="n">SHEET</span><span class="p">,</span>
|
|
418
|
+
<span class="s2">"nj"</span><span class="p">:</span> <span class="n">raw</span><span class="o">.</span><span class="n">STATE</span><span class="p">,</span>
|
|
419
|
+
<span class="s2">"after"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
|
|
420
|
+
<span class="s2">"fte"</span><span class="p">:</span> <span class="n">raw</span><span class="o">.</span><span class="n">fte_before</span><span class="p">,</span>
|
|
421
|
+
<span class="p">}),</span>
|
|
422
|
+
<span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">({</span>
|
|
423
|
+
<span class="s2">"store"</span><span class="p">:</span> <span class="n">raw</span><span class="o">.</span><span class="n">SHEET</span><span class="p">,</span>
|
|
424
|
+
<span class="s2">"nj"</span><span class="p">:</span> <span class="n">raw</span><span class="o">.</span><span class="n">STATE</span><span class="p">,</span>
|
|
425
|
+
<span class="s2">"after"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
|
|
426
|
+
<span class="s2">"fte"</span><span class="p">:</span> <span class="n">raw</span><span class="o">.</span><span class="n">fte_after</span><span class="p">,</span>
|
|
427
|
+
<span class="p">}),</span>
|
|
411
428
|
<span class="p">],</span>
|
|
412
429
|
<span class="n">ignore_index</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span>
|
|
413
430
|
<span class="p">)</span><span class="o">.</span><span class="n">dropna</span><span class="p">(</span><span class="n">subset</span><span class="o">=</span><span class="p">[</span><span class="s2">"fte"</span><span class="p">])</span>
|