cleverly.learners.ConditionalDensity#
- class cleverly.learners.ConditionalDensity(bin_probabilities: ndarray[tuple[Any, ...], dtype[float64]], edges: ndarray[tuple[Any, ...], dtype[float64]])[source]#
An evaluated conditional density: one row of bin probabilities per observation.
Like
RegimeSet, this holds evaluated arrays and no callables. The density is estimated once, out of fold, and stored as the(n, B)matrix \(P(b(A) = b \mid W_i)\); every later evaluation – at the observed treatment, at a shifted one, at a grid for a plot – is a lookup into rowi, not a model call.That is not only an efficiency: it is what makes “
g(A | W)andg(A - delta | W)come from the same out-of-fold model” a structural fact rather than an invariant somebody has to maintain. It is also what lets a fit surviveload(), a bootstrap resample and everyretarget()without the learner being refit.
Methods#
- ConditionalDensity.bin_of(values: ndarray[tuple[Any, ...], dtype[float64]]) ndarray[tuple[Any, ...], dtype[int64]][source]#
Which bin each value falls in;
-1outside the support.-1rather than a clamp, so a caller that evaluates outside the observed range gets a zero density and can see that it did, instead of silently reading the nearest edge bin.
- ConditionalDensity.crossing_fraction(shifted: ndarray[tuple[Any, ...], dtype[float64]], observed: ndarray[tuple[Any, ...], dtype[float64]]) float[source]#
Share of rows a shift actually moves into a different bin.
The resolution guard described in the module docstring: a shift that moves nobody across an edge has a clever covariate of exactly one everywhere and estimates nothing, however clean the fit looks.
- ConditionalDensity.density_at(values: ndarray[tuple[Any, ...], dtype[float64]]) ndarray[tuple[Any, ...], dtype[float64]][source]#
\(\hat g(a_i \mid W_i)\) for one value per row; zero outside the support.
A pure lookup into
bin_probabilities, so this can be called as often as an estimand needs without refitting anything.
- ConditionalDensity.integrated() ndarray[tuple[Any, ...], dtype[float64]][source]#
(n,)values of \(\int \hat g(a \mid W_i)\, da\), which must all be one.The hazard product is constructed to sum to one exactly, so this is a check on the arithmetic rather than on the model; it is reported because a silent failure here would rescale every clever covariate by an amount no other diagnostic would see.
- Returns:
ndarray(n,)integral of the fitted density over the support, per row.
- ConditionalDensity.subset(index: Any) ConditionalDensity[source]#
The same density on a row subset – a bootstrap resample, a validation fold.
The rows are sliced and the edges kept, exactly as
subset()slices its densities: re-deriving the edges from the resample would give the subset a different discretisation from the fit it is meant to be a replicate of.- Parameters:
- indexarray_like
Row positions or a boolean mask.
- Returns:
ConditionalDensityThe same density over the selected rows.
Attributes#
- ConditionalDensity.n#
Return the number of observations.
- ConditionalDensity.n_bins#
Return the number of bins the treatment support is divided into.
- Returns:
intBin count, which is the second axis of
bin_probabilities.
- ConditionalDensity.support#
(lower, upper)– the range the density is nonzero on.
- ConditionalDensity.widths#
(B,)bin widths, the Jacobian turning a probability into a density.