openquant._core.feature_importance
Compiled from Rust (crates/pyopenquant). Signatures and types are from the shipped .pyi stubs; descriptions are the functions’ own docstrings, as help() shows them. All Python modules.
Functions
mda_from_probabilities
mda_from_probabilities(
y: Sequence[float],
t0: Sequence[int],
t1: Sequence[int],
base_proba: Sequence[float],
permuted_proba: Sequence[Sequence[float]],
feature_names: Sequence[str],
*,
n_splits: int,
pct_embargo: float,
scoring: str,
sample_weight: Sequence[float] | None = None,
seed: int | None = None,
) -> dict[str, tuple[float, float]]Mean decrease accuracy (MDA) from out-of-sample probabilities you computed (AFML 8.3).
The purged folds are rebuilt here with PurgedKFold(n_splits, spans, pct_embargo); they are the folds of openquant.cross_validation.purged_kfold_splits with the same arguments. For each fold, fit your model on the training indices, predict the test indices to get base_proba, then, for each feature j, shuffle column j within the test rows and predict again to get permuted_proba[j]. Because the test folds partition the samples, each column holds one out-of-sample value per sample. The Rust MDA then scores, per fold, the base and each permuted prediction; the per-fold importance is (base - perm) / (0 - perm) for "neg_log_loss" and (base - perm) / (1 - perm) for "accuracy" and "f1" (0 when the denominator is 0 or the ratio is not finite), and the result is its mean over folds. Test-fold scores are weighted by sample_weight.
1 means shuffling the feature destroyed everything the model had, 0 that the model did not need it, and a negative value that it did better without it.
Parameters
y: list[float]Labels, each 0.0 or 1.0, one per sample.
t0: list[int]Start of each sample's label span, as int64 nanoseconds since the epoch (plain integers also work). One per sample, in time order.
t1: list[int]End of each sample's label span, in the same units as
t0. Must be>= t0.base_proba: list[float]Out-of-sample probability of class 1 per sample, from the unpermuted test fold; each in
[0, 1].permuted_proba: list[list[float]]One column per feature, in
feature_namesorder; columnjholds, per sample, the out-of-sample probability of class 1 with featurejshuffled within the test fold.feature_names: list[str]Feature names; must not be empty.
n_splits: intNumber of purged folds;
2 <= n_splits <= len(y). Keyword-only.pct_embargo: floatEmbargo as a fraction of the whole sample count, in
[0, 1), rounded up. Keyword-only.scoring: strOne of
"neg_log_loss","accuracy"or"f1"(F1 of the positive class). Accuracy and F1 threshold the probabilities at 0.5. Keyword-only.sample_weight: list[float] | None, default NoneWeight per sample, applied to the test-fold scores. Keyword-only.
seed: int | None, default NoneDeprecated: passing it emits a
DeprecationWarning. It cannot affect the result, since the shuffles already happened whenpermuted_probawas computed; seed those instead. Keyword-only.
Returns
- dict[str, tuple[float, float]]
{feature: (mean, std)}, keyed by feature name (in sorted order).stdis the standard error of the mean over folds, not the deviation.
Raises
- ValueError
If
scoringis not one of the names above; ift0andt1,y,base_proba, a column ofpermuted_probaorsample_weightdiffer in length from the sample count; iffeature_namesis empty orpermuted_probadoes not have one column per feature; if a probability is not finite or outside[0, 1]; or if the core rejects the folds (e.g. no samples,n_splitsbelow 2 or above the sample count,pct_embargonot a finite number in[0, 1), a span that ends before it starts).
mean_decrease_impurity
mean_decrease_impurity(
per_tree_importances: Sequence[Sequence[float]],
feature_names: Sequence[str],
) -> dict[str, tuple[float, float]]Mean decrease impurity (MDI) aggregated over the trees of a forest (AFML Snippet 8.2).
Following the snippet, a zero importance is treated as missing: the snippet trains with max_features=1, where 0 means "never offered to the tree". With other settings this inflates the mean, so replace zeros with a tiny positive number if 0 means "useless". Per feature the mean over non-missing trees is taken, and its standard error is the sample deviation (ddof 1) times n_trees ** -0.5, with n_trees counting every tree. Means and standard errors are then divided by the sum of the means, so the means sum to 1. A feature that is zero in every tree gets 0; if no mean is positive, everything is 0.
Parameters
per_tree_importances: list[list[float]]One row per tree, each with one impurity importance per feature in
feature_namesorder (e.g.[t.feature_importances_ for t in forest.estimators_]in scikit-learn).feature_names: list[str]Feature names, one per column of
per_tree_importances.
Returns
- dict[str, tuple[float, float]]
{feature: (mean, std)}, keyed by feature name (in sorted order). The means sum to 1;stdis the normalised standard error of the mean, not the deviation.
Raises
- ValueError
If
per_tree_importancesorfeature_namesis empty, or a row does not have one entry per feature name.
sfi_from_probabilities
sfi_from_probabilities(
y: Sequence[float],
t0: Sequence[int],
t1: Sequence[int],
proba: Sequence[Sequence[float]],
feature_names: Sequence[str],
*,
n_splits: int,
pct_embargo: float,
scoring: str,
sample_weight: Sequence[float] | None = None,
) -> dict[str, tuple[float, float]]Single feature importance (SFI) from out-of-sample probabilities you computed (AFML 8.4).
The purged folds are rebuilt here with PurgedKFold(n_splits, spans, pct_embargo); they are the folds of openquant.cross_validation.purged_kfold_splits with the same arguments. For each feature j and each fold, fit your model on feature j alone over the training indices and predict the test indices; proba[j] holds those out-of-sample probabilities, one per sample. The value per feature is the raw cross-validated score, not a ratio: for "neg_log_loss" compare it with -ln 2 (about -0.693), a coin flip. The standard error is the population deviation (ddof 0) over folds divided by sqrt(n_splits). Test-fold scores are weighted by sample_weight, as for mda_from_probabilities and AFML's cvScore.
Parameters
y: list[float]Labels, each 0.0 or 1.0, one per sample.
t0: list[int]Start of each sample's label span, as int64 nanoseconds since the epoch (plain integers also work). One per sample, in time order.
t1: list[int]End of each sample's label span, in the same units as
t0. Must be>= t0.proba: list[list[float]]One column per feature, in
feature_namesorder; columnjholds, per sample, the out-of-sample probability of class 1 from the model fitted on featurejalone.feature_names: list[str]Feature names; must not be empty.
n_splits: intNumber of purged folds;
2 <= n_splits <= len(y). Keyword-only.pct_embargo: floatEmbargo as a fraction of the whole sample count, in
[0, 1), rounded up. Keyword-only.scoring: strOne of
"neg_log_loss","accuracy"or"f1"(F1 of the positive class). Accuracy and F1 threshold the probabilities at 0.5. Keyword-only.sample_weight: list[float] | None, default NoneWeight per sample, applied to the test-fold scores (weighted accuracy, weighted mean log loss, F1 from weighted counts). Weight the fit on your side as well. Keyword-only.
Returns
- dict[str, tuple[float, float]]
{feature: (mean, std)}, keyed by feature name (in sorted order).stdis the standard error of the mean over folds, not the deviation.
Raises
- ValueError
If
scoringis not one of the names above; ift0andt1,y, a column ofprobaorsample_weightdiffer in length from the sample count; iffeature_namesis empty orprobadoes not have one column per feature; if a probability is not finite or outside[0, 1]; or if the core rejects the folds (e.g. no samples,n_splitsbelow 2 or above the sample count,pct_embargonot a finite number in[0, 1), a span that ends before it starts).