Integrate metabolomics datasets that do not share the same measured metabolites, by grouping metabolites into shared chemical classes (through the ChEBI ontology, RefMet, or ClassyFire) and scoring each class per sample.
You give it one or more intensity tables. The stages run in memory (annotation, class mapping, scoring, then integration) and it writes two files:
sscca_class_scores_integrated.csv, an autoscaling, PCA-ready matrix: rows are chemical classes, columns are samples, with aDatasetrow and harmonised label rows on top. Run PCA or PLS-DA on it yourself.sscca_report.xlsx: a Summary sheet, an integrated Class statistics sheet (the companion to a loading plot, giving the q-value, direction, effect, and cross-dataset reproducibility per class), one metabolite-mapping sheet plus per-dataset statistics per dataset, and a Shared-classes sheet. A power-diagnostics sheet is optional.
The method needs only annotated metabolite abundances, so it applies in principle to any analytical platform that produces them, including GC-MS and NMR. The evaluation here uses MS-based datasets, and some preprocessing defaults, in particular half-minimum imputation for left-censored missingness, are motivated by MS data. On another platform, review those defaults for your data.
read_dataset -> Stage 1 (ChEBI ID annotation; only when needed)
-> Stage 2 (class mapping: chebi_dag | refmet | classyfire)
-> Stage 3 (class scoring + per-class significance test)
-> Stage 4 (integrate datasets + output scaling)
Each stage is its own sub-package under src/sscca/, with a short README
describing its steps and settings.
For SVD and kernel-PCA scoring, each class score is the first principal component of that class's metabolites. A principal component has an arbitrary sign, so the score is sign-anchored to the class-mean abundance: if the component correlates negatively with the mean, it is flipped. This makes a higher score mean higher class abundance, so the sign of a Stage-4 PCA loading is interpretable as direction. The combined z-score method needs no such step.
In the ChEBI ontology a metabolite can land on both a parent class and one of its
child classes, which is redundant. When such parent-child pairs overlap by more
than 30%, ssCCA flags them: it keeps both classes and records the overlap in
a Redundancy_note column on the metabolite-mapping sheet, plus a row in the
redundancy log. Nothing is silently merged or dropped, so the output stays
auditable. This applies only to the ChEBI DAG engine; if no overlapping pairs are
found, no flag appears. RefMet and ClassyFire classes have no parent-child
hierarchy, so there is nothing to flag.
pip install -e . # from a local checkout
pip install "git+https://github.com/fritzhkh/ssCCA.git" # from GitHubOptional extras:
pip install "sscca[stats]" # scikit-posthocs, for the Kruskal-Wallis post-hoc
pip install git+https://github.com/tiganouri/ChemEquivMapper # Stage 1 annotationThe demo notebook is the fastest way in. It uses the RefMet engine, which reads the metabolite name directly and needs no ChEBI OBO download and no ChemEquivMapper, only a network connection:
jupyter notebook demo.ipynbThe same run in code:
from sscca import PipelineConfig, DatasetConfig, run
cfg = PipelineConfig(
class_source="refmet", refmet_class_level="main_class",
primary_factor="Diagnosis",
datasets=[DatasetConfig(name="ST000923",
path="examples/ibd/ST000923_RefMet.csv")],
)
result = run(cfg)
print(result.stacked_csv, result.report)To use the ChEBI ontology engine (the main method) and see all four stages, including name-to-ChEBI resolution, work through the tutorial notebook. It runs online in Google Colab (no downloads), or locally with the full ChEBI OBO:
You can also run the bundled config from the command line:
sscca run --config examples/ibd/config_ibd.yaml
sscca pca --matrix ibd_output/sscca_class_scores_integrated.csv # quick PCA on the matrixexamples/ibd/ bundles three public inflammatory-bowel-disease stool datasets as
name-annotated tables:
| Accession | Repository | Reference |
|---|---|---|
| ST000923 | Metabolomics Workbench | Lloyd-Price et al. (2019), Nature 569, 655–662 |
| ST001000 | Metabolomics Workbench | Franzosa et al. (2019), Nature Microbiology 4, 293–305 |
| MTBLS8713 | MetaboLights | Ning et al. (2023), Nature Communications 14, 7135 |
demo.ipynb runs them through the RefMet engine and ends on three
views of the integrated matrix: a PCA scores plot (samples by diagnosis), the PC1
loadings (the classes driving the disease trend), and the class-statistics table
that says which of those classes are significant. examples/tutorial.ipynb
repeats this with the full ChEBI DAG method and shows the RefMet and ClassyFire
engines as one-line swaps. Both notebooks run locally and are shipped without
saved outputs, because the ChEBI DAG path downloads the ChEBI OBO on first use
and the engines query web services.
Column A is always the metabolite name. Metadata rows (for example Diagnosis)
use - in the second column. The reader detects which of these three layouts a
file uses.
Format 1: name + RefMet name.
| Metabolite_name | RefMet_name | SM-5QVZD | SM-5U5DE |
|---|---|---|---|
| Diagnosis | - | CD | CD |
| 1-methylnicotinamide | 1-Methyl nicotinamide | 254110 | 196776 |
Format 2: name + samples (no second annotation column).
| Metabolite_name | S1 | S2 | S3 |
|---|---|---|---|
| Diagnosis | CD | CD | Control |
| Glycine | 812.3 | 774.0 | 690.1 |
Format 3: name + ChEBI ID already present.
| Metabolite_name | ChEBI_ID | S1 | S2 |
|---|---|---|---|
| Diagnosis | - | CD | Control |
| Glycine | 15428 | 812.3 | 690.1 |
When class_source is refmet or classyfire, the name is used directly and
Stage 1 is skipped. When it is chebi_dag, ChEBI IDs are required, and
chebi_id_source chooses where they come from:
pre-annotated(format 3): read from the file'sChEBI_IDcolumn, no lookup.api: resolve names online via the EBI ChEBI search API (no ChemEquivMapper, no OBO download). Convenient and fully online; resolves by best match.chemequivmapper: run the full ChemEquivMapper name-to-ChEBI cascade (needs theChemEquivMapperpackage and a ChEBI OBO). The most complete resolution.
A legacy UpperClass_ChEBI column is tolerated and ignored.
| Key | Values | Meaning |
|---|---|---|
class_source |
chebi_dag | refmet | classyfire |
mapping engine |
chebi_ontology_source |
obo | api |
ChEBI DAG only: download the OBO (obo, default, fast reruns) or query the EBI API per ID (api, no big download, slower first run) |
chebi_depth |
integer (default 11) |
minimum DAG depth (a floor; metabolites keep deeper natural depths) |
chebi_max_step |
2-5 (default 5; the example config uses 2) |
Stage 1 cascade depth. 2 = RefMet + ChEBI name lookup (no extra resources). 3-5 resolve more names but need KEGG/Reactome resources ChemEquivMapper does not reliably bundle. |
chebi_pipeline_mode |
dynamic | integer |
ChEBI DAG mode (single dataset uses integer 2) |
refmet_class_level |
super_class | main_class | sub_class |
RefMet only |
classyfire_class_level |
kingdom...subclass | deepest | smart |
ClassyFire only |
imputation_method |
half_min | median | minimum |
left-censored missing values |
zscore_scope |
per_class | global |
|
scoring_method |
svd | zscore | kpca |
|
stat_test |
mwu | kw | anova |
per-class significance test |
q_threshold |
0.05 | 0.1 |
BH-FDR significance cutoff |
power_diagnostics |
true | false |
optional per-dataset power-diagnostics sheet |
join_strategy |
inner | outer |
inner keeps shared classes (ssCCA) |
output_scaling |
autoscaling | meancentering | none |
final scaling of the matrix |
primary_factor, label_map, study_title |
grouping factor, metadata harmonisation, report title | |
output_dir, cache_dir |
outputs; OBO and API cache |
Single-dataset behaviour is automatic: RefMet and ClassyFire keep classes with at least two metabolites, and the ChEBI DAG switches to integer mode (default 2).
The ChEBI OBO is downloaded to cache_dir on first use of the ChEBI DAG
engine, and the RefMet, ClassyFire, and PubChem responses are cached there too.
These need internet the first time. The OBO is not shipped in the repository
because it exceeds GitHub's file-size limit; the engine fetches it from EBI and
reuses the cached copy on later runs.
To skip the OBO download entirely, set chebi_ontology_source: api. The ChEBI
DAG is then built by querying the EBI ChEBI API for each metabolite's ontology
parents and walking up to the roots, assembling only the sub-graph the data
touches (cached in cache_dir). This avoids the large download, but the first
run makes many small requests and is slower and dependent on the live service.
For a fast, reproducible rerun, prefer the OBO (obo); both paths build the same
ontology structure.
If you use ssCCA, please cite it via the "Cite this repository" button on
GitHub (see CITATION.cff). See REFERENCES.md for the method citations and the
data sources, and CONTRIBUTING.md to set up a development environment.
BSD 3-Clause. See LICENSE.