diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d828b564..3d91052c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,5 @@ name: PyPI release + on: workflow_dispatch: pull_request: @@ -23,6 +24,7 @@ jobs: run: | pip install build python -m build + ls dist # List the contents of the dist directory - name: Check the sdist installs and imports run: | mkdir -p test-sdist @@ -30,21 +32,22 @@ jobs: python -m venv venv-sdist venv-sdist/bin/python -m pip install ../dist/causalpy*.tar.gz echo "Checking import and version number (on release)" - venv-sdist/bin/python -c "import causalpy; assert causalpy.__version__ == '${{ github.ref_name }}' if '${{ github.ref_type }}' == 'tag' else causalpy.__version__; print(causalpy.__version__)" + venv-sdist/bin/python -c "import causalpy; assert causalpy.__version__ == '${{ github.ref_name }}' if '${{ github.ref_type }}' == 'tag' else causalpy.__version__; print(causalpy.__version__)" cd .. - name: Check the bdist installs and imports run: | mkdir -p test-bdist cd test-bdist python -m venv venv-bdist - venv-bdist/bin/python -m pip install ../dist/CausalPy*.whl + venv-bdist/bin/python -m pip install ../dist/causalpy*.whl echo "Checking import and version number (on release)" - venv-bdist/bin/python -c "import causalpy; assert causalpy.__version__ == '${{ github.ref_name }}' if '${{ github.ref_type }}' == 'tag' else causalpy.__version__; print(causalpy.__version__)" + venv-bdist/bin/python -c "import causalpy; assert causalpy.__version__ == '${{ github.ref_name }}' if '${{ github.ref_type }}' == 'tag' else causalpy.__version__; print(causalpy.__version__)" cd .. - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: artifact path: dist/* + test: name: Upload to Test PyPI permissions: @@ -72,7 +75,7 @@ jobs: python -m venv venv-test-pypi venv-test-pypi/bin/python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple causalpy echo "Checking import and version number" - venv-test-pypi/bin/python -c "import causalpy; assert causalpy.__version__ == '${{ github.ref_name }}'" + venv-test-pypi/bin/python -c "import causalpy; assert causalpy.__version__ == '${{ github.ref_name }}'" publish: environment: release diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index de13785a..77ae5882 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -25,7 +25,7 @@ repos: exclude: &exclude_pattern 'iv_weak_instruments.ipynb' args: ["--maxkb=1500"] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.4 + rev: v0.9.8 hooks: # Run the linter - id: ruff @@ -42,7 +42,7 @@ repos: # see here https://github.com/econchick/interrogate/issues/60#issuecomment-735436566 pass_filenames: false - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.1 hooks: - id: codespell additional_dependencies: diff --git a/causalpy/pymc_models.py b/causalpy/pymc_models.py index 0c2e167c..89605259 100644 --- a/causalpy/pymc_models.py +++ b/causalpy/pymc_models.py @@ -164,7 +164,7 @@ def print_row( ) -> None: """Print one row of the coefficient table""" formatted_name = f" {name: <{max_label_length}}" - formatted_val = f"{round_num(coeff_samples.mean().data, round_to)}, 94% HDI [{round_num(coeff_samples.quantile(0.03).data, round_to)}, {round_num(coeff_samples.quantile(1-0.03).data, round_to)}]" # noqa: E501 + formatted_val = f"{round_num(coeff_samples.mean().data, round_to)}, 94% HDI [{round_num(coeff_samples.quantile(0.03).data, round_to)}, {round_num(coeff_samples.quantile(1 - 0.03).data, round_to)}]" # noqa: E501 print(f" {formatted_name} {formatted_val}") print("Model coefficients:") diff --git a/causalpy/tests/test_api_stability.py b/causalpy/tests/test_api_stability.py index 7e61cdc1..495ee4f5 100644 --- a/causalpy/tests/test_api_stability.py +++ b/causalpy/tests/test_api_stability.py @@ -36,9 +36,9 @@ def test_causal_inference_and_discovery_with_python_example(): # Build the model model = cp.pymc_models.WeightedSumFitter(sample_kwargs=sample_kwargs) - assert isinstance( - model, WeightedSumFitter - ), "model is not an instance of WeightedSumFitter" + assert isinstance(model, WeightedSumFitter), ( + "model is not an instance of WeightedSumFitter" + ) formula = "twitter ~ 0 + tiktok + linkedin + instagram" @@ -49,9 +49,9 @@ def test_causal_inference_and_discovery_with_python_example(): formula=formula, model=model, ) - assert isinstance( - results, SyntheticControl - ), "results is not an instance of SyntheticControl" + assert isinstance(results, SyntheticControl), ( + "results is not an instance of SyntheticControl" + ) fig, ax = results.plot() assert isinstance(fig, plt.Figure) diff --git a/docs/source/index.md b/docs/source/index.md index 72025511..50fc8813 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -96,7 +96,7 @@ CausalPy has a broad range of quasi-experimental methods for causal inference: | Geographical lift | Measures the impact of an intervention in a specific geographic area by comparing it to similar areas without the intervention. Commonly used in marketing to assess regional campaigns. | | ANCOVA | Analysis of Covariance combines ANOVA and regression to control for the effects of one or more quantitative covariates. Used when comparing group means while controlling for other variables. | | Differences in Differences | Compares the changes in outcomes over time between a treatment group and a control group. Used in observational studies to estimate causal effects by accounting for time trends. | -| Regression discontinuity | Identifies causal effects by exploiting a cutoff or threshold in an assignment variable. Used when treatment is assigned based on a threshold value of an observed variable, allowing comparison just above and below the cutoff. | +|Regression discontinuity | Identifies causal effects by exploiting a sharp cutoff or threshold in an assignment variable. Used when treatment is assigned based on a threshold value of an observed variable, allowing comparison just above and below the cutoff. | | Regression kink designs | Focuses on changes in the slope (kinks) of the relationship between variables rather than jumps at cutoff points. Used to identify causal effects when treatment intensity changes at a threshold. | | Interrupted time series | Analyzes the effect of an intervention by comparing time series data before and after the intervention. Used when data is collected over time and an intervention occurs at a known point, allowing assessment of changes in level or trend. | | Instrumental variable regression | Addresses endogeneity by using an instrument variable that is correlated with the endogenous explanatory variable but uncorrelated with the error term. Used when explanatory variables are correlated with the error term, providing consistent estimates of causal effects. | diff --git a/docs/source/notebooks/multi_cell_geolift.ipynb b/docs/source/notebooks/multi_cell_geolift.ipynb index ebba88e9..fc71bb1a 100644 --- a/docs/source/notebooks/multi_cell_geolift.ipynb +++ b/docs/source/notebooks/multi_cell_geolift.ipynb @@ -1099,7 +1099,7 @@ "unpooled_results = []\n", "\n", "for i, target_geo in enumerate(treated):\n", - " print(f\"Analyzing test geo: {target_geo} ({i+1} of {len(treated)})\")\n", + " print(f\"Analyzing test geo: {target_geo} ({i + 1} of {len(treated)})\")\n", " formula = f\"{target_geo} ~ 0 + {' + '.join(untreated)}\"\n", " print(formula)\n", "\n", diff --git a/docs/source/notebooks/rd_pymc.ipynb b/docs/source/notebooks/rd_pymc.ipynb index be23d3c4..a6fba749 100644 --- a/docs/source/notebooks/rd_pymc.ipynb +++ b/docs/source/notebooks/rd_pymc.ipynb @@ -5,7 +5,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Regression discontinuity with `pymc` models" + "# Sharp regression discontinuity with `pymc` models" ] }, { diff --git a/docs/source/notebooks/rd_pymc_drinking.ipynb b/docs/source/notebooks/rd_pymc_drinking.ipynb index 4ffca0db..6052dc88 100644 --- a/docs/source/notebooks/rd_pymc_drinking.ipynb +++ b/docs/source/notebooks/rd_pymc_drinking.ipynb @@ -7,7 +7,7 @@ "source": [ "# Drinking age - Bayesian analysis\n", "\n", - "This example uses the regression discontinuity design to make claims about the causal effects of the minimum legal drinking age (21 in the USA) upon all cause mortality rates. The dataset is from a study by {cite:t}`carpenter2009effect`." + "This example uses the sharp regression discontinuity design to make claims about the causal effects of the minimum legal drinking age (21 in the USA) upon all cause mortality rates. The dataset is from a study by {cite:t}`carpenter2009effect`." ] }, { diff --git a/docs/source/notebooks/rd_skl.ipynb b/docs/source/notebooks/rd_skl.ipynb index b790fad2..e38475b3 100644 --- a/docs/source/notebooks/rd_skl.ipynb +++ b/docs/source/notebooks/rd_skl.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Regression discontinuity with sci-kit learn models" + "# Sharp regression discontinuity with sci-kit learn models" ] }, { diff --git a/docs/source/notebooks/rd_skl_drinking.ipynb b/docs/source/notebooks/rd_skl_drinking.ipynb index 05adda46..0c36fc3b 100644 --- a/docs/source/notebooks/rd_skl_drinking.ipynb +++ b/docs/source/notebooks/rd_skl_drinking.ipynb @@ -7,7 +7,7 @@ "source": [ "# Drinking age with a scikit-learn model\n", "\n", - "This example uses the regression discontinuity design to make claims about the causal effects of the minimum legal drinking age (21 in the USA) upon all cause mortality rates. The dataset is from a study by {cite:t}`carpenter2009effect`." + "This example uses the sharp regression discontinuity design to make claims about the causal effects of the minimum legal drinking age (21 in the USA) upon all cause mortality rates. The dataset is from a study by {cite:t}`carpenter2009effect`." ] }, { diff --git a/docs/source/notebooks/rkink_pymc.ipynb b/docs/source/notebooks/rkink_pymc.ipynb index 2c1d004c..368dece1 100644 --- a/docs/source/notebooks/rkink_pymc.ipynb +++ b/docs/source/notebooks/rkink_pymc.ipynb @@ -108,7 +108,7 @@ " beta = rng.random(5)\n", " ax[0, col].plot(x, f(x, beta, kink), lw=3)\n", " ax[1, col].plot(x, np.gradient(f(x, beta, kink), x), lw=3)\n", - " ax[0, col].set(title=f\"Random {col+1}\")\n", + " ax[0, col].set(title=f\"Random {col + 1}\")\n", " ax[1, col].set(xlabel=\"x\")\n", "\n", "ax[0, 0].set(ylabel=\"$y = f(x)$\")\n",