cleverly.fluctuation.Fluctuation#

class cleverly.fluctuation.Fluctuation(epsilon: ndarray[tuple[Any, ...], dtype[float64]], targeted: InitialFit, score: ndarray[tuple[Any, ...], dtype[float64]], converged: bool, n_iter: int, trace: tuple[float, ...], method: Literal['iterative', 'one_step', 'linear'], names: tuple[str, ...], carried: tuple[InitialFit, ...] = (), score_scale: ndarray[tuple[Any, ...], dtype[float64]] | None = None, folds: tuple[FoldFluctuation, ...] = (), score_initial: ndarray[tuple[Any, ...], dtype[float64]] | None = None, n_solver_calls: int = 1, failure: Literal['singular_hessian', 'line_search_exhausted', 'nonfinite_step', 'separation_suspected', 'bounds_pinned', 'max_iter_reached'] | None = None, hessian_condition: float = nan, epsilon_std_error: ndarray[tuple[Any, ...], dtype[float64]] | None = None, loglik: float = nan, mechanism: Any | None = None, projection: Any | None = None, reduction: Any | None = None, absolute_score_weights: ndarray[tuple[Any, ...], dtype[float64]] | None = None, stratified: Fluctuation | None = None, stopping: str = 'relative', thresholds: ndarray[tuple[Any, ...], dtype[float64]] | None = None, rule_ratio: float | None = None, best_iterate: int | None = None)[source]#

The result of a targeting step.

Parameters:
epsilonndarray

Fitted fluctuation coefficients, one per clever-covariate column.

targetedInitialFit

Targeted predictions at the observed treatment, and at A = 1 / A = 0.

scorendarray

Mean of w * h * (Y - Q*) per column – the estimating equation the targeting step is meant to zero out. Reported rather than asserted so cleverly.validation.score can check it against the standard error.

convergedbool

Whether the relative score norm reached tol.

n_iterint

Outer iterations of the solver: Newton steps for "iterative", walk steps for "one_step", 1 for "linear".

tracetuple of float

Relative score norm through one solve. trace[0] is always the score at epsilon = 0, whichever solver ran. Empty when this object aggregates several independent fold solves; their complete trajectories are in folds.

method{“iterative”, “one_step”, “linear”}

Which solver produced this step.

namestuple of str

Name of each clever-covariate column, in the order epsilon reports them.

carriedtuple of InitialFit

Further initial fits moved along the same submodel by the same steps, in the order they were passed as carry. Empty on every fit that did not ask for one, which is every fit but a DRTMLE with reduced_crossfit="nested".

It exists because a step sequence cannot be reconstructed from its endpoint. The solve applies apply_logistic() once per Newton step and shrinks after each, so a caller holding (initial, epsilon) recovers targeted only when no intermediate iterate touched the shrinkage bound – and the fits that reach it are exactly the weak-overlap ones a reference construction is compared on. Carrying the arrays through the solver is the only way to move them by the transformation that was actually applied rather than by one that usually equals it.

score_scalendarray or None

Per-column mean(|w * h|), the largest the score could possibly be given that the residual is bounded by one on the [0, 1] outcome scale. Dividing by it turns the score into a dimensionless quantity, which is what makes a single default tolerance meaningful across problems whose clever covariates differ by orders of magnitude.

foldstuple of FoldFluctuation

Per-fold detail, populated only by the cross-validated (targeting_scheme= "fold") targeting step and empty otherwise.

score_initialndarray or None

The score before targeting. Reported so the reader can see how far the step actually moved: a score that started near zero means the initial fit already solved the equation and targeting had nothing to do, which is a different situation from one that started large and was driven down.

n_solver_callsint

How many times a solver was invoked – 1 for a pooled fit, and one per fold for a fold-targeted one, where n_iter is the sum across folds and on its own would be indistinguishable from a single long solve.

failurestr or None

Why the step stopped, when it did not converge. None when it converged.

hessian_conditionfloat

Condition number of the last Newton Hessian. Large means the clever covariate columns are nearly collinear and epsilon is barely identified.

epsilon_std_errorndarray or None

Standard errors of the fluctuation coefficients from the inverse Hessian. A diagnostic only: the parameter’s inference comes from the influence curve, not from epsilon.

loglikfloat

Quasi-log-likelihood at the fitted epsilon. The submodel is fit by maximising it, so it must not decrease along the path.

mechanismobject or None

The treatment-mechanism half of the targeting, for a group whose parameter is defined through the mechanism.

projectionobject or None

The working model’s coefficients for an msm group whose link makes the clever covariate depend on them.

reductionobject or None

Equation (10)’s fluctuation and the reduced regressions it was solved against, for a DRTMLE fit.

absolute_score_weightsndarray or None

abs(w_i * h_ij) on the rows this outcome-score solve used. None only on a fluctuation built by hand.

stratifiedFluctuation or None

The stratum fluctuation of an msm group with baseline strata whose link makes the clever covariate depend on the coefficients. It solves one score block per stratum at that stratum’s coefficients, and the stratum estimates read it. This fluctuation then holds the marginal solve. None for every other fit.

stoppingstr

The rule that judged converged: "relative" (the shipped test against tol), "scaled_var" or "sample_size".

thresholdsndarray or None

Under a declared rule, the threshold of each score column at the reported fit, in the outcome’s units; the rule is met when |score| * span is at most the threshold in every column. None under "relative".

rule_ratiofloat or None

