cleverly.interventions.PolicySet#

class cleverly.interventions.PolicySet(names: tuple[str, ...], descriptions: tuple[str, ...], shifted: FloatArray, ratio: FloatArray, ratio_at: FloatArray, moved: BoolArray, reference: float = 0.0, mixing: FloatArray | None = None, component_codes: IntArray | None = None, observed_codes: IntArray | None = None, capped: BoolArray | None = None, joint: JointDensityParts | None = None, component_rows: BoolArray | None = None, curve_terms: FloatArray | None = None)[source]#

Every declared policy, evaluated on the data and on the estimated mechanism.

Holds arrays and no callables, for the reason RegimeSet does: a fit reached through retarget() – a truncation sweep, the bootstrap, a result loaded from disk – targets the same declared policies without the mechanism being refit or the caller’s objects being reachable.

The arrays are indexed by component: one deterministic branch of a declared policy. A deterministic policy is one component, mixing is then None and the component codes are the policy codes. A randomized policy contributes one component per randomizer value, and row s of mixing holds the known probabilities with which policy s averages its components.

Parameters:
namestuple of str

One per policy, in the order they were declared. Report labels.

descriptionstuple of str

A stable text of each policy, from describe_policy().

shiftedndarray

(n, C), \(d_c(A_i, W_i)\) per component: a dose on a continuous treatment and a level code on a categorical one. (n, C, d) on a vector treatment with a continuous component: the component codes and doses each branch assigns.

rationdarray

(n, C), \(h_c(A_i, W_i)\) – the clever covariate at the observed treatment. Untruncated on a continuous treatment, and targeting applies no bound to it: the mtp covariate is this ratio divided only by the missingness and intermediate mechanisms a fit declares. Those mechanisms are bounded at targeting time, and truncation_curve(mechanism=True) sweeps that bound. No option bounds or sweeps the ratio itself.

ratio_atndarray

(n, C, C), \(h_c(d_s(A_i, W_i), W_i)\) at [i, s, c]. The fluctuation updates \(\bar Q\) as a function of \((a, W)\), so obtaining \(\bar Q^*(d_s(A,W), W)\) needs the covariate evaluated at the assigned dose – hence a matrix per row rather than a vector.

movedndarray

(n, C) boolean, whether the component changed that row’s treatment.

referencefloat

Code of the policy contrasts are taken against.

mixingndarray or None

(S, C) known component weights of each policy, or None when every policy is deterministic.

component_codesndarray or None

(n, C, K) level map of each component on a categorical treatment, from discrete_assignments(), or None on a continuous one.

observed_codesndarray or None

(n,) observed level codes on a categorical treatment, or None.

cappedndarray or None

(n, C) boolean, whether a declared cap held the component at that row’s own dose; on a vector treatment, whether a cap held any of its doses. None on a categorical treatment, where no cap is evaluated.

jointJointDensityParts or None

On a vector treatment with a continuous component, fitted on the density route: the factors of the node’s law at the observed value and at each branch’s assigned value (joint_density). design_at() rebuilds the ratios from them with the categorical factor bounded. None elsewhere.

component_rowsndarray or None

(n, C) boolean, the rows each component’s mean and curve read, or None when every component reads every row. A fold-average policy (X40 part (e1)) has one component per outer fold, at the map fitted on that fold’s training rows, and the component reads only the fold’s held-out rows. Its ratio is zero on the other rows, so its fluctuation solves the fold’s own score.

curve_termsndarray or None

