From ae8ec15a098cf40272c34a23b89dccb96bf6030d Mon Sep 17 00:00:00 2001 From: Thomas Pinder Date: Tue, 3 Sep 2024 07:27:26 +0000 Subject: [PATCH 1/8] WIP - placebo testing --- examples/placebo_test.pct.py | 51 +++++++++++++++++ pyproject.toml | 1 + src/causal_validation/models.py | 22 ++++++++ src/causal_validation/validation/__init__.py | 0 src/causal_validation/validation/placebo.py | 58 ++++++++++++++++++++ 5 files changed, 132 insertions(+) create mode 100644 examples/placebo_test.pct.py create mode 100644 src/causal_validation/models.py create mode 100644 src/causal_validation/validation/__init__.py create mode 100644 src/causal_validation/validation/placebo.py diff --git a/examples/placebo_test.pct.py b/examples/placebo_test.pct.py new file mode 100644 index 0000000..59a5da5 --- /dev/null +++ b/examples/placebo_test.pct.py @@ -0,0 +1,51 @@ +# --- +# jupyter: +# jupytext: +# cell_metadata_filter: -all +# custom_cell_magics: kql +# text_representation: +# extension: .py +# format_name: percent +# format_version: '1.3' +# jupytext_version: 1.11.2 +# kernelspec: +# display_name: causal-validation +# language: python +# name: python3 +# --- + +# %% +from causal_validation import ( + Config, + simulate, +) +from causal_validation.effects import StaticEffect +from causal_validation.plotters import plot +from causal_validation.transforms import ( + Periodic, + Trend, +) +from causal_validation.validation.placebo import PlaceboTest +from causal_validation.models import AZCausalWrapper +from azcausal.estimators.panel.did import DID +from azcausal.core.error import JackKnife + +# %% +cfg = cv.Config( + n_control_units=10, + n_pre_intervention_timepoints=60, + n_post_intervention_timepoints=30, + seed=123, +) + +TRUE_EFFECT = 0.05 +effect = StaticEffect(effect=TRUE_EFFECT) +data = effect(simulate(cfg)) +plot(data) + +# %% +model = AZCausalWrapper(model=VTLab(), error_estimator=JackKnife()) +result = PlaceboTest(model, data).execute() +result.summary() + +# %% diff --git a/pyproject.toml b/pyproject.toml index f9968f3..0112fd3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ dependencies = [ "matplotlib", "numpy", "pandas", + "pandera" ] [tool.hatch.build] diff --git a/src/causal_validation/models.py b/src/causal_validation/models.py new file mode 100644 index 0000000..aad7727 --- /dev/null +++ b/src/causal_validation/models.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass +import typing as tp + +from azcausal.core.effect import Effect +from azcausal.core.error import Error +from azcausal.core.estimator import Estimator + +from causal_validation.data import Dataset + + +@dataclass +class AZCausalWrapper: + model: Estimator + error_estimator: tp.Optional[Error] = None + + def __call__(self, data: Dataset, **kwargs) -> Effect: + panel = data.to_azcausal() + result = self.model.fit(panel, **kwargs) + if self.error_estimator: + self.model.error(result, self.error_estimator) + breakpoint() + return result diff --git a/src/causal_validation/validation/__init__.py b/src/causal_validation/validation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/causal_validation/validation/placebo.py b/src/causal_validation/validation/placebo.py new file mode 100644 index 0000000..9514c64 --- /dev/null +++ b/src/causal_validation/validation/placebo.py @@ -0,0 +1,58 @@ +from dataclasses import dataclass +import typing as tp + +from azcausal.core.effect import Effect + +from causal_validation.data import Dataset +from causal_validation.models import AZCausalWrapper +import pandas as pd +import numpy as np +from pandera import Check, Column, DataFrameSchema +from tqdm import trange + + +PlaceboSchema = DataFrameSchema( + { + "Effect": Column(float, coerce=True), + "Standard Deviation": Column( + float, checks=[Check.greater_than(0.0)], coerce=True + ), + "Standard Error": Column(float, checks=[Check.greater_than(0.0)], coerce=True), + } +) + + +@dataclass +class PlaceboTestResult: + effects: tp.List[Effect] + + def summary(self) -> pd.DataFrame: + _effects = [effect.value for effect in self.effects] + _n_effects = len(_effects) + expected_effect = np.mean(_effects) + stddev_effect = np.std(_effects) + std_error = stddev_effect / np.sqrt(_n_effects) + result = { + "Effect": expected_effect, + "Standard Deviation": stddev_effect, + "Standard Error": std_error, + } + result_df = pd.DataFrame([result]) + PlaceboSchema.validate(result_df) + return result_df + + +@dataclass +class PlaceboTest: + model: AZCausalWrapper + dataset: Dataset + + def execute(self) -> PlaceboTestResult: + n_control_units = self.dataset.n_units + results = [] + for i in trange(n_control_units): + placebo_data = self.dataset.to_placebo_data(i) + result = self.model(placebo_data) + result = result.effect.percentage() + results.append(result) + return PlaceboTestResult(effects=results) From 9a45aac206cc9df20da7572964554f1a1d9e23eb Mon Sep 17 00:00:00 2001 From: Thomas Pinder Date: Wed, 4 Sep 2024 18:24:20 +0000 Subject: [PATCH 2/8] Update notebook --- examples/placebo_test.pct.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/placebo_test.pct.py b/examples/placebo_test.pct.py index 59a5da5..09a230f 100644 --- a/examples/placebo_test.pct.py +++ b/examples/placebo_test.pct.py @@ -28,10 +28,11 @@ from causal_validation.validation.placebo import PlaceboTest from causal_validation.models import AZCausalWrapper from azcausal.estimators.panel.did import DID +from azcausal.estimators.panel.sdid import SDID from azcausal.core.error import JackKnife # %% -cfg = cv.Config( +cfg = Config( n_control_units=10, n_pre_intervention_timepoints=60, n_post_intervention_timepoints=30, @@ -44,8 +45,13 @@ plot(data) # %% -model = AZCausalWrapper(model=VTLab(), error_estimator=JackKnife()) +model = AZCausalWrapper(model=SDID(), error_estimator=JackKnife()) result = PlaceboTest(model, data).execute() result.summary() +# %% [markdown] +# - Can you answer Qs around the data + +# %% + # %% From a5873b2cf886c65a4d196f52dc0f4abf089c9558 Mon Sep 17 00:00:00 2001 From: Thomas Pinder Date: Wed, 4 Sep 2024 21:13:25 +0000 Subject: [PATCH 3/8] Add placebo testing --- src/causal_validation/models.py | 1 - .../test_validation/test_placebo.py | 56 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 tests/test_causal_validation/test_validation/test_placebo.py diff --git a/src/causal_validation/models.py b/src/causal_validation/models.py index aad7727..70172ea 100644 --- a/src/causal_validation/models.py +++ b/src/causal_validation/models.py @@ -18,5 +18,4 @@ def __call__(self, data: Dataset, **kwargs) -> Effect: result = self.model.fit(panel, **kwargs) if self.error_estimator: self.model.error(result, self.error_estimator) - breakpoint() return result diff --git a/tests/test_causal_validation/test_validation/test_placebo.py b/tests/test_causal_validation/test_validation/test_placebo.py new file mode 100644 index 0000000..626c1b5 --- /dev/null +++ b/tests/test_causal_validation/test_validation/test_placebo.py @@ -0,0 +1,56 @@ +import pytest +import numpy as np + +from causal_validation.validation.placebo import ( + PlaceboTestResult, + PlaceboTest, + PlaceboSchema, +) +from causal_validation.models import AZCausalWrapper +from causal_validation.transforms import Trend +from hypothesis import given, settings, strategies as st +from causal_validation.testing import simulate_data, TestConstants +from azcausal.estimators.panel.did import DID +from azcausal.estimators.panel.sdid import SDID +import pandas as pd +import typing as tp + + +def test_schema_coerce(): + df = PlaceboSchema.example() + cols = df.columns + for col in cols: + df[col] = np.ceil((df[col])) + PlaceboSchema.validate(df) + + +@given( + global_mean=st.floats(min_value=0.0, max_value=10.0), + seed=st.integers(min_value=0, max_value=1000000), + n_control=st.integers(min_value=10, max_value=20), + model=st.sampled_from([DID(), SDID()]), +) +@settings(max_examples=10) +def test_placebo_test( + global_mean: float, seed: int, n_control: int, model: tp.Union[DID, SDID] +): + # Simulate data with a trend + constants = TestConstants(N_CONTROL=n_control, GLOBAL_SCALE=0.001) + data = simulate_data(global_mean=global_mean, seed=seed, constants=constants) + trend_term = Trend(degree=1, coefficient=0.1) + data = trend_term(data) + + # Execute the placebo test + model = AZCausalWrapper(model) + result = PlaceboTest(model, data).execute() + + # Check that the structure of result + assert isinstance(result, PlaceboTestResult) + assert len(result.effects) == n_control + + # Check the results are close to the true effect + summary = result.summary() + PlaceboSchema.validate(summary) + assert isinstance(summary, pd.DataFrame) + assert summary.shape == (1, 3) + assert summary["Effect"].iloc[0] == pytest.approx(0.0, abs=0.1) From 6052d2133436923700b544086536085e4276dab0 Mon Sep 17 00:00:00 2001 From: Thomas Pinder Date: Wed, 4 Sep 2024 21:14:06 +0000 Subject: [PATCH 4/8] Format --- examples/placebo_test.pct.py | 11 +++---- src/causal_validation/validation/placebo.py | 13 ++++---- .../test_validation/test_placebo.py | 30 ++++++++++++------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/examples/placebo_test.pct.py b/examples/placebo_test.pct.py index 09a230f..e0c8e68 100644 --- a/examples/placebo_test.pct.py +++ b/examples/placebo_test.pct.py @@ -7,7 +7,7 @@ # extension: .py # format_name: percent # format_version: '1.3' -# jupytext_version: 1.11.2 +# jupytext_version: 1.16.4 # kernelspec: # display_name: causal-validation # language: python @@ -15,21 +15,22 @@ # --- # %% +from azcausal.core.error import JackKnife +from azcausal.estimators.panel.did import DID +from azcausal.estimators.panel.sdid import SDID + from causal_validation import ( Config, simulate, ) from causal_validation.effects import StaticEffect +from causal_validation.models import AZCausalWrapper from causal_validation.plotters import plot from causal_validation.transforms import ( Periodic, Trend, ) from causal_validation.validation.placebo import PlaceboTest -from causal_validation.models import AZCausalWrapper -from azcausal.estimators.panel.did import DID -from azcausal.estimators.panel.sdid import SDID -from azcausal.core.error import JackKnife # %% cfg = Config( diff --git a/src/causal_validation/validation/placebo.py b/src/causal_validation/validation/placebo.py index 9514c64..8a6423b 100644 --- a/src/causal_validation/validation/placebo.py +++ b/src/causal_validation/validation/placebo.py @@ -2,14 +2,17 @@ import typing as tp from azcausal.core.effect import Effect - -from causal_validation.data import Dataset -from causal_validation.models import AZCausalWrapper -import pandas as pd import numpy as np -from pandera import Check, Column, DataFrameSchema +import pandas as pd +from pandera import ( + Check, + Column, + DataFrameSchema, +) from tqdm import trange +from causal_validation.data import Dataset +from causal_validation.models import AZCausalWrapper PlaceboSchema = DataFrameSchema( { diff --git a/tests/test_causal_validation/test_validation/test_placebo.py b/tests/test_causal_validation/test_validation/test_placebo.py index 626c1b5..ed58bf2 100644 --- a/tests/test_causal_validation/test_validation/test_placebo.py +++ b/tests/test_causal_validation/test_validation/test_placebo.py @@ -1,19 +1,27 @@ -import pytest +import typing as tp + +from azcausal.estimators.panel.did import DID +from azcausal.estimators.panel.sdid import SDID +from hypothesis import ( + given, + settings, + strategies as st, +) import numpy as np +import pandas as pd +import pytest +from causal_validation.models import AZCausalWrapper +from causal_validation.testing import ( + TestConstants, + simulate_data, +) +from causal_validation.transforms import Trend from causal_validation.validation.placebo import ( - PlaceboTestResult, - PlaceboTest, PlaceboSchema, + PlaceboTest, + PlaceboTestResult, ) -from causal_validation.models import AZCausalWrapper -from causal_validation.transforms import Trend -from hypothesis import given, settings, strategies as st -from causal_validation.testing import simulate_data, TestConstants -from azcausal.estimators.panel.did import DID -from azcausal.estimators.panel.sdid import SDID -import pandas as pd -import typing as tp def test_schema_coerce(): From d9d495f0184f8ed9f6fd82f5c0e0bb9c93cf7ac2 Mon Sep 17 00:00:00 2001 From: Thomas Pinder Date: Thu, 5 Sep 2024 06:02:46 +0000 Subject: [PATCH 5/8] Add testing --- src/causal_validation/models.py | 4 +- tests/test_causal_validation/test_models.py | 48 +++++++++++++++++++ tests/test_causal_validation/test_plotters.py | 8 ++-- 3 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 tests/test_causal_validation/test_models.py diff --git a/src/causal_validation/models.py b/src/causal_validation/models.py index 70172ea..bd41327 100644 --- a/src/causal_validation/models.py +++ b/src/causal_validation/models.py @@ -4,7 +4,7 @@ from azcausal.core.effect import Effect from azcausal.core.error import Error from azcausal.core.estimator import Estimator - +from azcausal.core.result import Result from causal_validation.data import Dataset @@ -13,7 +13,7 @@ class AZCausalWrapper: model: Estimator error_estimator: tp.Optional[Error] = None - def __call__(self, data: Dataset, **kwargs) -> Effect: + def __call__(self, data: Dataset, **kwargs) -> Result: panel = data.to_azcausal() result = self.model.fit(panel, **kwargs) if self.error_estimator: diff --git a/tests/test_causal_validation/test_models.py b/tests/test_causal_validation/test_models.py new file mode 100644 index 0000000..85783f8 --- /dev/null +++ b/tests/test_causal_validation/test_models.py @@ -0,0 +1,48 @@ +import numpy as np +import typing as tp +from causal_validation.models import AZCausalWrapper +from azcausal.estimators.panel import did, sdid +from azcausal.core.error import JackKnife, Bootstrap, Placebo +from hypothesis import given, settings, strategies as st +from azcausal.core.error import Error +from azcausal.core.estimator import Estimator +from causal_validation.testing import simulate_data, TestConstants +from azcausal.core.result import Result +from azcausal.core.effect import Effect + +MODELS = [did.DID(), sdid.SDID()] +MODEL_ERROR = [ + (did.DID(), None), + (sdid.SDID(), None), + (sdid.SDID(), Bootstrap()), + (sdid.SDID(), JackKnife()), +] + + +@given( + model_error=st.sampled_from(MODEL_ERROR), + n_control=st.integers(min_value=2, max_value=5), + n_pre_treatment=st.integers(min_value=1, max_value=50), + n_post_treatment=st.integers(min_value=1, max_value=50), + seed=st.integers(min_value=1, max_value=100), +) +@settings(max_examples=10) +def test_call( + model_error: tp.Union[Estimator, Error], + n_control: int, + n_pre_treatment: int, + n_post_treatment: int, + seed: int, +): + constancts = TestConstants( + N_CONTROL=n_control, + N_PRE_TREATMENT=n_pre_treatment, + N_POST_TREATMENT=n_post_treatment, + ) + data = simulate_data(global_mean=10.0, seed=seed, constants=constancts) + model = AZCausalWrapper(*model_error) + result = model(data) + + assert isinstance(result, Result) + assert isinstance(result.effect, Effect) + assert not np.isnan(result.effect.value) diff --git a/tests/test_causal_validation/test_plotters.py b/tests/test_causal_validation/test_plotters.py index fcdd286..a034fde 100644 --- a/tests/test_causal_validation/test_plotters.py +++ b/tests/test_causal_validation/test_plotters.py @@ -30,12 +30,12 @@ @given( - n_control=st.integers(min_value=1, max_value=50), + n_control=st.integers(min_value=1, max_value=10), n_pre_treatment=st.integers(min_value=1, max_value=50), n_post_treatment=st.integers(min_value=1, max_value=50), ax_bool=st.booleans(), ) -@settings(max_examples=5) +@settings(max_examples=10) def test_plot( n_control: int, n_pre_treatment: int, n_post_treatment: int, ax_bool: bool ): @@ -47,7 +47,9 @@ def test_plot( data = simulate_data(0.0, DEFAULT_SEED, constants=constants) if ax_bool: _, ax = plt.subplots() - ax = plot(data) + else: + ax = None + ax = plot(data, ax=ax) assert isinstance(ax, Axes) assert len(ax.lines) == n_control + 2 assert ax.get_legend() is not None From 16f15622e9d52192b3b214d2cc2e9df133c356c9 Mon Sep 17 00:00:00 2001 From: Thomas Pinder Date: Thu, 5 Sep 2024 06:05:55 +0000 Subject: [PATCH 6/8] Format --- src/causal_validation/config.py | 1 - src/causal_validation/models.py | 2 +- tests/test_causal_validation/test_models.py | 31 +++++++++++++++------ 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/causal_validation/config.py b/src/causal_validation/config.py index 554206f..8040be1 100644 --- a/src/causal_validation/config.py +++ b/src/causal_validation/config.py @@ -3,7 +3,6 @@ field, ) import datetime as dt -import typing as tp import numpy as np diff --git a/src/causal_validation/models.py b/src/causal_validation/models.py index bd41327..5b81fb4 100644 --- a/src/causal_validation/models.py +++ b/src/causal_validation/models.py @@ -1,10 +1,10 @@ from dataclasses import dataclass import typing as tp -from azcausal.core.effect import Effect from azcausal.core.error import Error from azcausal.core.estimator import Estimator from azcausal.core.result import Result + from causal_validation.data import Dataset diff --git a/tests/test_causal_validation/test_models.py b/tests/test_causal_validation/test_models.py index 85783f8..feeb1ce 100644 --- a/tests/test_causal_validation/test_models.py +++ b/tests/test_causal_validation/test_models.py @@ -1,14 +1,29 @@ -import numpy as np import typing as tp -from causal_validation.models import AZCausalWrapper -from azcausal.estimators.panel import did, sdid -from azcausal.core.error import JackKnife, Bootstrap, Placebo -from hypothesis import given, settings, strategies as st -from azcausal.core.error import Error + +from azcausal.core.effect import Effect +from azcausal.core.error import ( + Bootstrap, + Error, + JackKnife, +) from azcausal.core.estimator import Estimator -from causal_validation.testing import simulate_data, TestConstants from azcausal.core.result import Result -from azcausal.core.effect import Effect +from azcausal.estimators.panel import ( + did, + sdid, +) +from hypothesis import ( + given, + settings, + strategies as st, +) +import numpy as np + +from causal_validation.models import AZCausalWrapper +from causal_validation.testing import ( + TestConstants, + simulate_data, +) MODELS = [did.DID(), sdid.SDID()] MODEL_ERROR = [ From b01a5be4a67df50a3f742eacd0fbbb353db0c96d Mon Sep 17 00:00:00 2001 From: Thomas Pinder Date: Thu, 5 Sep 2024 16:56:43 +0000 Subject: [PATCH 7/8] Add notebook --- examples/placebo_test.pct.py | 53 +++++++++++++++++++-- src/causal_validation/__about__.py | 2 +- src/causal_validation/validation/placebo.py | 4 ++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/examples/placebo_test.pct.py b/examples/placebo_test.pct.py index e0c8e68..7016483 100644 --- a/examples/placebo_test.pct.py +++ b/examples/placebo_test.pct.py @@ -14,6 +14,24 @@ # name: python3 # --- +# %% [markdown] +# # Placebo Testing +# +# A placebo test is an approach to assess the validity of a causal model by checking if +# the effect can truly be attributed to the treatment, or to other spurious factors. A +# placebo test is conducted by iterating through the set of control units and at each +# iteration, replacing the treated unit by one of the control units and measuring the +# effect. If the model detects a significant effect, then it suggests potential bias or +# omitted variables in the analysis, indicating that the causal inference is flawed. +# +# A successful placebo test will show no statistically significant results and we may +# then conclude that the estimated effect can be attributed to the treatment and not +# driven by confounding factors. Conversely, a failed placebo test, which shows +# significant results, suggests that the identified treatment effect may not be +# reliable. Placebo testing is thus a critical step to ensure the robustness of findings +# in RCTs. In this notebook, we demonstrate how a placebo test can be conducted in +# `causal-validation`. + # %% from azcausal.core.error import JackKnife from azcausal.estimators.panel.did import DID @@ -32,6 +50,13 @@ ) from causal_validation.validation.placebo import PlaceboTest +# %% [markdown] +# ## Data simulation +# +# To demonstrate a placebo test, we must first simulate some data. For the purposes of +# illustration, we'll simulate a very simple dataset containing 10 control units where +# each unit has 60 pre-intervention observations, and 30 post-intervention observations. + # %% cfg = Config( n_control_units=10, @@ -45,14 +70,32 @@ data = effect(simulate(cfg)) plot(data) +# %% [markdown] +# ## Model +# +# We'll now define our model. To do this, we'll use the synthetic +# difference-in-differences implementation of AZCausal. This implementation, along with +# any other model from AZCausal, can be neatly wrapped up in our `AZCausalWrapper` to +# make fitting and effect estimation simpler. + # %% model = AZCausalWrapper(model=SDID(), error_estimator=JackKnife()) -result = PlaceboTest(model, data).execute() -result.summary() # %% [markdown] -# - Can you answer Qs around the data - -# %% +# ## Placebo Test Results +# +# Now that we have a dataset and model defined, we may conduct our placebo test. With 10 +# control units, the test will estimate 10 individual effects; 1 per control unit when +# it is mocked as the treated group. With those 10 effects, the routine will then +# produce the mean estimated effect, along with the standard deviation across the +# estimated effect, the effect's standard error, and the p-value that corresponds to the +# null-hypothesis test that the effect is 0. +# +# In the below, we see that expected estimated effect is small at just 0.08. +# Accordingly, the p-value attains a value of 0.5, indicating that we have insufficient +# evidence to reject the null hypothesis and we, therefore, have no evidence to suggest +# that there is bias within this particular setup. # %% +result = PlaceboTest(model, data).execute() +result.summary() diff --git a/src/causal_validation/__about__.py b/src/causal_validation/__about__.py index dacd2b4..f83f980 100644 --- a/src/causal_validation/__about__.py +++ b/src/causal_validation/__about__.py @@ -1,3 +1,3 @@ -__version__ = "0.0.3" +__version__ = "0.0.4" __all__ = ["__version__"] diff --git a/src/causal_validation/validation/placebo.py b/src/causal_validation/validation/placebo.py index 8a6423b..f12e2eb 100644 --- a/src/causal_validation/validation/placebo.py +++ b/src/causal_validation/validation/placebo.py @@ -9,6 +9,7 @@ Column, DataFrameSchema, ) +from scipy.stats import ttest_1samp from tqdm import trange from causal_validation.data import Dataset @@ -21,6 +22,7 @@ float, checks=[Check.greater_than(0.0)], coerce=True ), "Standard Error": Column(float, checks=[Check.greater_than(0.0)], coerce=True), + "p-value": Column(float, coerce=True), } ) @@ -35,10 +37,12 @@ def summary(self) -> pd.DataFrame: expected_effect = np.mean(_effects) stddev_effect = np.std(_effects) std_error = stddev_effect / np.sqrt(_n_effects) + p_value = ttest_1samp(_effects, 0, alternative="two-sided").pvalue result = { "Effect": expected_effect, "Standard Deviation": stddev_effect, "Standard Error": std_error, + "p-value": p_value, } result_df = pd.DataFrame([result]) PlaceboSchema.validate(result_df) From e01c9fbe3bb7376c0058a046a6cef3d16044c76b Mon Sep 17 00:00:00 2001 From: Thomas Pinder Date: Thu, 5 Sep 2024 16:58:26 +0000 Subject: [PATCH 8/8] Fix flaky test --- tests/test_causal_validation/test_validation/test_placebo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_causal_validation/test_validation/test_placebo.py b/tests/test_causal_validation/test_validation/test_placebo.py index ed58bf2..12557b0 100644 --- a/tests/test_causal_validation/test_validation/test_placebo.py +++ b/tests/test_causal_validation/test_validation/test_placebo.py @@ -60,5 +60,5 @@ def test_placebo_test( summary = result.summary() PlaceboSchema.validate(summary) assert isinstance(summary, pd.DataFrame) - assert summary.shape == (1, 3) + assert summary.shape == (1, 4) assert summary["Effect"].iloc[0] == pytest.approx(0.0, abs=0.1)