Skip to Content
DocumentationCurrent packageLearners and transformations

Adding learners and transformations

mllabiome sweeps are ordinary Python configuration files. You can extend a sweep by adding scikit-learn estimators, compatible custom estimators, built-in abundance transformations, or custom transformation functions.

Adding scikit-learn learners

Return a list of (name, estimator) pairs from _build_models().

from sklearn.ensemble import ExtraTreesClassifier, RandomForestClassifier from sklearn.linear_model import LogisticRegression def _build_models(): return [ ("RF_1000_msl5", RandomForestClassifier( n_estimators=1000, min_samples_leaf=5, n_jobs=1, random_state=42, )), ("ET_500_msl3", ExtraTreesClassifier( n_estimators=500, min_samples_leaf=3, n_jobs=1, random_state=42, )), ("LR_C1_bal", LogisticRegression( C=1.0, max_iter=3000, class_weight="balanced", random_state=42, )), ]

Use stable, readable names. The learner name is written to configs.tsv, ranking tables, ensemble outputs, report tables, and explainability metadata.

Adding estimator pipelines

Any scikit-learn compatible classifier or pipeline can be used.

from sklearn.linear_model import LogisticRegression from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler def _build_models(): return [ ("Std_LR_C1", make_pipeline( StandardScaler(), LogisticRegression(C=1.0, max_iter=3000, random_state=42), )), ]

For classifiers, prefer estimators that expose predict_proba. Estimators with decision_function can also be used, but probabilistic outputs are usually better aligned with ROC-AUC, PR-AUC, ensembling, and local explanation methods.

Adding FLAML AutoML

FLAML can be included as another learner in the same list.

def _build_models(): return [ ("FLAML_600s", mll.FLAMLClassifier( time_budget=600, metric="roc_auc", n_jobs=1, random_state=42, )), ]

In the report, the AutoML strategy is strict: it is reported only when a FLAML/AutoML learner is available on raw abundances at the deepest available single rank among strain, species, and genus.

Adding custom estimators

A custom learner should follow the scikit-learn estimator interface: implement fit(X, y) and either predict_proba(X) or decision_function(X). Implement predict(X) when direct class prediction is also needed.

from sklearn.base import BaseEstimator, ClassifierMixin from sklearn.linear_model import RidgeClassifier import numpy as np class RidgeSoftmaxClassifier(BaseEstimator, ClassifierMixin): def __init__(self, alpha=1.0, random_state=42): self.alpha = alpha self.random_state = random_state def fit(self, X, y): self.model_ = RidgeClassifier(alpha=self.alpha, random_state=self.random_state) self.model_.fit(X, y) self.classes_ = self.model_.classes_ return self def decision_function(self, X): return self.model_.decision_function(X) def predict_proba(self, X): score = self.decision_function(X) score = np.asarray(score) if score.ndim == 1: score = np.column_stack([-score, score]) score = score - score.max(axis=1, keepdims=True) exp_score = np.exp(score) return exp_score / exp_score.sum(axis=1, keepdims=True) def predict(self, X): return self.classes_[np.argmax(self.predict_proba(X), axis=1)] def _build_models(): return [("RidgeSoftmax_a1", RidgeSoftmaxClassifier(alpha=1.0))]

Keep custom estimators deterministic when possible by exposing and using random_state.

Built-in transformations

Use mll.Transformation(name) for built-in abundance transformations.

def _build_count_transformations(): T = mll.Transformation return [ T("none"), T("arcsin_sqrt"), T("hellinger"), T("rank_col"), T("scikit-bio_clr"), ]

Common choices include:

TransformationTypical use
noneRaw relative abundances
arcsin_sqrtVariance-stabilising transform for relative abundances
hellingerSquare-root transformed compositional profile
rank_colFold-fitted empirical rank per feature
scikit-bio_clrCLR-style compositional transform
pairwise_logratioPairwise log-ratio representation

Adding sample-wise custom transformations

For transformations that do not learn parameters from the training fold, pass a function fn(X).

import numpy as np def log1p_transform(X): return np.log1p(X) def _build_count_transformations(): T = mll.Transformation return [ T("none"), T("custom_log1p", log1p_transform), ]

The function should preserve row order and return a numeric matrix with the same number of rows as the input.

Adding fold-fitted custom transformations

If a transformation estimates any parameter from data, fit that parameter on the training fold only. Pass a function fn(X_train, X_test) and set the third argument to True.

import numpy as np def column_zscore_from_train(X_train, X_test): mean = X_train.mean(axis=0, keepdims=True) std = X_train.std(axis=0, keepdims=True) std[std == 0] = 1.0 return (X_train - mean) / std, (X_test - mean) / std def _build_count_transformations(): T = mll.Transformation return [ T("none"), T("column_zscore", column_zscore_from_train, True), ]

Use the fold-fitted form for scaling, quantile mapping, prevalence weighting, empirical ranks, feature selection, dimensionality reduction, or any operation whose parameters depend on the observed samples. This keeps evaluation and explainability out-of-fold.

Practical checks

Before launching a large sweep, run a small configuration with one or two resolutions, transformations, and learners.

mllabiome examples/configs_sweep.py --stage evaluate

Then add the remaining models or transformations and rerun the full workflow.

Last updated on