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:
| Transformation | Typical use |
|---|---|
none | Raw relative abundances |
arcsin_sqrt | Variance-stabilising transform for relative abundances |
hellinger | Square-root transformed compositional profile |
rank_col | Fold-fitted empirical rank per feature |
scikit-bio_clr | CLR-style compositional transform |
pairwise_logratio | Pairwise 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 evaluateThen add the remaining models or transformations and rerun the full workflow.