Code structure¶
The pipeline at a glance¶
input/ config.yaml
├─ counts.csv.gz ───┐ │
├─ taxa.csv.gz ───┼──────┘
└─ meta.csv ───┘
│
▼
[1] Import + aggregate counts summed to phylum … species, or kept as
sequence-hashed ASV ids / SGBs → feature_name_map.csv
│
▼
[2] Normalise (per sample) relative abundance → log10, fixed pseudocount
full feature space saved → normalization.json,
reference_features.csv
│
▼
[3] Outer folds grouped by dataset.group; inside every fold (nested):
├─ feature selection information-gain filter → recursive elimination
├─ tuning grid, Hyperband or random search per learner, two measures
└─ fit + score on the held-out fold → metrics/fold_metrics.csv,
lodo_metrics.csv,
oof_predictions.csv
│
▼
[4] Final models fitted before the outer folds run; the same selection + tuning, once, on all
training data → <method>_tuned_learner.rds
│
▼
[5] Test sets internal (test_split) and external (input/test_*):
align → normalise → predict → score → metrics/*test*.csv
│
▼
[6] Importance SHAP on the best final model → metrics/shap_*.csv
scripts/run_multi_tax_levels.R runs [1]–[6] once per level listed in
dataset.tax_level and stacks the metrics into
<date>_<id>_<version>_multi_tax_results/.
Repository layout¶
Entries marked ● are also copied into the container image, under /work; the rest
stay in the repository.
.
├── analysis/tabflux.qmd ● # the notebook: every step, one taxonomic level per render
├── config.yaml ● # template configuration for your own data
├── config_cfmd_demo.yaml ● # the container default: an 18-dataset, 891-sample slice of the category run
├── config_cfmd_category.yaml ● # the public multi-class run: food category, grouped CV
├── R/ ●
│ ├── tabflux_config_helpers.R # reads config.yaml, fills defaults, translates renamed keys
│ ├── tabflux_data_helpers.R # aggregation, sequence-hashed ids, normalisation, alignment
│ ├── tabflux_cfmd_helpers.R # a public cFMD release -> the three input files
│ ├── tabflux_selection_helpers.R # feature selection on a training task
│ ├── tabflux_training_helpers.R # learners, search spaces, tuners, the selection rule
│ ├── tabflux_evaluation_helpers.R # test split, outer folds, the nested outer loop
│ ├── tabflux_scoring_helpers.R # metric tables from mlr3 predictions, per-group tables
│ ├── tabflux_prediction_helpers.R # pooled predictions, bootstrap intervals, curves, dumbbells
│ ├── tabflux_summary_helpers.R # the group × rank table and figure of the wrapper
│ └── tabflux_figure_helpers.R # PNG + SVG copies of the report figures; the per-class SHAP figure
├── scripts/ ●
│ ├── run_multi_tax_levels.R # renders the notebook once per level, stacks the metrics
│ └── make_cfmd_comparison.R # the learner-comparison tables and figures, from a run folder
├── tests/ ● # plain-script tests: Rscript tests/test_<helpers>.R
├── conda/ ● # workstation environments; install_r_packages.R also builds the image
├── README.md, CHANGELOG.md, CITATION.cff, LICENSE, NOTICE ● # overview, releases, citation, licence
├── Dockerfile # the image: R 4.5 + Python + TabPFN, GPU-ready
├── .Renviron.example # TabPFN and token variables, to copy into ~/.Renviron
├── .gitignore, .dockerignore # what git and the image build leave out
├── .github/workflows/ # CI (image build + cFMD smoke test) and the docs deploy
├── .devcontainer/ # VS Code / Positron: the editor inside the image (docs: Chunk by chunk)
├── assets/logo.svg # the README logo
├── docs/, mkdocs.yml # this site
└── input/ # where your three files go; only README.md ships
The two cFMD configurations¶
Six keys differ (table below). Everything else is the same: tax_level: SGB,
group: dataset, outer: cv, nested, min_samples_per_dataset: 10,
min_samples_per_class: 50, smote: none, calibration: none,
prior_correction: true, and TabPFN's SHAP settings (25 draws, 100 rows).
| key | config_cfmd_demo.yaml |
config_cfmd_category.yaml |
|---|---|---|
methods.pick |
tabpfn, ranger |
tabpfn, ranger, xgboost |
input.cfmd.datasets |
18 listed datasets | [], every dataset |
evaluation.outer_folds |
3 | 5 |
importance.shap_max_rows |
100 | 300 |
execution.future_globals_max_gb |
2 | 4 |
dataset.version |
demo |
category |
- The result: 891 samples in 4 categories, about 34 min; against 3,680 samples in 7 categories, about 1 h 37 min, on the workstation described in running it.
shap_max_rowsapplies when the best learner is not TabPFN. For TabPFN both usetabpfn_shap_max_rows: 100.future_globals_max_gbcaps the data sent to each parallel worker.
The notebook, section by section¶
| section | what it does |
|---|---|
| Project Setup | finds the repository root; every path is built from it |
| Methods and Libraries | reads the config, decides which learners run, binds the TabPFN Python runtime |
| Set Parameters | turns every config value into a validated R variable; names the run folder and the report |
| Import Data | reads the three files, aggregates to the requested level, saves the name map |
| Preprocess Data | drops small classes; optional subsampling for test runs; prints the class sizes and, with a grouping column, a group × class table |
| Inspect Data | ordination of the samples (PCA and t-SNE): do the classes separate at all? |
| Normalize Abundances | per-sample relative abundance and log; prints the spread of sequencing depth, overall and per group; saves the settings |
| Define Task | builds the mlr3 task, reserves the internal test set, builds the outer folds |
| Prepare Modeling Inputs | the preprocessing graph every learner sits behind |
| Feature Selection | selection on the full training data, for the final models |
| Train models | tuning and refit of the final models, cached as .rds |
| Benchmark | the outer loop: selection and tuning inside every fold; per-fold and per-group metrics |
| Prediction | the internal test set, scored by the final models |
| Performance Comparison, Save Metrics | the tables that leave the run folder |
| External Test Set | your independent samples, predicted and scored |
| Feature Importance | SHAP on the best final model |
How the wrapper drives the notebook¶
The wrapper writes a one-level temporary configuration per taxonomic level, hands it to
the child render through the TABFLUX_CONFIG environment variable, and stops at the
first level that fails. Only when every level has rendered does it stack the per-level
metrics and draw the group × rank figure. Both arguments are optional:
Rscript scripts/run_multi_tax_levels.R [config.yaml] [analysis/tabflux.qmd]
Every render writes one run folder,
<date>_<id>_<version>_target_<target>_tax_<level>_saved_learners/, in the project root
or under output.dir when set, which is created when missing (the container writes to
/work/out). The HTML report is titled from output.report_title, or "Predicting