multi-omics-integration/mofa-integration/SKILL.md
Discovers shared and view-specific latent factors across bulk multi-omics blocks (RNA-seq, proteomics, methylation) on a common sample axis with MOFA2's unsupervised Bayesian group factor model, then attributes per-view variance explained and interprets signed factor weights. Covers why a factor is an unsupervised axis of variance and not a pathway, why a factor that correlates with batch is a batch factor, why the per-view variance-explained table is the primary read-out rather than p-values, why raw counts in a Gaussian view make factor 1 the library-size factor, and why MOFA2 handles missing omics-per-sample natively. Use when integrating two or more bulk omics to find joint axes of variation, choosing factor count, labeling factors against metadata, or running enrichment on factor weights. For supervised discriminant integration see mixomics-analysis; for the method decision see integration-design; for single-cell see single-cell/multimodal-integration; for enrichment see pathway-analysis/gsea.
npx skillsauth add GPTomics/bioSkills bio-multi-omics-mofa-integrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Reference examples tested with: MOFA2 1.12+ (Bioconductor), mofapy2 0.7+, muon 0.1+.
Before using code patterns, verify installed versions match. If versions differ:
packageVersion('MOFA2') then ?function_name to verify parameterspip show mofapy2 muon then help(module.function) to check signaturesIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
MOFA2 is R/Bioconductor and trains through the Python mofapy2 backend via basilisk/reticulate; the trained model serializes to an .hdf5 file whose schema is version-coupled. The R defaults differ from the muon Python defaults (spikeslab_weights, ard_factors, seed 42 vs 1) - confirm per ecosystem.
"Find shared variation across my omics layers" -> Learn unsupervised latent factors that decompose variation into shared and view-specific axes - because the factors are found WITHOUT the phenotype, so a factor need not separate the groups of interest, and the variance-explained table is the read-out.
create_mofa(data_list) -> prepare_mofa() -> run_mofa()mofapy2 for training, muon / mofax for downstreamScope: unsupervised cross-block factor modeling of bulk omics, variance decomposition, and signed-weight interpretation. Supervised discriminant integration -> mixomics-analysis. The method-selection decision -> integration-design. Per-omic normalization / HVG / transform-to-Gaussian -> data-harmonization. Enrichment mechanics -> pathway-analysis/gsea. Single-cell multimodal MOFA+ -> single-cell/multimodal-integration.
MOFA is PCA generalized to multiple omics: it returns factor scores (Z), per-view loadings (W), and a variance decomposition (R^2 per factor per view) that says which axis is shared across modalities and which is view-specific. That decomposition is the output. Three properties every misuse forgets:
correlate_factors_with_covariates against batch/depth/plate and exclude technical factors from biological interpretation. A factor that tracks run date is a batch factor, not confounded biology.| Tool | Citation | Output | When | |------|----------|--------|------| | MOFA / MOFA+ | Argelaguet 2018 Mol Syst Biol 14:e8124; Argelaguet 2020 Genome Biol 21:111 | continuous factors + R^2 per (factor,view) | interpretable variance-attributed axes; missing-data tolerant; the default factor model | | MEFISTO (within MOFA2) | Velten 2022 Nat Methods 19:179 | smooth factors along a covariate | samples carry a spatial or temporal coordinate | | iCluster / iClusterPlus / iClusterBayes | Shen 2009 Bioinformatics 25:2906; Mo 2018 Biostatistics 19:71 | a hard sample clustering | discrete SUBTYPES (a partition) rather than axes | | JIVE | Lock 2013 Ann Appl Stat 7:523 | explicit joint + per-view-individual + residual subspaces | quantify and separate joint vs dataset-specific structure | | MCIA | Meng 2016 J Proteome Res 15:755 (moCluster); omicade4 | co-inertia ordination across views | fast exploratory co-structure / visual pathway exploration | | mixOmics DIABLO | Singh 2019 Bioinformatics 35:3055 | supervised discriminant signature | the OUTCOME must drive the projection -> mixomics-analysis |
| Deliverable | Recommended | Why |
|-------------|-------------|-----|
| Interpretable axes attributed across views, hypothesis generation | MOFA / MOFA+ | continuous factors + variance decomposition; native missing-data |
| Discrete patient subtypes (a partition) | iCluster / iClusterPlus | the output IS a hard clustering -> integration-design, similarity-network |
| Explicitly separate joint from view-specific structure | JIVE | decomposition is joint + individual + residual by construction |
| Samples have a spatial/temporal coordinate | MEFISTO (within MOFA2) | GP prior gives smooth factors along the covariate |
| The outcome/class must drive the projection | -> mixomics-analysis (DIABLO) | supervised; the phenotype is in the model |
| Some samples missing a whole omic | MOFA (handles it natively) | the likelihood ignores missing entries; do not impute a block |
| Single-cell CITE-seq / Multiome MOFA+ | -> single-cell/multimodal-integration | single-cell object plumbing and stochastic inference |
| Enrich the factor weights | -> pathway-analysis/gsea | GSEA/ORA mechanics; here only run_enrichment on weights |
Goal: Get each omic into the orientation and distribution MOFA assumes, so the factors reflect biology rather than measurement scale.
Approach: Each view must be features-by-samples (the transpose of the usual samples-by-features matrix), per-omic normalized and variance-stabilized upstream, and HVG-filtered so feature counts are within an order of magnitude across views. The per-omic transform and HVG selection are owned by data-harmonization.
library(MOFA2)
common <- Reduce(intersect, list(colnames(rna), colnames(prot), colnames(meth))) # shared samples; mosaic samples may be kept, see below
data_list <- list(RNA=rna[, common], Protein=prot[, common], Methylation=meth[, common]) # each features x samples, already transformed
mofa <- create_mofa(data_list)
plot_data_overview(mofa) # shows views, samples, and the missing-data pattern (grey = missing, tolerated)
MOFA tolerates missing samples in a view (it ignores missing entries in the likelihood), so a mosaic cohort can be passed directly rather than intersected to complete cases - this is a core reason to choose MOFA when data is incomplete.
Goal: Configure a factor model whose count and likelihoods match the data and the sample size, then train by variational inference.
Approach: Over-specify the factor count and let ARD prune, transform counts to a Gaussian likelihood rather than using Poisson, set a seed for reproducibility, and write the model to a versioned .hdf5.
data_opts <- get_default_data_options(mofa) # scale_views=FALSE, center_groups=TRUE
model_opts <- get_default_model_options(mofa) # num_factors=10, likelihoods='gaussian'
train_opts <- get_default_training_options(mofa) # convergence_mode='fast', drop_factor_threshold=-1, stochastic=FALSE, seed=42
model_opts$num_factors <- 15 # over-specify; ARD prunes inactive factors
model_opts$likelihoods <- c(RNA='gaussian', Protein='gaussian', Methylation='gaussian') # transform counts upstream, prefer gaussian
train_opts$drop_factor_threshold <- 0.01 # drop factors explaining <1% variance in ALL views
data_opts$scale_views <- TRUE # equalize per-view variance if feature counts cannot be balanced by filtering
mofa <- prepare_mofa(mofa, data_options=data_opts, model_options=model_opts, training_options=train_opts)
mofa <- run_mofa(mofa, outfile=file.path(tempdir(), 'model.hdf5'), use_basilisk=TRUE)
Goal: Identify which factors are shared across views and which are view-specific before interpreting any of them.
Approach: The R^2 per factor per view is the central result: a factor active in two or more views is a shared axis, a factor active in one view is view-specific. Inspect this table first; factors with near-zero R^2 everywhere are noise.
var_exp <- get_variance_explained(mofa) # $r2_total, $r2_per_factor <- the central output
plot_variance_explained(mofa) # heatmap: factors x views
plot_variance_explained(mofa, plot_total=TRUE) # total variance explained per view
Goal: Earn a biological label for a factor instead of asserting one from its existence.
Approach: Attach metadata after fitting (it is never used to train), correlate each factor with both biological and technical covariates, and exclude any factor that tracks batch/depth/plate from biological interpretation. Then run enrichment on the signed weights, treating the two poles of the axis separately.
md <- metadata[unlist(samples_names(mofa)), ]
md$sample <- rownames(md) # samples_metadata<- requires a literal 'sample' column
samples_metadata(mofa) <- md
correlate_factors_with_covariates(mofa, covariates=c('condition', 'batch', 'depth')) # a factor that correlates with batch IS a batch factor
# enrichment per sign - the two poles are biological opposites along one axis
up <- run_enrichment(mofa, view='RNA', feature.sets=msig_binary_matrix, factors=1:5, sign='positive')
down <- run_enrichment(mofa, view='RNA', feature.sets=msig_binary_matrix, factors=1:5, sign='negative')
Multi-group MOFA (group in create_mofa) partitions samples so factor activity can differ across groups while weights stay shared - it asks "do the same axes operate within each group?" It is NOT batch correction and putting the phenotype in as a group does not make MOFA supervised. For samples with a spatial or temporal coordinate, MEFISTO (a GP-prior mode of MOFA2, via mefisto_options + set_covariates) learns factors that vary smoothly along that covariate.
Trigger: "factor 1 represents immune activation" from the factor's existence. Mechanism: a factor is a direction of covariation the ARD prior kept; it has no intrinsic meaning. Symptom: a biological story with no enrichment, no covariate correlation, no replication. Fix: report the R^2 footprint, the signed-weight enrichment, and the known-covariate correlation; call it hypothesis-generating until validated.
Trigger: interpreting a top factor without a technical-covariate check. Mechanism: MOFA captures the largest variance, and unregressed batch is often largest. Symptom: the top factor tracks run date / plate better than phenotype. Fix: regress known batch out upstream (before HVG selection); always correlate_factors_with_covariates and exclude technical factors.
Trigger: reporting the one of K factors that splits the groups. Mechanism: with enough factors one will split any grouping by chance. Symptom: a factor-phenotype association that does not replicate. Fix: pre-specify the test or correct for K; validate the chosen factor on a held-out cohort.
Trigger: feeding un-transformed RNA counts to a gaussian likelihood. Mechanism: counts are heavy-tailed and mean-variance-coupled, so high-count genes carry the most raw variance. Symptom: factor 1 tracks library size / housekeeping genes. Fix: normalize and variance-stabilize per view upstream (data-harmonization), then use gaussian; reserve bernoulli for genuine binaries.
Trigger: a 20k-gene view beside a 50-feature view, unequalized. Mechanism: bigger modalities are overrepresented in the factors. Symptom: every factor describes mostly the large view. Fix: HVG-filter to comparable feature counts and/or set scale_views=TRUE (which changes the R^2 interpretation to within-view relative variance).
Trigger: num_factors=20 with 30 samples. Mechanism: factor analysis needs sample size (the package floor is >15). Symptom: factors that fit noise and split the cohort by accident. Fix: request fewer factors, prune with drop_factor_threshold, confirm robustness across seeds, validate out-of-sample.
Trigger: building a story on one training run, especially with stochastic=TRUE. Mechanism: PCA init makes standard VI mostly reproducible, but local optima and stochastic inference still vary. Symptom: a headline factor that does not reappear on a retrain. Fix: set the seed AND retrain with a different seed/factor count; a robust axis recurs with factor-score correlation near 1.
| Threshold | Source | Rationale |
|-----------|--------|-----------|
| num_factors over-specified, ARD prunes | Argelaguet 2018 Mol Syst Biol 14:e8124 | a factor never allocated cannot be recovered; default cap is N-dependent (5 if N<=25, 15 if N<=1000) |
| drop_factor_threshold ~0.01 | MOFA2 docs | drop factors explaining <1% variance in ALL views; default -1 keeps all |
| Sample size floor > 15 | MOFA2 FAQ | factor analysis is only useful with adequate n; tens of samples overfit a generous factor count |
| scale_views=TRUE only when filtering cannot equalize | MOFA2 FAQ | bigger modalities are overrepresented; scaling equalizes per-view variance but changes R^2 reading |
| Transform counts to gaussian rather than poisson | MOFA2 FAQ | non-gaussian likelihoods are less-accurate approximations; transform if it can be defended |
| Robustness: factor recurs across seeds with |r|~1 | Argelaguet 2018 Mol Syst Biol 14:e8124 | a factor that does not reappear on retrain is fragile noise |
| Error / symptom | Cause | Solution |
|-----------------|-------|----------|
| create_mofa orientation error or nonsense factors | views passed samples-by-features | transpose to features-by-samples |
| Factor 1 tracks sequencing depth | raw counts into a gaussian view | normalize + variance-stabilize per view first |
| Every factor describes one omic | variance imbalance | HVG-filter to comparable feature counts / scale_views=TRUE |
| Model will not converge / factors NaN | unscaled blocks or a constant feature | center/scale; drop zero-variance features upstream |
| A factor loads almost entirely on one sample | an outlier hijacking a factor | inspect and remove the outlier; refit |
| Headline factor vanishes on rerun | seed fragility / stochastic inference | set seed; confirm the factor recurs across retrains |
tools
End-to-end CLIP-seq pipeline from FASTQ to ENCODE-compliant binding sites, single-nucleotide crosslink maps, annotation, motifs, and (optionally) differential binding. Use when running the full Yeo lab eCLIP / iCLIP / iCLIP2 / iCLIP3 / irCLIP / PAR-CLIP analysis with SMInput control, protocol-specific UMI extraction, ENCODE STAR parameters, CLIPper or Skipper peak calling with stringent log2 FC and -log10 p thresholds, IDR rescue and self-consistency QC, and downstream motif registration with mCross or PEKA.
development
Detect, date, and contextualize whole-genome duplication (WGD / paleopolyploidy) events using wgd v2 (Chen et al 2024), KsRates (Sensalari 2022 substitution-rate-corrected Ks dating), DupGen_finder (Qiao 2019), MAPS (Li 2018 phylogenomic), POInT (Conant 2008 ordered-block), SLEDGe (2024 ML-based), Whale.jl (Bayesian DL+WGD), and synteny-anchored paranome construction. Use when identifying ancient polyploidy from Ks distributions and synteny block analysis, positioning WGD events relative to speciation, distinguishing tandem from segmental from WGD duplications, dating the 2R/3R vertebrate / fish / salmonid WGDs, building paranome and Ks-age mixture models, applying KsRates substitution-rate correction across lineages, or testing alternative biased-fractionation / dosage-balance models post-WGD.
tools
Build whole-genome alignments using Progressive Cactus (Armstrong 2020 reference-free clade-level WGA), Minigraph-Cactus (Hickey 2024 pangenome-aware), LASTZ chain/net (UCSC pipeline), MUMmer4 (Marçais 2018 pairwise), minimap2 -x asm5/10/20 (Li 2018 fast pairwise), AnchorWave (Song 2022 WGD-aware), and Mauve / progressiveMauve (bacterial). Operates the HAL toolkit (Hickey 2013) for downstream extraction including halSynteny, halLiftover, halBranchMutations, and hal2maf. Use when constructing multi-species alignments for comparative-annotation projection (TOGA), synteny detection, conservation analyses (phyloP / PhastCons), or pangenome graph construction; selecting between reference-free (Cactus) and reference-anchored (LASTZ chains/nets) approaches; tuning sensitivity for closely vs distantly related genomes; or producing HAL files for genome-wide downstream tools.
development
Detect syntenic blocks and structural rearrangements between genomes using MCScanX (Wang 2012), JCVI/MCScan (Tang 2008 Python), GENESPACE (Lovell 2022) for orthology-anchored riparian visualization, SyRI for structural variation, AnchorWave for sequence-level synteny, i-ADHoRe 3.0 for highly diverged species, SynNet for synteny networks, and ntSynt for multi-genome macrosynteny. Use when identifying collinear gene blocks across species, distinguishing macrosynteny from microsynteny, detecting inversions/translocations/duplications, anchoring orthology in WGD lineages, producing publication riparian plots, computing synteny block age via Ks (cross-references whole-genome-duplication), or running synteny-aware ortholog inference in polyploids.