Repository navigation
Releases: esbraun/cleverly-tmle
Release list
cleverly 0.1.4
This alpha release labels each estimate as supported or experimental. It adds vector treatments
with a continuous component, repeated-outcome curves, hazard-based survival curves, and
sequential baselines. It also adds three C-TMLE paths and refusal reason codes. These are the
changes since 0.1.3.
Upgrade notes
Review these changes before refitting an analysis under 0.1.4.
Install scikit-learn 1.8 or later. The package now requires scikit-learn>=1.8 instead of
>=1.3. The L1 logistic path of CTMLE(strategy="lasso") needs the newer solver.
(#308)
Read estimate.support before you cite an interval, a band, or a derived estimate. Each
estimate now carries the label supported or experimental, with a reason, evidence ids, and
limits. No argument requests or removes the label.
(#344)
Replace BootstrapSummary.inferential with BootstrapSummary.diagnostic, which has the opposite
sense. BootstrapSummary also gains below_floor and support.
(#344)
Stop treating CapabilityError as the signal for these cases. They now return labelled
results, so read estimate.support or result.inference_status instead.
| case | 0.1.3 | 0.1.4 |
|---|---|---|
weights_estimated=True (estimated_weight_plugin) |
interval refused | plug-in interval, experimental |
3 to 9 clusters (3 to 19 for LTMLE) |
interval refused | Student t interval on J - 2 degrees of freedom, status few_cluster_plugin, experimental |
| fewer than 3 clusters | interval refused | point estimate and plugin_std_error, status no_cluster_variance_plugin |
| missing-outcome compositions listed under "Experimental compositions" in the scope and refusals reference | CapabilityError |
experimental label, old refusal text as the reason |
(#344)
Expect a full-refit bootstrap to keep its inferential columns with the experimental label in
more cases. These cases are clustered fits with 10 or more clusters, cross-fitted point fits,
competing-event fits, and DRTMLE fits. A CTMLE bootstrap and a bootstrap below the cluster
floor stay diagnostic.
(#344)
Update exception handling that catches TypeError from a refused longitudinal call. These calls
now raise a CleverlyError subclass, which is a ValueError.
SDRandIPSIwithalpha=,max_iter=, ortol=raiseMethodConfigurationError.IPSI.fit()on point-treatment data raisesMethodConfigurationError.- A point-treatment keyword that
LTMLErefuses by name raisesCapabilityError. TimeToEvent(time_varying=...)raisesCapabilityErrorwith the codeapi_redirection.
An unknown keyword still raises TypeError.
(#303)
Replace reason= with refusal= where code builds a capability row, such as
MethodAvailability or AssessmentItem. The argument takes a Refusal from
cleverly.exceptions. The reason property still returns the message text.
(#303)
Update code that reads the refusal constants in cleverly.methods. SDR_POINT_REFUSAL,
SDR_TILT_REFUSAL, LTMLE_TILT_REFUSAL, IPSI_NO_FLUCTUATION, IPSI_POINT_REFUSAL, and
REPEATED_OUTCOME_REFUSAL are now Refusal records. Replace SDR_NO_FLUCTUATION with
sdr_no_fluctuation_refusal(name).
(#303)
Replace cleverly.estimators.ctmle.OAT_PER_ARM_INFERENTIAL and per_arm_design_admits() with
per_arm_parametric_support(estimator, data). The function returns a SupportVerdict that
names the first unmet requirement.
(#314)
Remove from a truncation_curve() grid each pair that truncates a declared known mechanism.
Such a pair now raises CapabilityError with the code invalid_declaration. The default
point-treatment grid omits these pairs.
(#311)
Check code that reads LongitudinalDiagnostics.rows. A node with no fluctuation now reports
converged=None and epsilon=(). Earlier versions reported converged=True for SDR and IPSI
nodes.
(#318)
Refit analyses when you move saved results to 0.1.4. Loading another version's result emits
VersionMismatchWarning. The loader does not migrate the result, and the load can fail.
Added
Support label
Each estimate carries estimate.support. The label is supported only when a registered
support row covers the fit configuration and the row's study cells passed. Otherwise the label
is experimental. The label makes no coverage claim of its own.
(#344)
| surface | where the label appears |
|---|---|
repr() |
[experimental] |
summary() and to_frame() |
a support column |
to_dict() |
support, support_reason, support_evidence, and support_limits |
| simultaneous band | result.simultaneous.support |
result.config.support_key |
the key that classified the fit |
A ratio, contrast, or other derived estimate is supported only when its row lists the
derived kind and each input is supported. result.save() and cleverly.load() keep the
labels. The user guide section "Read the support label" describes each surface.
(#344)
Registered support rows now cover the IPSI studies, the vector-mtp study, and derived
estimates on in-sample binary TMLE. The IPSI competing-risk row is supported with a stated
limit: its standard error is conservative.
(#345,
#346,
#347)
Refusal reason codes
Each CleverlyError carries error.refusal, a Refusal record with a RefusalReason code.
The codes are invalid_declaration, different_estimand, implementation_gap,
inference_gap, and api_redirection. cleverly.exceptions exports Refusal,
RefusalReason, and NO_ALTERNATIVE.
(#303)
Capability rows carry the same record before a fit. These rows are MethodAvailability,
result.diagnostics.capabilities, and AssessmentItem. The row and the raised error state
one reason. A built-in ValueError or TypeError still means a malformed argument.
(#303)
Vector treatments with a continuous component
cleverly.interventions.JointPolicy declares one policy for each component of a vector
treatment. Pass treatment=[...] with continuous_treatment=[...] to CausalData or
PointTreatment. The longitudinal data classes take the same list form.
(#335)
- Point
TMLE(policies=[JointPolicy(...)])reportsey_policyandate_policywith
ratio="density"orratio="classifier". LTMLEandSDRfit a node with categorical and continuous columns through aJointPolicy
in each regimen. Censoring, survival, competing risks, weights, clusters, cross-fitting, and
msm=are available.- A vector of categorical columns only is one categorical treatment over the observed tuples,
with at most 20 levels. - A vector with a dose has no arms.
DRTMLE,CTMLE, a known treatment mechanism, and
survival_model="hazard"refuse it with a stated reason.
Evidence: the vector-mtp study passes 20 of 20 truth tests and 36 of 36 property cells at
1,000 replications of n = 2,000. The R lmtp comparator runs are pending.
(#335)
Repeated-outcome curves
Repeated declares an outcome that is measured at each node and can return to zero. Use
outcome=Repeated(["Y1", "Y2", "Y3"]) in LongitudinalTreatment. LTMLE then reports the
mean curve ey_regimen[d @ t=h] and its contrasts at each horizon h.
(#305,
#306)
- Each horizon is the end-of-study fit of the panel cut after h.
- Only
curve(scale="mean")is available. Risk, survival,rmst(), andrmtl()are refused. - Encode a lost unit as
C_t = 0. A missingY_ton an uncensored row is refused. SDR,IPSI, andLongitudinalData.from_long_panel()refuse aRepeatedoutcome.cleverly.datasets.make_longitudinal_repeatedreturns a frame and its exact truth.
Evidence: 13 of 13 truth tests and 97 of 97 property cells pass at 2,400 replications of
n = 3,000 on one three-node law. The R ltmle comparison is pending. The full-refit bootstrap
is diagnostic for this outcome.
(#305)
Hazard-based survival curves and isotonic projection
LTMLE accepts survival_model="hazard", targeting="one_step", step_size=, and
isotonic=. The hazard model targets cause-specific discrete hazards for a survival or
competing-risk curve. It needs one categorical baseline treatment and no covariate after
baseline.
(#310)
targeting="one_step"targets one set of hazards for all horizons (Cai and van der Laan
2020, Algorithm 2).isotonic=Trueprojects each curve and its band limits onto a non-decreasing curve.
result.isotonic_moveslists the moved levels.- A pointwise interval at a moved level is valid only where the true curve strictly increases.
- Other designs refuse the hazard model before any learner and name
survival_model="iterated_mean"withisotonic=Trueas the route.
Evidence: three Python property studies pass every truth test and cell. No registered s...
cleverly 0.1.3
This alpha release adds sequentially doubly robust estimation, longitudinal incremental interventions,
and long-panel input. It extends outcome-adaptive C-TMLE point estimates and narrows several
inference claims. These are the changes since 0.1.2.
Upgrade notes
Review these changes before refitting an analysis under 0.1.3.
Set oat_design="shared" to retain the previous CTMLE(strategy="oat") construction.
The default is now "per_arm", which fits a separate treatment mechanism on each arm's outcome
prediction. CollaborativeTMLEMethod accepts the same option. Both designs withhold inferential
standard errors, confidence intervals, and p-values.
(#279)
Treat full-refit bootstrap output as diagnostic for DRTMLE, every CTMLE strategy, and
cross-fitted point-treatment TMLE. Their summaries now report bootstrap sd and a
percentile range. Their to_frame() output uses bootstrap_sd, bootstrap_range_lower,
and bootstrap_range_upper instead of the inferential bootstrap columns.
(#298)
Reassess DR-TMLE intervals that relied on consistency of only one nuisance estimator.
The corrected contract does not establish interval validity from that condition alone.
The fit retains its influence_curve status because it cannot determine which nuisance
conditions hold. Point-estimate double robustness remains unchanged.
(#278,
#291)
Record survival events before censoring. For an event at a node, use censoring 1 and event 1.
For censoring after an event-free interval, use censoring 0 and a missing event.
LongitudinalData.from_frame() now refuses event values at or after censoring on an event-free trajectory.
It raises DataError where earlier versions discarded those values.
(#297)
Update simulation error handling if it relied on every exception becoming a failed replication.
CoverageStudy now propagates programming errors, unsupported configurations, and errors while
drawing a sample. It retains supported numerical and fitting failures as ReplicationFailure
records.
(#285)
Refit analyses when migrating saved results to 0.1.3. Loading another package version's result
emits VersionMismatchWarning; the loader does not migrate the result and loading may fail.
For results containing objects saved by value, use the same Python minor version that saved them.
(#297)
Added
Longitudinal estimators
cleverly.longitudinal.SDR and CausalStudy(...).estimate(method="sdr") add sequentially
doubly robust estimation. Consistency requires, at each node, a consistent mechanism ratio or
a consistent regression of the transformed outcome. This consistency property alone does not
establish valid confidence intervals; inference requires additional rate and regularity conditions.
(#284)
SDR supports cross-fitting through a separate recursion within each training fold.
Its registered evidence covers two decision nodes and reports finite-sample coverage shortfalls
in two declared cells.
(#284,
#293)
cleverly.longitudinal.IPSI estimates incremental interventions on binary treatments over time.
Use IncrementalEffect with method="ipsi" through CausalStudy.
Each Incremental(delta) multiplies treatment odds at the declared nodes.
The estimator supports dropout, survival and competing-risk horizons, and contrasts between plans.
(#286)
IPSI requires no treatment positivity at a tilt node, but it requires a consistent treatment
mechanism there. It is not doubly robust. Dropout still requires observation positivity, and
fixed-arm nodes retain their positivity requirements. The registered longitudinal incremental
studies have not yet run.
(#286)
SDR and IPSI report computed estimates even when they leave the outcome range.
Such estimates emit ParameterSpaceWarning; derived ratios refuse an undefined scale.
(#284,
#286)
Long-panel data and reusable rules
LongitudinalData.from_long_panel() accepts one row per unit and decision time.
LongPanel supplies the matching CausalStudy design, and to_long_panel() exports an adapted
container. The reader checks temporal ordering, duplicate rows, gaps, late entry, censoring,
and unit-level declarations before fitting learners.
(#297)
each_node(rule, n_times) creates one dynamic rule per decision node.
Each rule receives its node index and the history available at that node.
(#297)
Outcome-adaptive C-TMLE
The per-arm outcome-adaptive design accepts baseline strata, missing outcomes, and in-sample
missing treatments. With missing outcomes, it also supports natural-course means and
population attributable risk and fraction. Missing-treatment fits still refuse those
natural-course targets and cross-fitting.
(#283)
These fits report point estimates and plug-in diagnostics under generated_design_plugin.
Their registered studies identify standard-error deficits, so neither per-arm nor shared
outcome-adaptive fits supply inferential intervals.
(#279,
#280,
#294)
Resumable simulation
CoverageStudy(checkpoint=DirectoryCheckpoint(...)) saves each completed replication and
resumes unfinished work. ReplicationCheckpoint defines the interface for other storage.
Use a separate checkpoint directory for each study configuration: stored seeds are checked,
but the data-generating process and estimator configuration are not.
(#285)
Changed
Inference documentation
The DR-TMLE reference now distinguishes conditions that establish an interval from cases where
a first-order remainder remains. A new witness study measures these cases, and the tutorial
uses a declared treatment mechanism. The reference also explains the observed bias with a
gradient-boosting mechanism and an incorrect outcome regression.
(#278,
#282,
#291)
Among point-treatment fits, only in-sample ordinary TMLE retains an inferential full-refit
bootstrap interval. Its registered evidence uses correctly specified nuisances.
The documentation identifies the remaining evidence gap for data-adaptive learners.
(#298)
Fixed
Saved inline policies
result.save() now preserves lambdas, closures, and objects defined in notebooks or __main__
by value through cloudpickle. Loaded results can evaluate these rules, policy densities,
and maps in a new interpreter. Importable functions and classes still require their modules
on the loading machine.
(#297)
Compatibility
cleverly supports Python 3.11, 3.12, and 3.13 with pandas or Polars dataframes.
cloudpickle>=3.0 is now a core dependency.
(#297)
This remains an alpha release. The package does not promise compatibility for public APIs,
CausalStudy objects, pickled objects, or saved results. Follow the upgrade notes when moving
an analysis to 0.1.3.
Full changelog: v0.1.2...v0.1.3
cleverly 0.1.2
This alpha release extends missing-data, treatment-policy, and longitudinal analyses, with APIs
for known treatment and censoring mechanisms. It also changes cross-fitting defaults and makes
inference restrictions explicit. These are the changes since 0.1.1.
Upgrade notes
Review these API changes before refitting an analysis under 0.1.2.
Replace shifts= with policies= in TMLE, ModifiedTreatmentPolicy, and
ModifiedTreatmentPolicyEffect. Replace ey_shift and ate_shift with ey_policy and
ate_policy. ShiftSet, ShiftSupport, and check_shift_support become PolicySet,
PolicySupport, and check_policy_support. Import Shift from cleverly.interventions.
(#269)
Cross-fitting now draws unstratified outer folds by default. Treatment-based and outcome-based
fold strata are refused. For continuous outcomes, declare known outcome bounds with
Targeting(q_bounds=(lower, upper)). Do not calculate these bounds from the observed sample.
Use CrossFitting(enabled=False) for an in-sample fit.
(#218)
Declare supplied rules, intervention densities, MSM designs, and projection weights as known
when they are fixed independently of the analysis sample. The relevant arguments are
rule_kind, density_kind, design_kind, and weights_kind. Undeclared or estimated
functions are refused on these paths.
(#224,
#225,
#227,
#228)
Check each estimate's supplies_inference before reading uncertainty estimates. C-TMLE selection
paths and some estimated-weight DR-TMLE fits withhold standard errors, confidence intervals, and
p-values. Their plugin_std_error and plugin_interval remain available as diagnostics.
(#223,
#224)
Move DRTMLEMethod(treatment_probabilities=...) declarations to
PointTreatment(treatment_probabilities=...). Study designs accept mappings from treatment
levels to probability columns. Pass arrays through the estimator's fit method instead.
(#276)
Replace diagnostics.stagewise() with diagnostics.support() and
cv_targeting.fold_evaluated with cv_targeting.canonical. The loader does not migrate saved results.
Loading a result from another version emits VersionMismatchWarning and may fail. Refit analyses
under 0.1.2 when migrating saved results.
(#236)
Added
Study protocols and reusable splits
Studies can record their scientific protocol and reuse the outer folds from a previous fit.
StudyProtocol records the population, eligibility, treatment strategies, outcome, follow-up,
and assumptions. Identification retains the record, and fitted provenance carries its digest.
(#201)
SplitPlan records assignments across cross-fitting repeats. Pass result.split_plan through
CrossFitting(split_plan=...) to reuse them. Fits require package-generated assignments and
validate the plan before fitting learners.
(#202,
#218)
Missing data and baseline strata
Additional estimands support missing outcomes, missing treatments, and estimates within baseline
strata.
NaturalCourseMean estimates the population outcome mean with outcomes missing at random.
Ordinary TMLE and the supported stacked cross-fitted configuration are available.
(#207,
#210)
Stacked CV-TMLE supports arm means and contrasts with missing outcomes, subject to its fold,
scale, and composition restrictions.
(#216)
Missing-outcome DR-TMLE supports randomized treatments with three or more arms. Known
treatment probabilities can be supplied by arm.
(#260)
TMLE and DR-TMLE support a declared missing treatment, with or without missing outcomes,
through a composite observation indicator. Use treatment_delta= on the estimator or
treatment_missingness= on PointTreatment. Cross-fitting remains unsupported for this
construction. (#261)
Ordinary TMLE estimates population attributable risk and fraction with outcomes missing at
random. The stacked cross-fitted route requires a binary outcome and excludes weights and
clusters. (#262)
Baseline-stratum targeting extends to incremental interventions, MSMs, natural-course means,
and DR-TMLE. Fold-local targeting and fold evaluation remain unsupported with strata.
(#263)
Known treatment and censoring mechanisms
Fits can use treatment probabilities declared by a randomized design instead of estimating them.
TMLE.fit(treatment_probabilities=...) and PointTreatment(treatment_probabilities=...)
accept known treatment probabilities. TMLE and DR-TMLE use the declaration without fitting a
treatment learner. (#276)
LTMLE accepts known treatment and censoring probabilities at individual nodes, including
survival and time-to-event designs. Declare them with treatment_probabilities= and
censoring_probabilities=; nodes without declarations still estimate their mechanisms. The fit
refuses a regimen that requires an event with declared probability zero. (#276)
Known treatment probabilities replace only the treatment mechanism. Missing-outcome and
controlled-direct-effect fits still estimate their additional mechanisms and retain their
inference conditions. (#276)
Treatment policies and learned rules
Policy estimation now covers additional dose transformations and categorical policies.
Modified treatment policies include scaling, piecewise transformations, custom invertible
maps, categorical maps, and randomizers with known probabilities. They run through
point-treatment TMLE and supported longitudinal nodes.
(#269)
ratio="classifier" estimates a policy density ratio by classification. RiskRatioTilt
provides a separate binary-treatment policy through the estimator API.
(#269)
LearnedRuleValue estimates the average value of binary treatment rules learned separately
within the training folds. It requires cross-fitting and fold evaluation. Its target is the
fold-average learned-rule value, rather than the value of an optimal rule or one fitted on all
rows. (#248,
#249)
Longitudinal regimen nodes accept known stochastic categorical policies through
Stochastic(..., density_kind="known").
(#268)
Longitudinal and survival analyses
Longitudinal estimation adds baseline-treatment survival input, clustered cross-fitting, and
derived summaries.
TimeToEvent and LongitudinalData.from_time_to_event() accept one follow-up time and event
code per unit. They support a baseline treatment held over the survival nodes. Censoring
between declared grid times is refused.
(#270)
Clustered LTMLE cross-fits with whole clusters assigned to folds. Longitudinal MSMs also
cross-fit, with one pooled targeting update per node. LTMLE(msm=...) now runs with the
default ten folds. (#265,
#266)
ratio() derives risk ratios or odds ratios from fitted parameters. wald_test(null=...)
tests a specified null, and Transform supports transformed confidence intervals.
(#258)
rmst() and rmtl() report restricted mean survival time and time lost, including
between-regimen contrasts. Gridded time-to-event fits use grid times.
(#258,
#270)
LTMLE accepts n_bootstrap= for full refits of resampled trajectories or clusters. Unsupported
inference settings report bootstrap spreads as diagnostics.
(#258)
Diagnostics and sensitivity analyses
Reports expose more of the fitted mechanisms and distinguish deferred work from unavailable
analyses.
Support reports include combined mechanism denominators and the concentration of fitted score
weights. These are descriptive measures, without a pass threshold.
(#191,
#192,
#193)
Nuisance reports distinguish collaborative working mechanisms and show variation across
repeated splits. Longitudinal reports cover treatment, censoring, outcome, and pseudo-outcome
fits. (#194,
#195)
Longitudinal truncation curves refit the outcome recursion at each requested mechanism bound.
They report estimate movement and score-cell counts, without selecting a preferred bound.
(#204)
assess() adds...
cleverly 0.1.1
Changes since 0.1.0.
Added
result.assess()
result.assess() runs validation, diagnostics, and sensitivity analyses on a fitted result and
returns one AssessmentReport. It is available on point-treatment and longitudinal results.
battery = result.assess()
print(battery.summary())
support = battery.report("support")- Each row holds the returned report, the effective arguments, a status, and an omission reason.
- Statuses are
passed,failed,completed,warning,unavailable, andnot_applicable. battery.attentionlists failures and warnings.battery.omissionslists rows that were
unavailable or not applicable.- A refusal from one operation becomes an
unavailablerow, and the remaining operations still
run. A direct call to the operation still raises the refusal. - The default call reads stored fit artifacts and does not refit nuisance models. Pass
include_refits=Trueforrefuteandbenchmark. Passinclude_retargets=Truefor
truncation_curve,missingness, andtipping_gamma. - Pass per-operation arguments with
arguments={"operation": {...}}. random_stateseedsrefute,benchmark, andsimulated_confounding.- Saved results keep their cached reports and refusals.
simulated_confounding
cleverly.sensitivity.simulated_confounding refits a selected parameter across a grid of
simulated common-cause strengths, declared with ConfounderStrengthGrid. It reports the movement
of the estimate in each cell. It returns no verdict and no adjusted inference.
Supported targets:
- ATE, ATT, ATC, and counterfactual means for binary treatments
- risk ratios and odds ratios
- population attributable risk and fraction (ordinary TMLE only)
- continuous modified-policy means and contrasts
- fixed regime means and contrasts (binary ordinary TMLE)
- incremental propensity score means and contrasts (ordinary TMLE)
- MSM coefficients with identity, log, or logit links (ordinary TMLE)
Fits with fixed probability weights, baseline strata, and repeated cross-fitting are supported
where the fitted estimator supports them. Fits with estimated weights or clusters are refused.
Changed
- An eligible ordinary-TMLE fit computes its E-value by default in
assess()without a refit. - E-values work on results from the raw API, including multi-arm fits.
- A risk-difference E-value conversion refuses a reference-arm mean that is not separated from
zero. A converted lower bound is truncated at zero, andEValue.truncated_boundrecords the
original value. diagnostics.corrections()is available only when the fitted DR-TMLE method used a correction
term.diagnostics.capability("refute")reports unavailable on a result that cannot replay its fit.
Fixed
- The cluster bootstrap gave repeated draws of one source cluster the same cluster code, which
merged them. Each draw now has its own code. Bootstrap standard errors and intervals on
clustered data change. - The Gaussian E-value approximation divided a weighted estimate by an unweighted standard
deviation. It now uses the weighted deviation. - A result derived with
dataclasses.replacereused the cached assessment reports of the
original result. isinstance(result, CausalResult)raised on a multi-parameter result under Python 3.11.
Compatibility
Python 3.11, 3.12, and 3.13. pandas or Polars dataframes.
Full changelog: v0.1.0...v0.1.1