cleverly 0.1.0__py3-none-any.whl
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.
- cleverly/__init__.py +162 -0
- cleverly/_typing.py +121 -0
- cleverly/_version.py +5 -0
- cleverly/assessment.py +2324 -0
- cleverly/data/__init__.py +8 -0
- cleverly/data/causal_data.py +1249 -0
- cleverly/data/validate.py +453 -0
- cleverly/data/weighting.py +627 -0
- cleverly/datasets/__init__.py +97 -0
- cleverly/datasets/longitudinal.py +894 -0
- cleverly/datasets/synthetic.py +1708 -0
- cleverly/estimators/__init__.py +43 -0
- cleverly/estimators/_nuisance.py +1320 -0
- cleverly/estimators/base.py +1233 -0
- cleverly/estimators/ctmle.py +1461 -0
- cleverly/estimators/direct_effect.py +387 -0
- cleverly/estimators/drtmle.py +996 -0
- cleverly/estimators/reduced.py +964 -0
- cleverly/estimators/serialize.py +99 -0
- cleverly/estimators/targeting.py +2610 -0
- cleverly/estimators/tmle.py +2809 -0
- cleverly/exceptions.py +108 -0
- cleverly/fluctuation/__init__.py +74 -0
- cleverly/fluctuation/_score.py +72 -0
- cleverly/fluctuation/iterative.py +820 -0
- cleverly/fluctuation/mechanism.py +641 -0
- cleverly/fluctuation/one_step.py +176 -0
- cleverly/fluctuation/reduced.py +273 -0
- cleverly/fluctuation/submodel.py +1362 -0
- cleverly/inference/__init__.py +64 -0
- cleverly/inference/bootstrap.py +305 -0
- cleverly/inference/cluster.py +267 -0
- cleverly/inference/delta.py +173 -0
- cleverly/inference/influence.py +1449 -0
- cleverly/inference/multiplier.py +471 -0
- cleverly/inference/results.py +98 -0
- cleverly/interventions/__init__.py +49 -0
- cleverly/interventions/base.py +582 -0
- cleverly/interventions/incremental.py +564 -0
- cleverly/interventions/shift.py +558 -0
- cleverly/interventions/support.py +238 -0
- cleverly/learners/__init__.py +61 -0
- cleverly/learners/_fitting.py +256 -0
- cleverly/learners/_threads.py +322 -0
- cleverly/learners/crossfit.py +520 -0
- cleverly/learners/density.py +507 -0
- cleverly/learners/library.py +153 -0
- cleverly/learners/screeners.py +170 -0
- cleverly/learners/super_learner.py +631 -0
- cleverly/longitudinal/__init__.py +69 -0
- cleverly/longitudinal/data.py +1378 -0
- cleverly/longitudinal/estimator.py +1701 -0
- cleverly/longitudinal/msm.py +1069 -0
- cleverly/longitudinal/regimen.py +346 -0
- cleverly/longitudinal/sequential.py +1261 -0
- cleverly/methods.py +866 -0
- cleverly/msm.py +1029 -0
- cleverly/provenance.py +232 -0
- cleverly/py.typed +0 -0
- cleverly/sensitivity/__init__.py +39 -0
- cleverly/sensitivity/_parameters.py +188 -0
- cleverly/sensitivity/evalue.py +321 -0
- cleverly/sensitivity/missingness.py +392 -0
- cleverly/sensitivity/omitted_variable.py +883 -0
- cleverly/sensitivity/positivity.py +839 -0
- cleverly/study.py +2071 -0
- cleverly/targets/__init__.py +240 -0
- cleverly/targets/base.py +484 -0
- cleverly/targets/builtin.py +637 -0
- cleverly/utils/__init__.py +31 -0
- cleverly/utils/bounds.py +272 -0
- cleverly/utils/frames.py +195 -0
- cleverly/utils/parallel.py +52 -0
- cleverly/utils/phases.py +300 -0
- cleverly/utils/records.py +101 -0
- cleverly/utils/text.py +49 -0
- cleverly/validation/__init__.py +70 -0
- cleverly/validation/drtmle.py +840 -0
- cleverly/validation/nuisance.py +555 -0
- cleverly/validation/refute.py +1743 -0
- cleverly/validation/score.py +745 -0
- cleverly/validation/simulation.py +650 -0
- cleverly/variable_importance.py +240 -0
- cleverly-0.1.0.dist-info/METADATA +257 -0
- cleverly-0.1.0.dist-info/RECORD +87 -0
- cleverly-0.1.0.dist-info/WHEEL +4 -0
- cleverly-0.1.0.dist-info/licenses/LICENSE +21 -0
cleverly/__init__.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""cleverly: targeted maximum likelihood estimation for Python.
|
|
2
|
+
|
|
3
|
+
Quickstart
|
|
4
|
+
----------
|
|
5
|
+
>>> from cleverly import ATE, CausalStudy, PointTreatment
|
|
6
|
+
>>> from cleverly.datasets import make_nonlinear_ate
|
|
7
|
+
>>> from sklearn.linear_model import LinearRegression, LogisticRegression
|
|
8
|
+
>>> frame, _ = make_nonlinear_ate(n=200, seed=0)
|
|
9
|
+
>>> study = CausalStudy(
|
|
10
|
+
... frame,
|
|
11
|
+
... design=PointTreatment(
|
|
12
|
+
... outcome="Y",
|
|
13
|
+
... treatment="A",
|
|
14
|
+
... adjustment=["W1", "W2", "W3", "W4"],
|
|
15
|
+
... ),
|
|
16
|
+
... )
|
|
17
|
+
>>> result = study.identify(ATE()).estimate(
|
|
18
|
+
... outcome_learner=LinearRegression(),
|
|
19
|
+
... treatment_learner=LogisticRegression(max_iter=1000),
|
|
20
|
+
... n_folds=2,
|
|
21
|
+
... random_state=0,
|
|
22
|
+
... )
|
|
23
|
+
>>> sorted(result.estimates)
|
|
24
|
+
['ate']
|
|
25
|
+
|
|
26
|
+
The estimator takes pandas or polars dataframes interchangeably and returns results
|
|
27
|
+
in whichever backend it was given. Every result carries the fitted artifacts used by
|
|
28
|
+
its cache-only validation and diagnostic operations. Other assessments declare when
|
|
29
|
+
they retarget or refit.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
from ._version import __version__
|
|
35
|
+
from .assessment import (
|
|
36
|
+
AssessmentCapability,
|
|
37
|
+
AssessmentStatus,
|
|
38
|
+
DiagnosticReport,
|
|
39
|
+
Replayability,
|
|
40
|
+
ValidationReport,
|
|
41
|
+
)
|
|
42
|
+
from .estimators.serialize import load
|
|
43
|
+
from .exceptions import (
|
|
44
|
+
CapabilityError,
|
|
45
|
+
CleverlyError,
|
|
46
|
+
ConvergenceWarning,
|
|
47
|
+
DataError,
|
|
48
|
+
MethodConfigurationError,
|
|
49
|
+
NotFittedError,
|
|
50
|
+
PositivityWarning,
|
|
51
|
+
WeightingWarning,
|
|
52
|
+
)
|
|
53
|
+
from .inference import ParameterEstimate
|
|
54
|
+
from .learners import SuperLearner
|
|
55
|
+
from .methods import (
|
|
56
|
+
CollaborativeTMLEMethod,
|
|
57
|
+
CrossFitting,
|
|
58
|
+
DRTMLEMethod,
|
|
59
|
+
EstimationMethod,
|
|
60
|
+
Inference,
|
|
61
|
+
MethodAvailability,
|
|
62
|
+
ModelSpec,
|
|
63
|
+
Runtime,
|
|
64
|
+
Targeting,
|
|
65
|
+
TMLEMethod,
|
|
66
|
+
)
|
|
67
|
+
from .provenance import Provenance
|
|
68
|
+
from .study import (
|
|
69
|
+
ATC,
|
|
70
|
+
ATE,
|
|
71
|
+
ATT,
|
|
72
|
+
BackdoorMeanContrast,
|
|
73
|
+
CausalResult,
|
|
74
|
+
CausalStudy,
|
|
75
|
+
ControlledDirectEffect,
|
|
76
|
+
CounterfactualMean,
|
|
77
|
+
Estimand,
|
|
78
|
+
ExplicitAdjustmentProvider,
|
|
79
|
+
IdentificationProvider,
|
|
80
|
+
IdentifiedEffect,
|
|
81
|
+
IncrementalEffect,
|
|
82
|
+
IncrementalMean,
|
|
83
|
+
LongitudinalTreatment,
|
|
84
|
+
ModifiedTreatmentPolicy,
|
|
85
|
+
ModifiedTreatmentPolicyEffect,
|
|
86
|
+
MSMProjection,
|
|
87
|
+
NaturalCourseMean,
|
|
88
|
+
OddsRatio,
|
|
89
|
+
ParameterKey,
|
|
90
|
+
PointTreatment,
|
|
91
|
+
PopulationAttributableFraction,
|
|
92
|
+
PopulationAttributableRisk,
|
|
93
|
+
RegimeContrast,
|
|
94
|
+
RegimeMean,
|
|
95
|
+
RiskRatio,
|
|
96
|
+
)
|
|
97
|
+
from .variable_importance import (
|
|
98
|
+
VariableImportanceEntry,
|
|
99
|
+
VariableImportanceResult,
|
|
100
|
+
variable_importance,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
__all__ = [
|
|
104
|
+
"ATC",
|
|
105
|
+
"ATE",
|
|
106
|
+
"ATT",
|
|
107
|
+
"AssessmentCapability",
|
|
108
|
+
"AssessmentStatus",
|
|
109
|
+
"BackdoorMeanContrast",
|
|
110
|
+
"CapabilityError",
|
|
111
|
+
"CausalResult",
|
|
112
|
+
"CausalStudy",
|
|
113
|
+
"CleverlyError",
|
|
114
|
+
"CollaborativeTMLEMethod",
|
|
115
|
+
"ControlledDirectEffect",
|
|
116
|
+
"ConvergenceWarning",
|
|
117
|
+
"CounterfactualMean",
|
|
118
|
+
"CrossFitting",
|
|
119
|
+
"DRTMLEMethod",
|
|
120
|
+
"DataError",
|
|
121
|
+
"DiagnosticReport",
|
|
122
|
+
"Estimand",
|
|
123
|
+
"EstimationMethod",
|
|
124
|
+
"ExplicitAdjustmentProvider",
|
|
125
|
+
"IdentificationProvider",
|
|
126
|
+
"IdentifiedEffect",
|
|
127
|
+
"IncrementalEffect",
|
|
128
|
+
"IncrementalMean",
|
|
129
|
+
"Inference",
|
|
130
|
+
"LongitudinalTreatment",
|
|
131
|
+
"MSMProjection",
|
|
132
|
+
"MethodAvailability",
|
|
133
|
+
"MethodConfigurationError",
|
|
134
|
+
"ModelSpec",
|
|
135
|
+
"ModifiedTreatmentPolicy",
|
|
136
|
+
"ModifiedTreatmentPolicyEffect",
|
|
137
|
+
"NaturalCourseMean",
|
|
138
|
+
"NotFittedError",
|
|
139
|
+
"OddsRatio",
|
|
140
|
+
"ParameterEstimate",
|
|
141
|
+
"ParameterKey",
|
|
142
|
+
"PointTreatment",
|
|
143
|
+
"PopulationAttributableFraction",
|
|
144
|
+
"PopulationAttributableRisk",
|
|
145
|
+
"PositivityWarning",
|
|
146
|
+
"Provenance",
|
|
147
|
+
"RegimeContrast",
|
|
148
|
+
"RegimeMean",
|
|
149
|
+
"Replayability",
|
|
150
|
+
"RiskRatio",
|
|
151
|
+
"Runtime",
|
|
152
|
+
"SuperLearner",
|
|
153
|
+
"TMLEMethod",
|
|
154
|
+
"Targeting",
|
|
155
|
+
"ValidationReport",
|
|
156
|
+
"VariableImportanceEntry",
|
|
157
|
+
"VariableImportanceResult",
|
|
158
|
+
"WeightingWarning",
|
|
159
|
+
"__version__",
|
|
160
|
+
"load",
|
|
161
|
+
"variable_importance",
|
|
162
|
+
]
|
cleverly/_typing.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""Shared type aliases.
|
|
2
|
+
|
|
3
|
+
Kept in a private module so the public namespace stays focused on estimators.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any, Literal
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from numpy.typing import NDArray
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Backend",
|
|
15
|
+
"BoolArray",
|
|
16
|
+
"CumulativeGBounds",
|
|
17
|
+
"EstimandName",
|
|
18
|
+
"Family",
|
|
19
|
+
"FloatArray",
|
|
20
|
+
"FluctuationKind",
|
|
21
|
+
"FoldStrata",
|
|
22
|
+
"GBounds",
|
|
23
|
+
"IntArray",
|
|
24
|
+
"Learner",
|
|
25
|
+
"ParameterAxis",
|
|
26
|
+
"TargetingMethod",
|
|
27
|
+
"TargetingScheme",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
FloatArray = NDArray[np.float64]
|
|
31
|
+
IntArray = NDArray[np.int64]
|
|
32
|
+
BoolArray = NDArray[np.bool_]
|
|
33
|
+
|
|
34
|
+
#: Anything implementing the scikit-learn ``fit``/``predict`` (or
|
|
35
|
+
#: ``predict_proba``) protocol, including :class:`cleverly.SuperLearner`,
|
|
36
|
+
#: :class:`sklearn.pipeline.Pipeline`, and grid-search wrappers. Typed as
|
|
37
|
+
#: ``Any`` deliberately: scikit-learn estimators are structurally, not
|
|
38
|
+
#: nominally, typed and requiring a Protocol here would reject valid learners.
|
|
39
|
+
Learner = Any
|
|
40
|
+
|
|
41
|
+
Backend = Literal["pandas", "polars"]
|
|
42
|
+
Family = Literal["gaussian", "binomial", "auto"]
|
|
43
|
+
FluctuationKind = Literal["logistic", "linear"]
|
|
44
|
+
TargetingMethod = Literal["iterative", "one_step"]
|
|
45
|
+
TargetingScheme = Literal["pooled", "fold"]
|
|
46
|
+
|
|
47
|
+
#: What the outer cross-fitting folds are balanced on. ``"treatment"`` is the
|
|
48
|
+
#: long-standing behaviour and the default; ``"treatment+outcome"`` crosses in the
|
|
49
|
+
#: outcome so that a rare event cannot leave a fold with none of them.
|
|
50
|
+
FoldStrata = Literal["treatment", "treatment+outcome"]
|
|
51
|
+
#: Every estimand *name* :class:`~cleverly.estimators.TMLE`'s ``estimands=`` accepts, which is
|
|
52
|
+
#: every key of the target registry. Which of them a *particular* fit can report depends on
|
|
53
|
+
#: its outcome family, its arm count and which parameter axis it declared, and that is
|
|
54
|
+
#: checked at runtime by :func:`~cleverly.targets.resolve_estimands` against the registry
|
|
55
|
+
#: itself. This alias is the static half of the same statement and cannot be derived from
|
|
56
|
+
#: the registry: a ``Literal`` has to be written out, and importing ``cleverly.targets`` here
|
|
57
|
+
#: would invert the dependency of the type aliases on the package.
|
|
58
|
+
#:
|
|
59
|
+
#: So it is a hand-maintained copy, and the way a hand-maintained copy stays honest is a gate
|
|
60
|
+
#: rather than care -- ``tests/unit/test_registry.py`` compares its members with ``TARGETS``
|
|
61
|
+
#: in both directions. It had already drifted once without one: ``ey``, ``ey_obs``, ``par``
|
|
62
|
+
#: and ``paf`` were reportable and absent from here, as was every non-arm axis, so annotating
|
|
63
|
+
#: a correct call was a type error.
|
|
64
|
+
#:
|
|
65
|
+
#: Named ``EstimandName`` rather than ``Estimand`` because :class:`cleverly.Estimand` is a
|
|
66
|
+
#: different thing entirely -- the :class:`~typing.Protocol` in :mod:`cleverly.study` that a
|
|
67
|
+
#: question like :class:`~cleverly.ATE` satisfies. The two shared a name while meaning a
|
|
68
|
+
#: string and an object, and the public export won, so this one could not be reached under
|
|
69
|
+
#: its own name from outside the package.
|
|
70
|
+
#:
|
|
71
|
+
#: It covers the *built-in* registry only. A target registered at runtime -- see
|
|
72
|
+
#: ``tests/unit/test_registry.py``'s ``TestRegistration`` -- is a name no ``Literal`` written
|
|
73
|
+
#: here can know, so passing one is a legitimate ``cast``. That is a reason to keep this
|
|
74
|
+
#: narrow rather than to widen it to ``str``: the annotation exists to catch a misspelt
|
|
75
|
+
#: built-in name, and the registry still validates every name at runtime either way.
|
|
76
|
+
EstimandName = Literal[
|
|
77
|
+
"ate",
|
|
78
|
+
"att",
|
|
79
|
+
"atc",
|
|
80
|
+
"ey",
|
|
81
|
+
"ey1",
|
|
82
|
+
"ey0",
|
|
83
|
+
"ey_obs",
|
|
84
|
+
"par",
|
|
85
|
+
"paf",
|
|
86
|
+
"rr",
|
|
87
|
+
"or",
|
|
88
|
+
"ey_regime",
|
|
89
|
+
"ate_regime",
|
|
90
|
+
"ey_ipsi",
|
|
91
|
+
"ate_ipsi",
|
|
92
|
+
"ey_shift",
|
|
93
|
+
"ate_shift",
|
|
94
|
+
"msm",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
#: What a fit's parameters are indexed *by*: a treatment arm, a declared regime, a
|
|
98
|
+
#: declared shift, a declared tilt of the mechanism, or a coefficient of a declared
|
|
99
|
+
#: working model. The five partition the target registry -- see
|
|
100
|
+
#: :attr:`cleverly.Target.parameter_axis` for why they are exclusive rather than
|
|
101
|
+
#: cumulative.
|
|
102
|
+
#:
|
|
103
|
+
#: The first four also declare what "counterfactual" means for the fit. ``"msm"`` does
|
|
104
|
+
#: not: its counterfactuals are still the arms, and what moves is the *summary* the fit
|
|
105
|
+
#: reports of them. It is an axis all the same, because a summary's coefficients are not
|
|
106
|
+
#: indexed by anything the other four name.
|
|
107
|
+
#:
|
|
108
|
+
#: ``"ipsi"`` is the one whose intervention is a functional of the observed-data law: its
|
|
109
|
+
#: ``q_delta`` is built out of the estimated mechanism, so it carries an extra influence
|
|
110
|
+
#: curve term and a second score equation. That is why it is not a kind of ``"regime"``.
|
|
111
|
+
ParameterAxis = Literal["arm", "regime", "shift", "ipsi", "msm"]
|
|
112
|
+
|
|
113
|
+
#: Propensity-score truncation: ``"auto"`` for the sample-size dependent
|
|
114
|
+
#: default, a single float ``lo`` meaning ``[lo, 1 - lo]``, or an explicit pair.
|
|
115
|
+
GBounds = Literal["auto"] | float | tuple[float, float]
|
|
116
|
+
|
|
117
|
+
#: Bounds on an estimated cumulative longitudinal treatment-and-censoring
|
|
118
|
+
#: probability. There is deliberately no ``"auto"`` member: cleverly has no
|
|
119
|
+
#: data-adaptive or depth-adaptive rule for choosing this bound. LTMLE's package
|
|
120
|
+
#: default is an explicit fixed pair, recorded in :mod:`cleverly.utils.bounds`.
|
|
121
|
+
CumulativeGBounds = float | tuple[float, float]
|