Bits & Flames bitsandflames/fyron

Machine Learning

fyron.ml provides tabular classification workflows for clinical data science. It is designed for patient-level feature matrices where the target is a binary or multiclass endpoint, such as treatment response, readmission, toxicity, or progression status.

The module covers the full lightweight workflow:

  1. split data reproducibly,
  2. handle class imbalance,
  3. train Random Forest or XGBoost classifiers,
  4. compute clinically meaningful metrics,
  5. cross-validate and tune,
  6. inspect feature importance,
  7. generate publication-ready evaluation plots,
  8. save fitted estimators.

What This Module Is For

Use fyron.ml when you have a patient-level feature matrix and a classification endpoint. The module is intentionally focused on tabular clinical prediction workflows. It does not decide which variables are clinically valid, whether leakage is present, or whether a model is appropriate for deployment; those remain study-design decisions.

For transparent feature derivation from ICD-10, OPS, delimited code strings, or date columns, use DataFrame Operations before ML preprocessing and model fitting.

Core Concepts

ConceptMeaning
XFeature matrix. A DataFrame is preferred because feature names are preserved.
yEndpoint labels. Binary endpoints are most fully supported.
X_train, X_testSplit feature matrices used for model fitting and evaluation.
y_predHard class predictions.
y_probPositive-class probabilities for binary classifiers.
class imbalanceEvent rates are often skewed; use class_weight or scale_pos_weight deliberately.
result dictNamed output bundle containing model, predictions, probabilities, metrics, and figures.

Install

bash
uv add "fyron[ml]"

Optional Boruta feature selection:

bash
uv add "fyron[ml,boruta]"

Import Pattern

python
from fyron import ml

Using the ml namespace keeps notebooks readable and makes it clear which helpers come from Fyron.

Expected Data

InputAccepted typesNotes
XDataFrame, NumPy array, list-likeDataFrames preserve feature names for importance plots
ySeries, NumPy array, list-likeBinary labels are most fully supported
probabilities1-D array-likePositive-class probabilities for binary metrics and plots

API Contract

TopicContract
Input shapeFeature matrix X, label vector y, train/test splits, or result dictionaries from Fyron training helpers.
Required columnsDataFrames should preserve feature names; metric/plot helpers require explicit label, prediction, probability, or feature columns.
Return shapeResult dictionaries with model/predictions/metrics/figures, metric DataFrames, fitted estimators, or Matplotlib figures.
Saved artifactsFitted estimator files, metric tables, feature importance tables, ROC/PR/calibration/confusion plots, and model-card summaries.
Failure modesMissing optional ML dependencies, mismatched array lengths, nonnumeric feature matrices, invalid labels, missing probabilities, or leakage/imbalance requiring review.

Clinical ML usually benefits from explicit preprocessing before modeling:

  • encode categorical variables,
  • impute missing values,
  • avoid leakage from post-outcome variables,
  • split by patient, not row, when repeated measurements exist,
  • keep a held-out validation set when possible.

Fyron does not hide those choices because they are study-design decisions.

Research Decision: Model Development Versus Validation

DecisionPractical guidance
Feature windowInclude only features available before the prediction/index time.
SplitKeep a locked validation set or external cohort separate from model development.
ImbalanceState whether class weights, scale_pos_weight, or sampling were used.
ThresholdThreshold scans are development information unless pre-specified before validation.
CalibrationReport calibration when probabilities influence decisions.
InterpretationTreat feature importance as model inspection, not causal evidence.

How To Interpret Outputs

Fyron ML functions return dictionaries instead of a single score so you can inspect the model, predictions, probabilities, metrics, and figures independently. In clinical settings, accuracy is rarely enough. Review sensitivity, specificity, AUC, confusion counts, and calibration together, then decide whether threshold tuning or subgroup analysis is needed before reporting performance.

Use feature importance as a model-inspection tool, not as causal evidence. If a feature is clinically surprising, check leakage, coding artifacts, missingness patterns, and whether the train/test split respects patient-level independence.

Parameter Notes

ParameterPractical guidance
test_sizeChoose before model evaluation; common values are 0.2 or 0.3.
stratifyKeep True for imbalanced binary endpoints unless study design says otherwise.
random_stateSet for reproducibility and record it in a provenance manifest.
class_weightUse with sklearn models such as Random Forest.
scale_pos_weightUse with XGBoost binary classification.
pos_labelSet explicitly when the positive/event label is not 1.
plotUse True to create ROC, PR, calibration, and confusion-matrix figures during training.
search / parameter gridsUse for development; keep final reported settings explicit.

API Summary