Under a declared rule, max_j |P_n S_j| * span / threshold_j at the reported fit (rule_ratio_at()); the rule is met when it is at most one. The outer exits of the alternating engines read it. A ratio is valid only at the pair it was computed at, so an engine that moves the fit after a solve restates it. None under "relative".

best_iterateint or None

Under iterate="best", the index of the iterate the fit reports (0 is the initial fit, taken only when the walk took no step). n_iter and trace describe the whole walk. None under iterate="last".

Attributes:
score_normfloat

Largest absolute score component, in the outcome’s own units.

relative_score_normfloat

Largest score component relative to its maximum possible magnitude.

initial_score_normfloat

Largest absolute score component before targeting.

See also

cleverly.fluctuation.FoldFluctuation

One validation fold’s own solve.

cleverly.estimators.TMLEResult

The fit these are read out of.

Methods#

Fluctuation.coefficients() → dict[str, float][source]#

Return the fitted epsilon, keyed by clever-covariate column name.

Returns:
dict of str to float

One coefficient for each column named in names, in that order.

Fluctuation.describe_failure() → str[source]#

One sentence naming the failure and what usually causes it.

Returns:
str

"converged" when the step converged, and the explanation otherwise.

Attributes#

Fluctuation.absolute_score_weights: ndarray[tuple[Any, ...], dtype[float64]] | None = None#

Absolute per-row score weights, abs(w_i * h_ij), on exactly the rows used by this outcome-score solve. Stored because stratified, fold-evaluated, fold-targeted, linked-MSM, and doubly-robust fits can use a covariate or scoring measure that cannot be reconstructed from result.nuisance. None only on a fluctuation built by hand.

Fluctuation.best_iterate: int | None = None#

The index of the reported iterate under iterate="best"; None under "last".

Fluctuation.carried: tuple[InitialFit, ...] = ()#
Fluctuation.epsilon_std_error: ndarray[tuple[Any, ...], dtype[float64]] | None = None#
Fluctuation.failure: Literal['singular_hessian', 'line_search_exhausted', 'nonfinite_step', 'separation_suspected', 'bounds_pinned', 'max_iter_reached'] | None = None#
Fluctuation.folds: tuple[FoldFluctuation, ...] = ()#
Fluctuation.hessian_condition: float = nan#
Fluctuation.initial_score_norm#

Largest absolute score component before targeting.

Fluctuation.loglik: float = nan#
Fluctuation.mechanism: Any | None = None#

The treatment-mechanism half of the targeting, for a group whose parameter is defined through the mechanism (today only ipsi); None for every other group, whose targeting is finished when this fluctuation converges. Carried here rather than on the nuisances so that result.nuisance.propensity stays the initial cross-fitted mechanism, exactly as outcome stays the initial regression – see cleverly.fluctuation.mechanism.

Fluctuation.n_solver_calls: int = 1#
Fluctuation.projection: Any | None = None#

The working model’s coefficients and how the alternation that found them went, for an msm group whose link makes the clever covariate depend on them; None for the identity link and for every other group. A sibling of mechanism above and carried for the same reason – it is the other half of a targeting step that has two – but not the same kind of object: the mechanism is a nuisance that was tilted, while this is the reported parameter, solved for rather than fluctuated. See solve_with_projection().

Fluctuation.reduction: Any | None = None#

The extra half of a doubly-robust fit: equation (10)’s fluctuation, the reduced regressions it was solved against, and the mechanism tilt of equation (9). None for every fit that is not a DRTMLE. A third sibling of the two above, carried here for the same reason and with the same consequence – neither the targeted mechanism nor the refitted reductions are written back onto the nuisances, so result.nuisance keeps describing the models that were actually fitted. See solve_with_reduction().

Fluctuation.relative_score_norm#

Largest score component relative to its maximum possible magnitude.

Fluctuation.rule_ratio: float | None = None#

The largest score over its threshold at the reported fit, under a declared rule. None under "relative".

Fluctuation.score_initial: ndarray[tuple[Any, ...], dtype[float64]] | None = None#
Fluctuation.score_norm#

Largest absolute score component, in the outcome’s own units.

Fluctuation.score_scale: ndarray[tuple[Any, ...], dtype[float64]] | None = None#
Fluctuation.stopping: str = 'relative'#

The stopping rule that judged converged. "relative" is the shipped test of the relative score norm against tol.

Fluctuation.stratified: Fluctuation | None = None#

The stratum half of a linked working model with baseline strata. The marginal coefficients come from this fluctuation, which is the shipped unstratified solve, and the stratum coefficients from this nested one, whose covariate holds one block I(S = s) H(beta_s) / P_n(S = s) per stratum. Each stratum block reads its own coefficients, so one fluctuation cannot solve both. None for every other fit.

Fluctuation.thresholds: ndarray[tuple[Any, ...], dtype[float64]] | None = None#

The per-column threshold of a declared rule at the reported fit, in the outcome’s units. None under "relative".

Fluctuation.epsilon: ndarray[tuple[Any, ...], dtype[float64]]#
Fluctuation.targeted: InitialFit#
Fluctuation.score: ndarray[tuple[Any, ...], dtype[float64]]#
Fluctuation.converged: bool#
Fluctuation.n_iter: int#
Fluctuation.trace: tuple[float, ...]#
Fluctuation.method: Literal['iterative', 'one_step', 'linear']#
Fluctuation.names: tuple[str, ...]#