Skip to content

Commit 6822c61

Browse files
committed
Handle deprecation like a grown up
1 parent f915f77 commit 6822c61

File tree

8 files changed

+400
-5
lines changed

8 files changed

+400
-5
lines changed

causalpy/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
# limitations under the License.
1414
import arviz as az
1515

16+
import causalpy.pymc_experiments as pymc_experiments # to be depricated
1617
import causalpy.pymc_models as pymc_models
18+
import causalpy.skl_experiments as skl_experiments # to be depricated
1719
import causalpy.skl_models as skl_models
1820
from causalpy.version import __version__
1921

@@ -36,9 +38,11 @@
3638
"InversePropensityWeighting",
3739
"load_data",
3840
"PrePostNEGD",
41+
"pymc_experiments", # to be depricated
3942
"pymc_models",
4043
"RegressionDiscontinuity",
4144
"RegressionKink",
45+
"skl_experiments", # to be depricated
4246
"skl_models",
4347
"SyntheticControl",
4448
]

causalpy/pymc_experiments.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Copyright 2024 The PyMC Labs Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
This module exists to maintain the old API where experiment classes were defined in the
16+
`causalpy.pymc_experiments` namespace. They have moved to `causalpy.experiments` and
17+
this module is a thin wrapper around the new classes to maintain backwards
18+
compatibility. A deprication warning is delivered to the user. This module may be
19+
removed in a future release.
20+
"""
21+
22+
import warnings
23+
24+
from .experiments.diff_in_diff import (
25+
DifferenceInDifferences as NewDifferenceInDifferences,
26+
)
27+
from .experiments.instrumental_variable import (
28+
InstrumentalVariable as NewInstrumentalVariable,
29+
)
30+
from .experiments.inverse_propensity_weighting import (
31+
InversePropensityWeighting as NewInversePropensityWeighting,
32+
)
33+
from .experiments.prepostfit import (
34+
InterruptedTimeSeries as NewInterruptedTimeSeries,
35+
)
36+
from .experiments.prepostfit import (
37+
SyntheticControl as NewSyntheticControl,
38+
)
39+
from .experiments.prepostnegd import PrePostNEGD as NewPrePostNEGD
40+
from .experiments.regression_discontinuity import (
41+
RegressionDiscontinuity as NewRegressionDiscontinuity,
42+
)
43+
from .experiments.regression_kink import RegressionKink as NewRegressionKink
44+
45+
# Ensure deprecation warnings are always shown in Jupyter Notebooks
46+
warnings.simplefilter("always", DeprecationWarning)
47+
48+
49+
class PrePostNEGD(NewPrePostNEGD):
50+
def __init__(self, *args, **kwargs):
51+
warnings.warn(
52+
"""causalpy.pymc_experiments.PrePostNEGD is deprecated and will be removed in a future release. Please use causalpy.experiments.PrePostNEGD instead.""",
53+
DeprecationWarning,
54+
stacklevel=2,
55+
)
56+
super().__init__(*args, **kwargs)
57+
58+
59+
class DifferenceInDifferences(NewDifferenceInDifferences):
60+
def __init__(self, *args, **kwargs):
61+
warnings.warn(
62+
"""causalpy.pymc_experiments.DifferenceInDifferences is deprecated and will be removed in a future release. Please use causalpy.experiments.DifferenceInDifferences instead.""",
63+
DeprecationWarning,
64+
stacklevel=2,
65+
)
66+
super().__init__(*args, **kwargs)
67+
68+
69+
class InterruptedTimeSeries(NewInterruptedTimeSeries):
70+
def __init__(self, *args, **kwargs):
71+
warnings.warn(
72+
"""causalpy.pymc_experiments.InterruptedTimeSeries is deprecated and will be removed in a future release. Please use causalpy.experiments.InterruptedTimeSeries instead.""",
73+
DeprecationWarning,
74+
stacklevel=2,
75+
)
76+
super().__init__(*args, **kwargs)
77+
78+
79+
class SyntheticControl(NewSyntheticControl):
80+
def __init__(self, *args, **kwargs):
81+
warnings.warn(
82+
"""causalpy.pymc_experiments.SyntheticControl is deprecated and will be removed in a future release. Please use causalpy.experiments.SyntheticControl instead.""",
83+
DeprecationWarning,
84+
stacklevel=2,
85+
)
86+
super().__init__(*args, **kwargs)
87+
88+
89+
class RegressionKink(NewRegressionKink):
90+
def __init__(self, *args, **kwargs):
91+
warnings.warn(
92+
"""causalpy.pymc_experiments.RegressionKink is deprecated and will be removed in a future release. Please use causalpy.experiments.RegressionKink instead.""",
93+
DeprecationWarning,
94+
stacklevel=2,
95+
)
96+
super().__init__(*args, **kwargs)
97+
98+
99+
class RegressionDiscontinuity(NewRegressionDiscontinuity):
100+
def __init__(self, *args, **kwargs):
101+
warnings.warn(
102+
"""causalpy.pymc_experiments.RegressionDiscontinuity is deprecated and will be removed in a future release. Please use causalpy.experiments.RegressionDiscontinuity instead.""",
103+
DeprecationWarning,
104+
stacklevel=2,
105+
)
106+
super().__init__(*args, **kwargs)
107+
108+
109+
class InversePropensityWeighting(NewInversePropensityWeighting):
110+
def __init__(self, *args, **kwargs):
111+
warnings.warn(
112+
"""causalpy.pymc_experiments.InversePropensityWeighting is deprecated and will be removed in a future release. Please use causalpy.experiments.InversePropensityWeighting instead.""",
113+
DeprecationWarning,
114+
stacklevel=2,
115+
)
116+
super().__init__(*args, **kwargs)
117+
118+
119+
class InstrumentalVariable(NewInstrumentalVariable):
120+
def __init__(self, *args, **kwargs):
121+
warnings.warn(
122+
"""causalpy.pymc_experiments.InstrumentalVariable is deprecated and will be removed in a future release. Please use causalpy.experiments.InstrumentalVariable instead.""",
123+
DeprecationWarning,
124+
stacklevel=2,
125+
)
126+
super().__init__(*args, **kwargs)

causalpy/skl_experiments.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright 2024 The PyMC Labs Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
This module exists to maintain the old API where experiment classes were defined in the
16+
`causalpy.skl_experiments` namespace. They have moved to `causalpy.experiments` and
17+
this module is a thin wrapper around the new classes to maintain backwards
18+
compatibility. A deprication warning is delivered to the user. This module may be
19+
removed in a future release.
20+
"""
21+
22+
import warnings
23+
24+
from .experiments.diff_in_diff import (
25+
DifferenceInDifferences as NewDifferenceInDifferences,
26+
)
27+
from .experiments.prepostfit import (
28+
InterruptedTimeSeries as NewInterruptedTimeSeries,
29+
)
30+
from .experiments.prepostfit import (
31+
SyntheticControl as NewSyntheticControl,
32+
)
33+
from .experiments.regression_discontinuity import (
34+
RegressionDiscontinuity as NewRegressionDiscontinuity,
35+
)
36+
37+
# Ensure deprecation warnings are always shown in Jupyter Notebooks
38+
warnings.simplefilter("always", DeprecationWarning)
39+
40+
41+
class SyntheticControl(NewSyntheticControl):
42+
def __init__(self, *args, **kwargs):
43+
warnings.warn(
44+
"""causalpy.skl_experiments.SyntheticControl is deprecated and will be removed in a future release. Please use causalpy.experiments.SyntheticControl instead.""",
45+
DeprecationWarning,
46+
stacklevel=2,
47+
)
48+
super().__init__(*args, **kwargs)
49+
50+
51+
class DifferenceInDifferences(NewDifferenceInDifferences):
52+
def __init__(self, *args, **kwargs):
53+
warnings.warn(
54+
"""causalpy.skl_experiments.DifferenceInDifferences is deprecated and will be removed in a future release. Please use causalpy.experiments.DifferenceInDifferences instead.""",
55+
DeprecationWarning,
56+
stacklevel=2,
57+
)
58+
super().__init__(*args, **kwargs)
59+
60+
61+
class InterruptedTimeSeries(NewInterruptedTimeSeries):
62+
def __init__(self, *args, **kwargs):
63+
warnings.warn(
64+
"""causalpy.skl_experiments.InterruptedTimeSeries is deprecated and will be removed in a future release. Please use causalpy.experiments.InterruptedTimeSeries instead.""",
65+
DeprecationWarning,
66+
stacklevel=2,
67+
)
68+
super().__init__(*args, **kwargs)
69+
70+
71+
class RegressionDiscontinuity(NewRegressionDiscontinuity):
72+
def __init__(self, *args, **kwargs):
73+
warnings.warn(
74+
"""causalpy.skl_experiments.RegressionDiscontinuity is deprecated and will be removed in a future release. Please use causalpy.experiments.RegressionDiscontinuity instead.""",
75+
DeprecationWarning,
76+
stacklevel=2,
77+
)
78+
super().__init__(*args, **kwargs)