FunctionPurpose
train_test_split_dataStratified train/test split
compute_class_weightssklearn class weights for imbalanced targets
compute_scale_pos_weightXGBoost binary imbalance ratio
train_random_forestFit RF, predict, score, optionally plot
train_xgboostFit XGBClassifier, predict, score, optionally plot
calculate_classification_metricsAccuracy, balanced accuracy, F1, precision, recall, sensitivity, specificity, AUC, counts
cross_validate_modelStratified k-fold with fold metrics and optional OOF predictions
grid_search_modelGridSearchCV wrapper with optional validation metrics
get_default_param_gridDefault grids for RF, XGB, survival boosting
get_feature_importanceExtract tree-model feature importance table
plot_feature_importancePlot top feature importances
run_boruta_feature_selectionOptional all-relevant feature selection
run_classification_pipelineSplit, optional Boruta, optional search, train, evaluate
save_estimator / load_estimatorjoblib persistence

Before training, use fyron.preprocessing.feature_matrix_report to flag missingness, constants, rare binary variables, and leakage-like column names.

Function Parameter Reference

FunctionRequiredOptionalReturns
train_test_split_dataX, ytest_size, stratify, random_statetrain/test split tuple
compute_class_weightsyclass_weightclass-weight dict
compute_scale_pos_weightypos_labelfloat ratio
sample_weights_for_sklearnyclass_weightsample-weight array
train_random_forestX_train, y_traintest data, class_weight, random_state, plot, pos_label, RF paramstraining result dict
train_xgboostX_train, y_traintest data, scale_pos_weight, random_state, plot, pos_label, XGB paramstraining result dict
calculate_classification_metricsy_true, y_predy_prob, pos_labelmetrics dict
cross_validate_modelestimator, X, ycv, random_state, return_models, return_oof_predictions, pos_labelCV result dict
grid_search_modelestimator, param_grid, X_train, y_trainvalidation data, scoring, cv, n_jobs, refit, random_statesearch result dict
get_default_param_gridmodel_namenoneparam grid dict
get_feature_importancemodelfeature_namesDataFrame
plot_feature_importancemodel, optional feature namestop_n, title, save_path, fmt(fig, ax)
run_boruta_feature_selectionX, yestimator, class_weight, max_iter, random_stateBoruta result dict
run_classification_pipelineX, ymodel/split/imbalance/Boruta/search/plot paramsfull result dict
save_estimatormodel, pathnonewrites file
load_estimatorpathnoneloaded estimator

Train/Test Split

python
X_train, X_test, y_train, y_test = ml.train_test_split_data(
    X,
    y,
    test_size=0.2,
    stratify=True,
    random_state=42,
)

Use stratify=True for binary clinical endpoints unless there is a specific study-design reason not to.

Class Imbalance

For Random Forest:

python
class_weight = ml.compute_class_weights(y_train)

For XGBoost:

python
scale_pos_weight = ml.compute_scale_pos_weight(y_train)

These helpers are intentionally separate because sklearn and XGBoost use different imbalance APIs.

Random Forest

python
rf = ml.train_random_forest(
    X_train,
    y_train,
    X_test,
    y_test,
    class_weight=class_weight,
    n_estimators=500,
    max_depth=None,
    random_state=42,
    plot=True,
)

rf["metrics"]
rf["figures"].keys()

Returned keys include:

KeyMeaning
modelfitted RandomForestClassifier
y_predpredicted class labels on evaluation data
y_probpositive-class probabilities for binary targets
metricsdictionary of classification metrics
figuresROC, PR, calibration, confusion matrix when plot=True

For clinical reporting, preserve metrics, y_pred, y_prob, and the exact train/test split so validation can be audited later.

XGBoost

python
xgb = ml.train_xgboost(
    X_train,
    y_train,
    X_test,
    y_test,
    scale_pos_weight=scale_pos_weight,
    n_estimators=500,
    max_depth=3,
    learning_rate=0.05,
    random_state=42,
    plot=True,
)

For binary targets, Fyron defaults eval_metric to logloss. For multiclass targets, it defaults to mlogloss and multi:softprob.

Metrics

python
metrics = ml.calculate_classification_metrics(
    y_test,
    rf["y_pred"],
    rf["y_prob"],
)

Binary outputs include:

  • accuracy
  • balanced_accuracy
  • f1
  • precision
  • recall
  • sensitivity
  • specificity
  • auc
  • tn, fp, fn, tp

For clinical models, inspect sensitivity and specificity directly. Accuracy can be misleading when events are rare.

Cross-Validation

python
from sklearn.ensemble import RandomForestClassifier

cv = ml.cross_validate_model(
    RandomForestClassifier(n_estimators=300, random_state=42),
    X,
    y,
    cv=5,
    return_oof_predictions=True,
)

cv["fold_metrics"]
cv["mean_metrics"]
cv["std_metrics"]

Set return_oof_predictions=True when you want out-of-fold predictions for calibration or error analysis.

