cleverly.sensitivity.truncation_curve#

cleverly.sensitivity.truncation_curve(result: TMLEResult, bounds: Any = None, *, estimands: Any = None, mechanism: bool | None = None) → Any[source]#

Re-estimate across a grid of truncation bounds.

Returns a tidy frame with one row per evaluated bound pair and estimand, giving the point estimate and confidence interval. A fit whose inference status supplies no inference, such as a selector-path collaborative fit, has no interval, so its frame carries plugin_std_err, plugin_interval_lower and plugin_interval_upper in place of std_err, ci_lower and ci_upper. A scalar bound supplied by the caller remains shorthand for the symmetric treatment-mechanism pair (bound, 1 - bound); an observation- or intermediate-mechanism sweep uses (bound, 1). On an ordinary, collaborative, or unguarded doubly-robust fit only the targeting step is re-run – the nuisance fits are cached – so each bound costs a small fraction of the original fit. A CTMLE(strategy="derivative") fit also re-runs its derivative update at each bound, because both of its mechanisms are bounded there.

A guarded DR-TMLE fit is the exception, and it is why the combined report prices this operation per result rather than per operation. Its targeting step alternates against the reduced-dimension regressions, so every bound refits them. The ordinary closure refits them at the fitted reduced bounds; the missing-outcome construction is handed the swept bounds instead, because they define two of its regression targets. The primary outcome regression and the propensity stay cached, so one bound costs less than a fit – but the default grid has several of them, so the whole curve can cost more than the fit it describes. Budget it as refitting work.

A curve that is flat over the plausible range of bounds says the estimate does not hinge on the truncation choice. A curve that drifts monotonically says the estimate is sensitive to this finite-sample regularisation. It does not change the requested estimand. The bound should be reported with the estimate.

Parameters:
resultTMLEResult

A fitted result.

boundssequence of float or None

Lower truncation values to try. None uses a grid from 0.001 to 0.2 and adds every exact bound pair the requested parameters used. An explicit sequence is evaluated exactly as requested and is never expanded with fitted points. On a declared known treatment mechanism, a g(W) pair that moves a declared value is one the fit refuses: an explicit one is refused, and the default grid omits it.

estimandssequence of str or None

Restrict the emitted rows to these parameters. None uses every parameter the fit reported. Two forms are accepted. A reported parameter selects its own row, so "ey[high]" gives one row per bound pair. A registered target name selects every reported alias of that target, in report order, so "ey" on a three-armed fit gives three rows per bound pair and on a two-armed fit gives one. A name that is neither is refused, because it has no fitted pair and no fitted estimate for a row to reference, and an empty selection is refused because it names no row.

mechanismbool or None

Sweep the bound on P(Delta = 1 | A, W) (and the intermediate density) instead of the one on g(W). None resolves to the only axis the fit has: the observation mechanism on a natural-course fit, which estimates no treatment law, and g(W) on every other fit. That probability divides the clever covariate exactly as the propensity does, so it has a truncation curve for exactly the same reason – and it is the one that goes unexamined, because it has no familiar name. Requires a fit with delta= or intermediate=. An incremental fit refuses the g(W) axis, including the default, because its estimand contains g. truncation_refusal states every refusal, and the capability row reads it too.

Note what the curve does and does not show. Truncating a mechanism cannot move the estimand: the plug-in is an average of targeted predictions and contains no mechanism at all. What moves is the second-order remainder, so a curve that drifts is saying the estimate is leaning on rows the bound is holding up.

Returns:
dataframe

One row per evaluated bound pair and estimand, with the point estimate, interval, fitted pair and estimate, and signed movement from that fitted estimate.

Raises:
CapabilityError

When the requested axis is refused, as truncation_refusal() states, or when an explicit g(W) bound moves a value of a declared known mechanism.