causalpy/tests/test_integration_pymc_examples.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,3 +595,121 @@ def test_iv_reg():
595595
assert isinstance(result, cp.InstrumentalVariable)
596596
assert len(result.idata.posterior.coords["chain"]) == sample_kwargs["chains"]
597597
assert len(result.idata.posterior.coords["draw"]) == sample_kwargs["draws"]
598+
599+
600+
# DEPRECATION WARNING TESTS ============================================================
601+
602+
603+
def test_did_deprecation_warning():
604+
"""Test that the old DifferenceInDifferences class raises a deprecation warning."""
605+
606+
with pytest.warns(DeprecationWarning):
607+
df = cp.load_data("did")
608+
result = cp.pymc_experiments.DifferenceInDifferences(
609+
df,
610+
formula="y ~ 1 + group*post_treatment",
611+
time_variable_name="t",
612+
group_variable_name="group",
613+
model=cp.pymc_models.LinearRegression(sample_kwargs=sample_kwargs),
614+
)
615+
assert isinstance(result, cp.DifferenceInDifferences)
616+
617+
618+
def test_rd_deprecation_warning():
619+
"""Test that the old RegressionDiscontinuity class raises a deprecation warning."""
620+
621+
with pytest.warns(DeprecationWarning):
622+
df = cp.load_data("rd")
623+
result = cp.pymc_experiments.RegressionDiscontinuity(
624+
df,
625+
formula="y ~ 1 + bs(x, df=6) + treated",
626+
model=cp.pymc_models.LinearRegression(sample_kwargs=sample_kwargs),
627+
treatment_threshold=0.5,
628+
epsilon=0.001,
629+
)
630+
assert isinstance(result, cp.RegressionDiscontinuity)
631+
632+
633+
def test_rk_deprecation_warning():
634+
"""Test that the old RegressionKink class raises a deprecation warning."""
635+
636+
with pytest.warns(DeprecationWarning):
637+
kink = 0.5
638+
df = setup_regression_kink_data(kink)
639+
result = cp.pymc_experiments.RegressionKink(
640+
df,
641+
formula=f"y ~ 1 + x + I((x-{kink})*treated)",
642+
model=cp.pymc_models.LinearRegression(sample_kwargs=sample_kwargs),
643+
kink_point=kink,
644+
)
645+
assert isinstance(result, cp.RegressionKink)
646+
647+
648+
def test_its_deprecation_warning():
649+
"""Test that the old InterruptedTimeSeries class raises a deprecation warning."""
650+
651+
with pytest.warns(DeprecationWarning):
652+
df = (
653+
cp.load_data("its")
654+
.assign(date=lambda x: pd.to_datetime(x["date"]))
655+
.set_index("date")
656+
)
657+
treatment_time = pd.to_datetime("2017-01-01")
658+
result = cp.pymc_experiments.InterruptedTimeSeries(
659+
df,
660+
treatment_time,
661+
formula="y ~ 1 + t + C(month)",
662+
model=cp.pymc_models.LinearRegression(sample_kwargs=sample_kwargs),
663+
)
664+
assert isinstance(result, cp.InterruptedTimeSeries)
665+
666+
667+
def test_sc_deprecation_warning():
668+
"""Test that the old SyntheticControl class raises a deprecation warning."""
669+
670+
with pytest.warns(DeprecationWarning):
671+
df = cp.load_data("sc")
672+
treatment_time = 70
673+
result = cp.pymc_experiments.SyntheticControl(
674+
df,
675+
treatment_time,
676+
formula="actual ~ 0 + a + b + c + d + e + f + g",
677+
model=cp.pymc_models.WeightedSumFitter(sample_kwargs=sample_kwargs),
678+
)
679+
assert isinstance(result, cp.SyntheticControl)
680+
681+
682+
def test_ancova_deprecation_warning():
683+
"""Test that the old PrePostNEGD class raises a deprecation warning."""
684+
685+
with pytest.warns(DeprecationWarning):
686+
df = cp.load_data("anova1")
687+
result = cp.pymc_experiments.PrePostNEGD(
688+
df,
689+
formula="post ~ 1 + C(group) + pre",
690+
group_variable_name="group",
691+
pretreatment_variable_name="pre",
692+
model=cp.pymc_models.LinearRegression(sample_kwargs=sample_kwargs),
693+
)
694+
assert isinstance(result, cp.PrePostNEGD)
695+
696+
697+
def test_iv_deprecation_warning():
698+
"""Test that the old InstrumentalVariable class raises a deprecation warning."""
699+
700+
with pytest.warns(DeprecationWarning):
701+
df = cp.load_data("risk")
702+
instruments_formula = "risk ~ 1 + logmort0"
703+
formula = "loggdp ~ 1 + risk"
704+
instruments_data = df[["risk", "logmort0"]]
705+
data = df[["loggdp", "risk"]]
706+
result = cp.pymc_experiments.InstrumentalVariable(
707+
instruments_data=instruments_data,
708+
data=data,
709+
instruments_formula=instruments_formula,
710+
formula=formula,
711+
model=cp.pymc_models.InstrumentalVariableRegression(
712+
sample_kwargs=sample_kwargs
713+
),
714+
)
715+
assert isinstance(result, cp.InstrumentalVariable)

0 commit comments

Comments
 (0)