python
from sklearn.ensemble import RandomForestClassifier

grid = ml.get_default_param_grid("random_forest")

search = ml.grid_search_model(
    RandomForestClassifier(random_state=42),
    grid,
    X_train,
    y_train,
    X_val=X_test,
    y_val=y_test,
    scoring="roc_auc",
    cv=5,
    n_jobs=1,
)

search["best_params"]
search["val_metrics"]

n_jobs=1 is useful in restricted notebook, CI, or sandbox environments. Use higher parallelism on local machines when appropriate.

End-To-End Pipeline

python
result = ml.run_classification_pipeline(
    X,
    y,
    model="xgboost",
    test_size=0.2,
    handle_imbalance=True,
    use_grid_search=False,
    random_state=42,
    n_estimators=300,
    learning_rate=0.05,
    plot=True,
)

Use the pipeline helper for fast baselines. For final analyses, consider calling the lower-level functions explicitly so every preprocessing and modeling step is visible in your methods.

Feature Importance

python
importance = ml.get_feature_importance(
    rf["model"],
    feature_names=list(X.columns),
)

fig, ax = ml.plot_feature_importance(
    rf["model"],
    feature_names=list(X.columns),
    top_n=20,
)

For XGBoost, Fyron extracts booster importance scores and normalizes them when possible.

Boruta Feature Selection

python
boruta = ml.run_boruta_feature_selection(
    X_train,
    y_train,
    max_iter=100,
    random_state=42,
)

selected = boruta["selected_features"]
ranking = boruta["ranking_df"]

Boruta is an all-relevant feature-selection method. It can be useful in biomarker discovery, but it should still be validated with external or held-out data.

External Validation And Calibration

Use these helpers when a model is already trained and the next question is reliability on locked or external data.

FunctionRequiredOptionalReturns
nested_cross_validate_modelestimator, parameter grid, X, yinner/outer CV, scoringnested CV result dict
evaluate_external_validationfitted estimator, external X/ylocked threshold, positive labelmetrics dict
evaluate_locked_thresholdlabels, probabilities, thresholdpositive/negative labelsthreshold metrics dict
fit_probability_calibratorfitted estimator, calibration X/ymethodsklearn calibrator
apply_probability_calibratorcalibrator, Xpositive class indexcalibrated probabilities
model_card_summaryestimatordata, metrics, intended use, limitationsmodel-card dict
python
nested = ml.nested_cross_validate_model(
    estimator,
    {"C": [0.1, 1.0, 10.0]},
    X,
    y,
    outer_cv=5,
    inner_cv=3,
)

external = ml.evaluate_external_validation(
    fitted_model,
    X_external,
    y_external,
    threshold=0.35,
)

calibrator = ml.fit_probability_calibrator(
    fitted_model,
    X_calibration,
    y_calibration,
)

card = ml.model_card_summary(
    fitted_model,
    X=X_train,
    y=y_train,
    metrics=external["metrics"],
    intended_use="Research endpoint prediction",
)

Clinical Plotting

Model evaluation plots:

python
fig_roc, _ = ml.plot_roc_curve(y_test, rf["y_prob"])
fig_pr, _ = ml.plot_precision_recall_curve(y_test, rf["y_prob"])
fig_cal, _ = ml.plot_calibration_curve(y_test, rf["y_prob"])
fig_cm, _ = ml.plot_confusion_matrix(y_test, rf["y_pred"])

Exploratory feature plots:

python
fig_corr, _, corr = ml.plot_correlation_heatmap(
    X,
    method="spearman",
    mask_upper=True,
)

Model comparison:

python
fig_metrics, _ = ml.plot_metric_bars(
    {
        "Random Forest": rf["metrics"],
        "XGBoost": xgb["metrics"],
    },
    metrics_to_plot=["auc", "sensitivity", "specificity", "f1"],
)

Grouped clinical summaries:

python
fig_bar, _ = ml.plot_patterned_bar(
    summary_df,
    x="endpoint",
    y="rate",
    group="cohort",
    error="ci_half_width",
    title="Endpoint rates by cohort",
    annotate=True,
)

See Clinical Plotting for detailed plotting guidance.

Persistence

python
ml.save_estimator(rf["model"], "models/rf.joblib")
loaded = ml.load_estimator("models/rf.joblib")

Persist only the fitted estimator. Keep feature-generation code and schema validation separately versioned so the model can be reproduced.

Practical Checklist

  • Confirm rows represent independent patients or split by patient ID.
  • Define the endpoint before feature engineering.
  • Remove leakage variables.
  • Preserve feature names with DataFrames.
  • Use stratified splits for imbalanced endpoints.
  • Report sensitivity, specificity, AUC, and calibration when probabilities are used clinically.
  • Save both figures and metric tables for reproducibility.