(n, S) the term each policy’s curve adds on the scaled outcome scale, or None. A population-target policy with a tuned cap adds \(\partial\Phi_n' \mathrm{IF}\) (X40 part (e2)); every other policy adds zero.

Attributes:
nint

Return the number of observations.

n_policiesint

Return the number of declared policies.

n_componentsint

Return the number of deterministic components the policies expand into.

codestuple of float

The keys the per-parameter arrays use, (0.0, ..., S-1.0).

component_keystuple of float

The keys the per-component predictions use, (0.0, ..., C-1.0).

labelsdict of float to str

Code to reported label, which is what parameter_name is given.

is_discretebool

Whether the treatment is categorical, so the ratio reads the propensity.

weightsndarray

(S, C) component weights of each policy, the identity without a randomizer.

policy_rationdarray

(n, S) each policy’s clever covariate at the observed treatment.

designndarray

(n, C + 1, C) – the covariate at the observed dose, then at each assigned one.

Methods#

PolicySet.design_at(propensity: ndarray[tuple[Any, ...], dtype[float64]] | None, bounds: tuple[float, float] | None = None) → ndarray[tuple[Any, ...], dtype[float64]][source]#

design, rebuilt from a bounded mechanism on a categorical treatment.

On a continuous treatment this is design, since the density ratio is not bounded. On a categorical one the ratio reads the fitted probabilities, and targeting passes them bounded by g_bounds, as for every other arm-indexed clever covariate. On a vector treatment fitted on the density route the categorical factor is bounded into bounds by the same rule, in the numerator and the denominator, and the doses’ factors are not bounded.

Parameters:
propensityndarray or None

(n, K) bounded treatment probabilities.

boundstuple of float or None

The bounds of the categorical factor of a vector treatment. None reads the factor unbounded.

Returns:
ndarray

(n, C + 1, C).

classmethod PolicySet.evaluate(policies: Sequence[object], data: CausalData, density: ConditionalDensity | MixedDensity | None = None, *, propensity: ndarray[tuple[Any, ...], dtype[float64]] | None = None, reference: str | None = None) → PolicySet[source]#

Evaluate every policy against the data and the estimated mechanism.

On a continuous treatment every entry is a lookup into density’s stored bin probabilities, so \(g(A \mid W)\) and \(g(b_j(A) \mid W)\) for one row necessarily come from the same out-of-fold model – there is no second model to get wrong. On a categorical treatment the ratio reads propensity by the discrete formula, and targeting rebuilds it from the bounded mechanism (design_at()).

Parameters:
policiessequence of policy

The policies to evaluate, in the order their codes will follow.

dataCausalData

Validated study data, which supplies the observed treatment.

densityConditionalDensity, MixedDensity or None

Estimated conditional density of the dose given the covariates, on a continuous treatment. A dose with declared atoms reads a MixedDensity.

propensityndarray or None

(n, K) fitted treatment probabilities, on a categorical treatment.

referencestr or None

Label of the policy contrasts are taken against. None uses the first.

Returns:
PolicySet

Every policy evaluated on the data and that mechanism.

classmethod PolicySet.evaluate_by_classifier(policies: Sequence[object], data: CausalData, learner: Any, folds: Folds, *, reference: str | None = None, n_jobs: int = 1, by_fold: Sequence[Sequence[object]] | None = None) → PolicySet[source]#

Evaluate every policy with ratios estimated by classification.

The ratio of each component is classifier_ratio() (Díaz, Williams, Hoffman and Schenck 2023, Section 5.4), cross-fitted over units on folds. No density is fitted. The checks of the policy are the ones the density route runs, so a policy that is not piecewise smooth invertible on a continuous treatment is refused on this route too.

Parameters:
policiessequence of policy

The policies to evaluate, in the order their codes will follow.

dataCausalData

Validated study data.

learnerLearner

The binary classifier of the stacked label.

foldsFolds

The outer split.

referencestr or None

Label of the policy contrasts are taken against.

n_jobsint

Parallel workers across the outer folds.

by_foldsequence or None

Per outer fold, the policies that fold’s classifier stacks, in place of policies, with the same branches: a population-target tuned cap at the tuner fitted on that fold’s training rows (test_t_e7_each_fold_trains_at_its_own_tuner). The ratio is still evaluated at the doses policies assign. None stacks policies in every fold.

Returns:
PolicySet

Every policy evaluated on the data, with classifier ratios.

classmethod PolicySet.evaluate_vector_density(policies: Sequence[object], data: CausalData, learner: Any, folds: Folds, *, reference: str | None = None, n_bins: int | None = None, n_jobs: int = 1) → PolicySet[source]#

Evaluate joint policies at a vector treatment with a continuous component, by density.

The treatment’s law is fitted by the chain rule, and each policy’s induced density is the sum over categorical preimages and piece paths of joint_density. The ratio of component c is evaluated at the observed value and at every component’s assigned value, from the same out-of-fold factors. design_at() rebuilds both with the categorical factor bounded, as for a categorical treatment.

Parameters:
policiessequence of JointPolicy

The policies to evaluate, in the order their codes will follow.

dataCausalData

Validated study data with a vector treatment.

learnerLearner

The classifier of the categorical factor and of every dose’s hazards.

foldsFolds

The outer split.

referencestr or None

Label of the policy contrasts are taken against.

n_binsint or None

The bin count of each dose’s density.

n_jobsint

Parallel workers across the outer folds.

Returns:
PolicySet

Every policy evaluated on the data, with the joint factors kept.

PolicySet.label(code: float) → str[source]#

Return the label for one indexed policy.

Parameters:
codefloat

Policy code.

Returns:
str

The label the policy was declared under.

PolicySet.subset(index: Any) → PolicySet[source]#

The same policies on a row subset – a fold, a bootstrap resample.

Sliced rather than re-evaluated, for the reason subset() gives: a policy is the same policy on a subsample, and re-deriving it would let the resample redefine the estimand.

Parameters:
indexarray_like

Row positions or a boolean mask.

Returns:
PolicySet

The same policies over the selected rows.

Attributes#

PolicySet.capped: BoolArray | None = None#
PolicySet.codes#

The keys the per-parameter arrays use, (0.0, ..., S-1.0).

PolicySet.component_codes: IntArray | None = None#
PolicySet.component_keys#

The keys the per-component predictions use, (0.0, ..., C-1.0).

PolicySet.component_rows: BoolArray | None = None#
PolicySet.curve_terms: FloatArray | None = None#
PolicySet.design#

(n, C + 1, C) – the covariate at the observed dose, then at each assigned one.

One array rather than two, because submodel_for() dispatches on the group name alone and every builder takes the same keyword-only signature; policies= is that one keyword. Row block 0 is the covariate at the observed treatment and block s + 1 the covariate at \(d_s(A, W)\).

PolicySet.is_discrete#

Whether the treatment is categorical, so the ratio reads the propensity.

PolicySet.joint: JointDensityParts | None = None#
PolicySet.labels#

Code to reported label, which is what parameter_name is given.

PolicySet.mixing: FloatArray | None = None#
PolicySet.n#

Return the number of observations.

PolicySet.n_components#

Return the number of deterministic components the policies expand into.

PolicySet.n_policies#

Return the number of declared policies.

PolicySet.observed_codes: IntArray | None = None#
PolicySet.policy_ratio#

(n, S) each policy’s clever covariate at the observed treatment.

The known component weights average the component ratios, which is the ratio of the policy’s induced density: \(g^d = \sum_e p(e)\, g^{d_e}\). A fold-average policy reads, at each row, the ratio of the component that holds the row out, so the weights are renormalised over the components that read the row.

PolicySet.reference: float = 0.0#
PolicySet.weights#

(S, C) component weights of each policy, the identity without a randomizer.

PolicySet.names: tuple[str, ...]#
PolicySet.descriptions: tuple[str, ...]#
PolicySet.shifted: FloatArray#
PolicySet.ratio: FloatArray#
PolicySet.ratio_at: FloatArray#
PolicySet.moved: BoolArray#