Evaluation Engine API Reference
The kreview.eval_engine module contains the statistical testing functions, ML model training, visualization generators, and clinical utility computations.
For conceptual explanations, see:
- Statistical Tests
- Models & Metrics
- Decision Curve Analysis
- Dashboard Interpretation Guide
- Feature Cards
kreview.eval_engine
FeatureEvaluator
Base class for all feature evaluators. Defines the extraction contract that transforms raw DuckDB queries into 1D arrays.
Subclasses must implement extract() for per-sample Python extraction.
Optionally, override extract_sql() to return a DuckDB SQL query that
performs full-cohort extraction in a single pass (no Python loop).
Class Attributes
name: Evaluator identifier (used in file names, logging).
source_file: Parquet suffix (e.g. '.WPS.parquet').
tier: Feature importance tier (1=core, 2=supplementary).
category: Feature category for grouping (e.g. 'epigenetics').
extract_columns: If set, DuckDB will SELECT only these columns
instead of *. Reduces memory for wide parquet files.
Must include 'sample_id' (or it will be auto-appended).
max_chunk_rows: Target rows per DuckDB chunk (default 15M).
Lower values reduce peak memory for heavy features.
supports_sql
property
True if this evaluator provides a SQL pushdown query.
extract(df)
Transform the loaded raw dataframe into meaningful scalar metrics. Called per sample-group or per sample.
extract_sql()
Return a DuckDB SQL query for full-cohort extraction, or None.
If implemented, the query should:
- Accept a read_parquet(?, ...) placeholder for file paths
- GROUP BY sample_id to produce one row per sample
- SELECT all extracted feature columns with their final names
Returning None (the default) means this evaluator does not
support SQL pushdown and will use the chunked Python path.
GPUModelCVAdapter
Bases: BaseEstimator, ClassifierMixin
sklearn-compatible wrapper for GPU foundation models.
Sidesteps two TabPFN sklearn-compatibility issues:
-
classes_is a read-only@propertyonFinetunedTabPFNClassifierthat raisesAttributeErrorwhen unfitted.cross_val_predicttries to readclasses_on the fitted clone, butsklearn.clone()copies the unfitted object first and the property breaks. This adapter exposesclasses_as a plain attribute set duringfit(). -
sklearn.base.clone()is unreliable for TabPFN (PriorLabs/TabPFN#327). By accepting amodel_factorycallable, eachfit()creates a fresh instance without relying on clone.
Attributes:
| Name | Type | Description |
|---|---|---|
model_factory |
Zero-argument callable returning a fresh model instance. |
|
name |
Human-readable model name for logging. |
|
model_ |
The fitted model instance (set after |
|
classes_ |
Unique sorted class labels (set after |
__getstate__()
Exclude model_factory (a lambda closure) from pickle serialization.
The factory is only needed for training (creating fresh model instances
during CV). Deserialized adapters are inference-only — predict_proba()
uses the already-fitted model_ attribute.
__setstate__(state)
Restore adapter in inference-only mode (no model_factory).
Calling fit() on a deserialized adapter will raise TypeError
because model_factory is None. This is intentional — loaded models
are for prediction/SHAP only.
fit(X, y)
Create a fresh model via factory and fit it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix (n_samples, n_features). |
required |
y
|
ndarray
|
Binary label array (n_samples,). |
required |
Returns:
| Type | Description |
|---|---|
'GPUModelCVAdapter'
|
self |
Raises:
| Type | Description |
|---|---|
TypeError
|
If called on a deserialized adapter ( |
predict_proba(X)
Predict class probabilities.
Raises:
| Type | Description |
|---|---|
NotFittedError
|
If |
predict(X)
Predict class labels.
Raises:
| Type | Description |
|---|---|
NotFittedError
|
If |
parse_array(s)
Parse a numeric array value into a list of floats.
Handles multiple input formats:
- numpy arrays (native parquet list<float>, DuckDB FLOAT[])
- Python lists/tuples
- String-encoded arrays ('[1.0 2.0 3.0]') from legacy parquet
Returns empty list on any parse failure (no silent corruption).
univariate_auc(feature_col, y, n_folds=5, random_state=42)
Compute cross-validated AUC for a single feature using univariate LR.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feature_col
|
pandas Series or array-like of a single feature. |
required | |
y
|
binary label array (0/1). |
required | |
n_folds
|
int
|
number of CV folds. |
5
|
random_state
|
int
|
random seed. |
42
|
Returns:
| Type | Description |
|---|---|
float
|
Cross-validated AUC (float). Returns 0.5 if the feature is constant, |
float
|
there are too few samples per class, or CV fails. |
mutual_info_score(feature_col, y, random_state=42)
Compute mutual information between a single feature and binary target.
Uses sklearn's mutual_info_classif with k=3 nearest neighbors to estimate the non-linear dependency between a feature and the label. Unlike AUC, mutual information captures arbitrary (non-monotonic) relationships.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
feature_col
|
pandas Series or array-like of a single feature. |
required | |
y
|
binary label array (0/1). |
required | |
random_state
|
int
|
random seed for reproducibility. |
42
|
Returns:
| Type | Description |
|---|---|
float
|
Mutual information score (float, >= 0). Higher means more informative. |
float
|
Returns 0.0 if the feature is constant or computation fails. |
set_theme(cvd_safe=False)
Dynamically updates the global label and model colors based on CVD preference.
evaluate_feature(feature_values, labels, total_fragments=None, max_vaf=None)
Run all statistical tests for a single feature in one stratum. Outputs metrics directly to scoring dict.
plot_violin(df, feature_col, label_col='label', title='')
4-group violin with overlaid box plot and individual points for small groups.
plot_density(df, feature_col, label_col='label', title='')
Overlaid density curves per group — shows distribution shape differences.
plot_feature_vs_vaf(df, feature_col, vaf_col='max_vaf', label_col='label', title='')
Continuous relationship between feature and tumor burden (VAF proxy).
plot_roc_curves(y_true_dict, y_score_dict, title='')
Overlay ROC curves for multiple comparisons.
plot_feature_importance(importances, title='')
Bar plot of RF feature importances.
decision_curve_analysis(y_true, y_prob, thresholds=None)
Compute Decision Curve Analysis (DCA) net benefit data.
For each threshold, calculates the net benefit of using the model vs treating all or treating none. This helps clinicians choose an operating threshold that balances false positives against missed detections.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_true
|
ndarray
|
Binary ground truth labels (0/1). |
required |
y_prob
|
ndarray
|
Predicted probabilities for positive class. |
required |
thresholds
|
ndarray | None
|
Array of decision thresholds to evaluate.
Defaults to |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Dictionary with keys |
dict
|
|
identify_feature_groups(columns, meta_cols=None)
Map selected feature column names → feature groups by statistic type.
Groups are identified by suffix pattern matching against known kreview
evaluator output patterns (see _SUFFIX_TO_GROUP). Columns that don't
match any known suffix go into the "other" group.
Parameters
columns : list[str]
Feature column names from a selected matrix.
meta_cols : set[str] | None
Columns to exclude (e.g. LABEL_META_COLS). If None, uses
kreview.core.LABEL_META_COLS.
Returns
dict[str, list[str]] Mapping of group name → list of column names belonging to that group.
generate_subsets(groups)
Generate feature subsets for ablation from feature groups.
Returns a dict of named subsets:
"ALL": all features from all groups"{group}": solo — only features from that group (one per group)"no_{group}": leave-one-out — everything except that group
If only 1 group exists, returns only {"ALL": all_features} since
ablation is meaningless with a single group.
Parameters
groups : dict[str, list[str]]
Output of :func:identify_feature_groups.
Returns
dict[str, list[str]] Named subsets mapping subset name → list of feature column names.
ablate_feature_groups(matrix_path, models=('lr', 'rf', 'xgb'), n_outer_folds=5, n_inner_folds=3, random_state=42, output_path=None, device='cpu', max_gpu_features=None, eval_stats_path=None)
Run feature group ablation with per-fold nested cross-validation.
For each outer fold, evaluates all feature subsets using inner CV to find the best subset per model. Results include per-fold winners and the fold assignment array for downstream reproducibility.
Data flow:
- Load matrix, filter to
split == "train"(prevent holdout leakage). - Build binary target via :func:
kreview.selection.build_binary_target. - Identify feature groups via :func:
identify_feature_groups. - Generate subsets via :func:
generate_subsets. - For each outer fold:
a. Split into outer-train / outer-val using StratifiedKFold.
b. For each model × subset:
- Run :func:
_inner_cv_sensitivityon outer-train. - Record sensitivity@100%spec_healthy score. c. Identify winning subset per model for this fold.
- Run :func:
- Aggregate results across folds.
Parameters
matrix_path : Path | str
Path to *_matrix.parquet file.
models : tuple[str, ...]
Model names to evaluate. CPU: "lr", "rf", "xgb".
GPU: "tabpfn", "tabicl" (zero-shot only).
n_outer_folds : int
Number of outer CV folds (must match downstream EVAL step).
n_inner_folds : int
Number of inner CV folds for subset selection.
random_state : int
Random seed for reproducibility.
output_path : Path | str | None
Directory to write ablation JSON results.
device : str
PyTorch device for GPU models (default "cpu").
max_gpu_features : int | None
Feature cap for GPU models (TabPFN limit).
eval_stats_path : Path | str | None
Path to *_eval_stats.parquet for score-based feature capping.
Returns
dict Ablation results containing per-fold per-model scores, winners, fold assignments, and feature group metadata.
merge_ablation(cpu_json_path, gpu_json_path=None, matrix_path=None, output_path=None)
Merge CPU and GPU ablation results into a unified best_subset.json.
For each model across all folds, determines the per-fold winning
feature subset. The output JSON is consumed by downstream
kreview eval cpu / kreview eval gpu via --best-subset.
Handles GPU error JSONs gracefully: if the GPU JSON contains an
"error" key (from a failed GPU ablation), the merge proceeds
with CPU-only results.
Parameters
cpu_json_path : Path | str
Path to *_ablation_cpu.json from :func:ablate_feature_groups.
gpu_json_path : Path | str | None
Path to *_ablation_gpu.json. None or a path to a
file containing "error" triggers CPU-only mode.
matrix_path : Path | str | None
Path to the original matrix parquet (for fold assignment
verification).
output_path : Path | str | None
Directory to write {evaluator}_best_subset.json.
Returns
dict
Merged result with per_model_per_fold_features and
fold_assignment.
evaluate_model(model, X, y, cv, name, feature_names=None, refit=True, compute_shap=False, shap_samples=500, random_state=42, sample_labels=None)
Evaluate any sklearn-compatible model via stratified cross-validation.
Shared primitive used by cpu_models(), gpu_models(), and multimodal_eval(). The model must implement fit() and predict_proba().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
sklearn-compatible estimator (Pipeline, RF, XGB, TabPFN, etc.). |
required | |
X
|
ndarray
|
Feature matrix, shape (n_samples, n_features). |
required |
y
|
ndarray
|
Binary labels (0/1), shape (n_samples,). |
required |
cv
|
StratifiedKFold
|
Pre-configured StratifiedKFold splitter. |
required |
name
|
str
|
Prefix for all result keys (e.g. "lr", "rf", "tabpfn"). |
required |
feature_names
|
list[str] | None
|
Optional feature names for importance extraction. |
None
|
refit
|
bool
|
If True, refit model on full data after CV. |
True
|
compute_shap
|
bool
|
If True, compute SHAP values (requires refit=True). |
False
|
shap_samples
|
int
|
Max samples for SHAP computation. |
500
|
random_state
|
int
|
Random seed for bootstrap CI. |
42
|
sample_labels
|
ndarray | None
|
Original 4-tier labels (e.g. "Healthy Normal", "Possible ctDNA+") for computing sensitivity@100%spec on healthy normals only. Shape (n_samples,). Optional. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
(result_dict, fitted_model_or_None). result_dict keys are prefixed |
object | None
|
with |
tuple[dict, object | None]
|
|
evaluate_holdout(fitted_model, X_test, y_test, name, sample_labels=None)
Evaluate a fitted model on the holdout test set.
This is called AFTER evaluate_model() has refitted the model on
the full training set. The holdout set was never seen during CV or
feature selection, providing an unbiased estimate of generalization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fitted_model
|
Model already fitted on training data. |
required | |
X_test
|
ndarray
|
Holdout feature matrix. |
required |
y_test
|
ndarray
|
Holdout binary labels. |
required |
name
|
str
|
Model name prefix for result keys. |
required |
sample_labels
|
ndarray | None
|
Original label strings for the holdout set (for healthy-normal specificity). |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Dict with holdout metrics, all prefixed with |
cpu_models(X, y, feature_names=None, cancer_types=None, assays=None, n_folds=5, random_state=42, compute_shap=False, shap_samples=500, sample_labels=None, per_fold_features=None, fold_assignment=None)
Train LR, RF, and XGB on a feature matrix with stratified CV.
Delegates per-model evaluation to evaluate_model(), then adds
cross-model diagnostics (AUC deltas, top features, threshold sweep,
DCA, feature stability, subgroup analysis).
When per_fold_features and fold_assignment are provided
(from nested CV ablation), bypasses evaluate_model() and uses
manual fold iteration with per-fold feature filtering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sample_labels
|
ndarray | None
|
Original 4-tier labels for computing healthy-normal
specificity. Passed to |
None
|
per_fold_features
|
dict | None
|
Dict from best_subset.json with per-model
per-fold feature lists. Structure:
|
None
|
fold_assignment
|
list | ndarray | None
|
Per-sample fold index array from ablation. Must align with the samples in X/y. |
None
|
Returns (results_dict, lr_pipeline, rf_model, xgb_model).
Audit fixes preserved
- C-01: LR uses Pipeline(scaler+lr) to prevent data leakage
- C-02: Subgroup metrics use out-of-fold predictions (unbiased)
- H-01: LR has class_weight="balanced", XGB has scale_pos_weight
- M-02: Bootstrap 95% CI on AUC values
gpu_models(X, y, feature_names=None, cancer_types=None, assays=None, n_folds=5, random_state=42, models=('tabpfn',), device='cuda', finetune_epochs=50, finetune_lr=1e-05, compute_shap=False, shap_samples=500, sample_labels=None, max_gpu_features=None, eval_stats=None, per_fold_features=None, fold_assignment=None)
Train GPU foundation models on a feature matrix.
Same output schema as cpu_models() for each model, using the shared
evaluate_model() primitive. Each model name encodes its variant:
"tabpfn" = zero-shot, "tabpfn_ft" = fine-tuned, etc.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
X
|
ndarray
|
Feature matrix, shape (n_samples, n_features). |
required |
y
|
ndarray
|
Binary labels (0/1). |
required |
feature_names
|
list[str] | None
|
Optional feature names. |
None
|
cancer_types
|
ndarray | None
|
Optional cancer type array for subgroup analysis. |
None
|
assays
|
ndarray | None
|
Optional assay array for subgroup analysis. |
None
|
n_folds
|
int
|
Number of CV folds. |
5
|
random_state
|
int
|
Random seed. |
42
|
models
|
tuple[str, ...]
|
Tuple of GPU model names. Valid values:
|
('tabpfn',)
|
device
|
str
|
PyTorch device ( |
'cuda'
|
finetune_epochs
|
int
|
Epochs for fine-tuned variants (default 50). |
50
|
finetune_lr
|
float
|
Learning rate for fine-tuned variants. |
1e-05
|
compute_shap
|
bool
|
If True, compute SHAP values. |
False
|
shap_samples
|
int
|
Max samples for SHAP computation. |
500
|
sample_labels
|
ndarray | None
|
Original 4-tier labels for computing healthy-normal
specificity. Passed to |
None
|
max_gpu_features
|
int | None
|
If set and n_features > max_gpu_features, cap to the top N features using eval_stats scores. Default None (no cap). CLI default is 150. |
None
|
eval_stats
|
DataFrame | None
|
DataFrame from |
None
|
per_fold_features
|
dict | None
|
Dict from best_subset.json with per-model per-fold feature lists (from ablation). When provided, uses manual fold iteration with per-fold feature filtering. |
None
|
fold_assignment
|
list | ndarray | None
|
Per-sample fold index array from ablation. |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
(results_dict, fitted_models_dict). fitted_models_dict maps |
dict[str, object]
|
model name to fitted model object. |
load_model_results(directory, evaluator_name)
Load and merge CPU + GPU model results for a single evaluator.
Looks for:
{evaluator_name}_model_results.json(CPU results){evaluator_name}_gpu_model_results.json(merged GPU results){evaluator_name}_*_gpu_model_results.json(scattered per-model GPU results from NF scattering, e.g.FSC_tabpfn_gpu_model_results.json)
All found GPU files are merged into the CPU dict. GPU keys take
precedence for overlapping model keys; CPU metadata keys
(evaluator, matrix_path, etc.) are preserved.
Used by report templates where the evaluator name is known.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Path
|
Directory containing the JSON result files. |
required |
evaluator_name
|
str
|
Evaluator name (e.g., |
required |
Returns:
| Type | Description |
|---|---|
dict | None
|
Merged dict, or None if no JSON exists for this evaluator. |
load_all_model_results(directory)
Load and merge all CPU + GPU model results from a directory.
Scans for *_model_results.json and *_gpu_model_results.json,
groups by evaluator name, and merges GPU keys into CPU dicts.
Used by scoreboard and multimodal engine for directory scanning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Path
|
Directory containing model result JSON files. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, dict]
|
Dict keyed by evaluator name → merged model results dict. |
dict[str, dict]
|
Empty dict if no results found. |
multimodal_eval(results_dir, super_matrix_path=None, *, models=('rf', 'xgb'), gpu_models=(), device='cuda', finetune_epochs=50, finetune_lr=1e-05, n_folds=5, top_percentile=10.0, random_state=42, multimodal_selection='mi')
Cross-evaluator multimodal evaluation.
Implements three complementary strategies:
-
Stacking: Meta-learner trained on OOF predictions from all per-evaluator models. Each column is one evaluator's OOF probability. This measures how much combining multiple fragmentomics signals improves classification.
-
Raw features (optional): If
super_matrix_pathis provided, trains directly on the fused feature matrix withmultimodal_selection-based feature selection. -
Ablation: Leave-one-evaluator-out analysis on the stacking matrix, showing each evaluator's marginal contribution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results_dir
|
str | Path
|
Directory with |
required |
super_matrix_path
|
str | Path | None
|
Optional path to |
None
|
models
|
tuple[str, ...]
|
CPU model names to use ( |
('rf', 'xgb')
|
gpu_models
|
tuple[str, ...]
|
GPU model names ( |
()
|
device
|
str
|
PyTorch device string for GPU models. |
'cuda'
|
finetune_epochs
|
int
|
Epochs for fine-tuned GPU variants (default 50). |
50
|
finetune_lr
|
float
|
Learning rate for fine-tuned GPU variants. |
1e-05
|
n_folds
|
int
|
Cross-validation folds. |
5
|
top_percentile
|
float
|
Top N% features for feature selection. |
10.0
|
random_state
|
int
|
Reproducibility seed. |
42
|
multimodal_selection
|
str
|
Feature selection strategy ('mi', 'boruta_shap', 'leshy', or 'grootcv'). |
'mi'
|
Returns:
| Type | Description |
|---|---|
dict
|
A comprehensive results dict with keys for each strategy. |
multimodal_prep(results_dir, super_matrix_path=None, *, multimodal_selection='mi', top_percentile=10.0, random_state=42, output_dir='.')
Stage 1: Build stacking + optional raw-feature matrices.
Loads per-evaluator model results, builds a stacking matrix from OOF probabilities, optionally loads and selects from the super matrix, and writes outputs as parquet files + a metadata JSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
results_dir
|
str | Path
|
Directory with |
required |
super_matrix_path
|
str | Path | None
|
Optional path to |
None
|
multimodal_selection
|
str
|
Feature selection strategy for raw features ('mi', 'boruta_shap', 'leshy', or 'grootcv'). |
'mi'
|
top_percentile
|
float
|
Top N% features to retain after MI ranking. |
10.0
|
random_state
|
int
|
Random seed. |
42
|
output_dir
|
str | Path
|
Directory to write output files. |
'.'
|
Returns:
| Type | Description |
|---|---|
dict
|
Metadata dict with keys: |
dict
|
|
dict
|
|
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If |
ValueError
|
If no valid evaluator baselines found, or stacking matrix has 0 columns. |
multimodal_single(stacking_matrix_path, model_name, *, raw_features_path=None, n_folds=5, random_state=42, device='cuda', finetune_epochs=50, finetune_lr=1e-05, best_single_auc=0.0, output_dir='.')
Stage 2: Train a single model on the stacking (+ optional raw) matrix.
Reads the stacking matrix parquet, trains the requested model via
:func:evaluate_model, and writes a per-model result JSON.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stacking_matrix_path
|
str | Path
|
Path to |
required |
model_name
|
str
|
Model to train (e.g. |
required |
raw_features_path
|
str | Path | None
|
Optional path to |
None
|
n_folds
|
int
|
Cross-validation folds. |
5
|
random_state
|
int
|
Random seed. |
42
|
device
|
str
|
PyTorch device for GPU models. |
'cuda'
|
finetune_epochs
|
int
|
Fine-tuning epochs for |
50
|
finetune_lr
|
float
|
Fine-tuning learning rate for |
1e-05
|
best_single_auc
|
float
|
Best single-evaluator AUC (for delta computation). |
0.0
|
output_dir
|
str | Path
|
Directory to write output JSON. |
'.'
|
Returns:
| Type | Description |
|---|---|
dict
|
Results dict with stacking and optional raw-feature metrics. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If stacking_matrix_path doesn't exist. |
ValueError
|
If model build fails. |
multimodal_ablation(stacking_matrix_path, stacking_results_dir, *, n_folds=5, random_state=42, output_dir='.')
Stage 3: Leave-one-evaluator-out ablation study.
Finds the best stacking model from partial result JSONs, then for each evaluator drops its columns and retrains to measure marginal contribution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stacking_matrix_path
|
str | Path
|
Path to |
required |
stacking_results_dir
|
str | Path
|
Directory with |
required |
n_folds
|
int
|
Cross-validation folds. |
5
|
random_state
|
int
|
Random seed. |
42
|
output_dir
|
str | Path
|
Directory to write output JSON. |
'.'
|
Returns:
| Type | Description |
|---|---|
dict
|
Ablation results dict with per-evaluator deltas. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If inputs don't exist. |
ValueError
|
If no stacking results found. |
multimodal_merge(stacking_results_dir, prep_metadata_path, *, ablation_path=None, output_dir='.')
Stage 4: Merge partial JSONs into unified multimodal_results.json.
Combines the prep metadata, per-model stacking results, and ablation
results into a single output JSON matching the schema produced by the
monolithic :func:multimodal_eval.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stacking_results_dir
|
str | Path
|
Directory with |
required |
prep_metadata_path
|
str | Path
|
Path to |
required |
ablation_path
|
str | Path | None
|
Optional path to |
None
|
output_dir
|
str | Path
|
Directory to write |
'.'
|
Returns:
| Type | Description |
|---|---|
dict
|
Unified results dict matching :func: |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If prep_metadata_path doesn't exist. |