Skip to content

Latest commit

 

History

History
223 lines (171 loc) · 10.2 KB

File metadata and controls

223 lines (171 loc) · 10.2 KB

ssCCA: single-sample Chemical Class Analysis

tests licence: BSD-3-Clause python

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:

  1. sscca_class_scores_integrated.csv, an autoscaling, PCA-ready matrix: rows are chemical classes, columns are samples, with a Dataset row and harmonised label rows on top. Run PCA or PLS-DA on it yourself.
  2. 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.

Scope: which platforms

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.

Pipeline

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.

How a class becomes a score (and its direction)

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.

Redundancy handling (ChEBI DAG only)

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.

Install

pip install -e .                                          # from a local checkout
pip install "git+https://github.com/fritzhkh/ssCCA.git"   # from GitHub

Optional extras:

pip install "sscca[stats]"     # scikit-posthocs, for the Kruskal-Wallis post-hoc
pip install git+https://github.com/tiganouri/ChemEquivMapper   # Stage 1 annotation

Quick start

The 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.ipynb

The 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:

Open In Colab

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 matrix

Worked example: three IBD datasets

examples/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.

Input formats (auto-detected)

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's ChEBI_ID column, 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 the ChemEquivMapper package and a ChEBI OBO). The most complete resolution.

A legacy UpperClass_ChEBI column is tolerated and ignored.

Config

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).

First-run downloads

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.

Citing

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.

Licence

BSD 3-Clause. See LICENSE.