HuMMANet is a Bioconductor experiment data package for
paired human microbiome-metabolome studies. It provides a unified
interface to curated study-level species profiles, original and
harmonized metabolite profiles, matched sample metadata, and
study-specific annotation resources, with study data distributed through
ExperimentHub.
The central motivation for the package is to provide a reusable resource for paired study species and metabolite data together with supporting annotations, so that users can work with these studies in a consistent and reproducible way. Rather than manually assembling heterogeneous files from individual studies, users can access matched tables and annotation resources through a common API in the Bioconductor ecosystem.
Typical use cases for HuMMANet include:
Each study bundle can include the following modalities:
MetadataProfile: sample-level study metadata, including
sample identifiers, subject or participant annotations, clinical
covariates, longitudinal labels, body site information, and other
variables needed to interpret the study.taxaAbundanceProfile: the curated microbial abundance
table for a study, aligned to the study samples and representing taxa or
related microbial features.OriginalMetaboliteProfile: the curated metabolomics
matrix prior to HuMMANet harmonization, preserving the original
study-specific metabolite feature space.harmonizedMetaboliteProfile: the HuMMANet-processed
metabolomics matrix after harmonization, designed to support more
consistent comparisons across studies.metaboliteIdentifierMapping: metabolite identifier
mapping tables that link HuMMANet identifiers to external resources such
as HMDB, PubChem, KEGG, ChEBI, structural descriptors, and standardized
metabolite names.microbialProducerAnnotations: curated annotations
linking metabolites to potential microbial producers or species-level
producer evidence.metaboliteDiseaseAssociations: curated disease
associations for metabolites represented in a study.metabolitePathwayAnnotations: curated pathway
annotations for study metabolites.drugBankSimilarityMatches: DrugBank similarity matches
for study metabolites.drugCentralSimilarityMatches: DrugCentral similarity
matches for study metabolites.Each study is represented as a MultiAssayExperiment
object:
colData() stores the MetadataProfileexperiments() stores assay-level containers for
taxaAbundanceProfile,
OriginalMetaboliteProfile, and
harmonizedMetaboliteProfilemetadata()This representation is useful because it keeps the paired modalities
for a study synchronized through a shared sample-level metadata table
while still allowing each assay to preserve its own feature space and
feature annotations. For example, microbial taxa and metabolites do not
share the same rows, but they do share the same study samples.
MultiAssayExperiment is designed exactly for this
setting.
Within each study object:
taxaAbundanceProfile is stored as a
SummarizedExperiment containing a taxa-by-sample abundance
matrix.OriginalMetaboliteProfile is stored as a
SummarizedExperiment containing the original curated
metabolite feature table for the study.harmonizedMetaboliteProfile is stored as a
SummarizedExperiment containing the harmonized metabolite
table together with feature-level identifier annotations in
rowData().metadata() because they are annotation tables rather than
sample-by-feature assay matrices.The package is structured as a lightweight ExperimentHub
interface package. Large study bundles live outside the source tarball,
while the package ships the accessor functions, metadata, and
documentation needed to discover and load those resources
reproducibly.
Install from Bioconductor:
if (!requireNamespace("BiocManager", quietly = TRUE)) {
install.packages("BiocManager")
}
BiocManager::install("HuMMANet")Install the development version from GitHub:
library(HuMMANet)
library(MultiAssayExperiment)
#> Loading required package: SummarizedExperiment
#> Loading required package: MatrixGenerics
#> Loading required package: matrixStats
#>
#> Attaching package: 'MatrixGenerics'
#> The following objects are masked from 'package:matrixStats':
#>
#> colAlls, colAnyNAs, colAnys, colAvgsPerRowSet, colCollapse,
#> colCounts, colCummaxs, colCummins, colCumprods, colCumsums,
#> colDiffs, colIQRDiffs, colIQRs, colLogSumExps, colMadDiffs,
#> colMads, colMaxs, colMeans2, colMedians, colMins, colOrderStats,
#> colProds, colQuantiles, colRanges, colRanks, colSdDiffs, colSds,
#> colSums2, colTabulates, colVarDiffs, colVars, colWeightedMads,
#> colWeightedMeans, colWeightedMedians, colWeightedSds,
#> colWeightedVars, rowAlls, rowAnyNAs, rowAnys, rowAvgsPerColSet,
#> rowCollapse, rowCounts, rowCummaxs, rowCummins, rowCumprods,
#> rowCumsums, rowDiffs, rowIQRDiffs, rowIQRs, rowLogSumExps,
#> rowMadDiffs, rowMads, rowMaxs, rowMeans2, rowMedians, rowMins,
#> rowOrderStats, rowProds, rowQuantiles, rowRanges, rowRanks,
#> rowSdDiffs, rowSds, rowSums2, rowTabulates, rowVarDiffs, rowVars,
#> rowWeightedMads, rowWeightedMeans, rowWeightedMedians,
#> rowWeightedSds, rowWeightedVars
#> Loading required package: GenomicRanges
#> Loading required package: stats4
#> Loading required package: BiocGenerics
#> Loading required package: generics
#>
#> Attaching package: 'generics'
#> The following objects are masked from 'package:base':
#>
#> as.difftime, as.factor, as.ordered, intersect, is.element, setdiff,
#> setequal, union
#>
#> Attaching package: 'BiocGenerics'
#> The following objects are masked from 'package:stats':
#>
#> IQR, mad, sd, var, xtabs
#> The following object is masked from 'package:utils':
#>
#> data
#> The following objects are masked from 'package:base':
#>
#> anyDuplicated, aperm, append, as.data.frame, basename, cbind,
#> colnames, dirname, do.call, duplicated, eval, evalq, Filter, Find,
#> get, grep, grepl, is.unsorted, lapply, Map, mapply, match, mget,
#> order, paste, pmax, pmax.int, pmin, pmin.int, Position, rank,
#> rbind, Reduce, rownames, sapply, saveRDS, scale, sequence, table,
#> tapply, transform, unique, unsplit, which.max, which.min
#> Loading required package: S4Vectors
#>
#> Attaching package: 'S4Vectors'
#> The following object is masked from 'package:utils':
#>
#> findMatches
#> The following objects are masked from 'package:base':
#>
#> expand.grid, I, unname
#> Loading required package: IRanges
#> Loading required package: Seqinfo
#> Loading required package: Biobase
#> Welcome to Bioconductor
#>
#> Vignettes contain introductory material; view with
#> 'browseVignettes()'. To cite Bioconductor, see
#> 'citation("Biobase")', and for packages 'citation("pkgname")'.
#>
#> Attaching package: 'Biobase'
#> The following object is masked from 'package:MatrixGenerics':
#>
#> rowMedians
#> The following objects are masked from 'package:matrixStats':
#>
#> anyMissing, rowMedians
library(SummarizedExperiment)studies <- HuMMANet_studies()
length(studies)
#> [1] 55
head(studies)
#> [1] "BarkerTejedaTC_2024_Targeted" "BarkerTejedaTC_2024_Untargeted"
#> [3] "CarterM_2025" "ChengT_2024_F"
#> [5] "ChengT_2024_S" "ConleyT_2024"The study identifiers returned by HuMMANet_studies() are
the main entry points for downstream loading. These identifiers are also
present in the study index, which can be used to inspect available
modalities before loading a full study object.
idx <- HuMMANet_study_index()
head(
idx[, c(
"study",
"has_MetadataProfile",
"has_taxaAbundanceProfile",
"has_OriginalMetaboliteProfile",
"has_harmonizedMetaboliteProfile"
)]
)
#> study has_MetadataProfile has_taxaAbundanceProfile
#> 1 BarkerTejedaTC_2024_Targeted TRUE TRUE
#> 2 BarkerTejedaTC_2024_Untargeted TRUE TRUE
#> 3 CarterM_2025 TRUE TRUE
#> 4 ChengT_2024_F TRUE TRUE
#> 5 ChengT_2024_S TRUE TRUE
#> 6 ConleyT_2024 TRUE TRUE
#> has_OriginalMetaboliteProfile has_harmonizedMetaboliteProfile
#> 1 TRUE TRUE
#> 2 TRUE TRUE
#> 3 TRUE TRUE
#> 4 TRUE TRUE
#> 5 TRUE TRUE
#> 6 TRUE TRUEThe study index provides one row per study. In addition to the study identifier, it records availability flags and file-level information for the metadata, species, original metabolite, harmonized metabolite, and annotation resources. This is useful when deciding which studies and which data layers to retrieve for a particular analysis.
The study-loading examples below illustrate the intended user workflow through the package accessors.
study_id <- idx$study[[1]]
available <- HuMMANet_available_modalities(
study_id
)
available
#> [1] "MetadataProfile" "taxaAbundanceProfile"
#> [3] "OriginalMetaboliteProfile" "harmonizedMetaboliteProfile"
#> [5] "metaboliteIdentifierMapping" "microbialProducerAnnotations"
#> [7] "metaboliteDiseaseAssociations" "metabolitePathwayAnnotations"
#> [9] "drugBankSimilarityMatches" "drugCentralSimilarityMatches"
one_study <- HuMMANet_load_study(
study_id
)
class(one_study)
#> [1] "MultiAssayExperiment"
#> attr(,"package")
#> [1] "MultiAssayExperiment"
names(experiments(one_study))
#> [1] "taxaAbundanceProfile" "OriginalMetaboliteProfile"
#> [3] "harmonizedMetaboliteProfile"
colData(one_study)[1:3, 1:6]
#> DataFrame with 3 rows and 6 columns
#> sample_id study_name Age BMI seq_type
#> <character> <character> <numeric> <logical> <character>
#> Moth_50_T Moth_50_T BarkerTejedaTC_2024_.. 33 NA WGS
#> Moth_66_T Moth_66_T BarkerTejedaTC_2024_.. 28 NA WGS
#> Gran_22_T Gran_22_T BarkerTejedaTC_2024_.. 60 NA WGS
#> country
#> <character>
#> Moth_50_T ESP
#> Moth_66_T ESP
#> Gran_22_T ESPAt this point one_study is a
MultiAssayExperiment. The assay names returned by
experiments(one_study) correspond to the core quantitative
data layers, while colData(one_study) contains the matched
sample metadata shared across those assays.
experiments(one_study)
#> ExperimentList class object of length 3:
#> [1] taxaAbundanceProfile: SummarizedExperiment with 677 rows and 122 columns
#> [2] OriginalMetaboliteProfile: SummarizedExperiment with 14 rows and 122 columns
#> [3] harmonizedMetaboliteProfile: SummarizedExperiment with 14 rows and 122 columns
sampleMap(one_study)
#> DataFrame with 366 rows and 3 columns
#> assay primary colname
#> <factor> <character> <character>
#> 1 taxaAbundanceProfile Moth_50_T Moth_50_T
#> 2 taxaAbundanceProfile Moth_66_T Moth_66_T
#> 3 taxaAbundanceProfile Gran_22_T Gran_22_T
#> 4 taxaAbundanceProfile Gran_49_T Gran_49_T
#> 5 taxaAbundanceProfile Gran_32_T Gran_32_T
#> ... ... ... ...
#> 362 harmonizedMetaboliteProfile Inf_22_T Inf_22_T
#> 363 harmonizedMetaboliteProfile Moth_17_T Moth_17_T
#> 364 harmonizedMetaboliteProfile Gran_09_T Gran_09_T
#> 365 harmonizedMetaboliteProfile Moth_11_T Moth_11_T
#> 366 harmonizedMetaboliteProfile Inf_07_T Inf_07_Texperiments(one_study) lists the assay-level objects
stored in the study. Each of these is a
SummarizedExperiment, so standard Bioconductor accessors
such as assay(), rowData(), and
colData() can be used on the individual assays.
sampleMap(one_study) records how assay-level columns map
back to the shared study-level sample metadata.
metadata_profile <- HuMMANet_load_modality(
study_id,
"MetadataProfile"
)
dim(metadata_profile)
#> [1] 122 21
head(metadata_profile[, seq_len(min(6, ncol(metadata_profile)))])
#> sample_id study_name Age BMI seq_type country
#> Moth_50_T Moth_50_T BarkerTejedaTC_2024_Targeted 33 NA WGS ESP
#> Moth_66_T Moth_66_T BarkerTejedaTC_2024_Targeted 28 NA WGS ESP
#> Gran_22_T Gran_22_T BarkerTejedaTC_2024_Targeted 60 NA WGS ESP
#> Gran_49_T Gran_49_T BarkerTejedaTC_2024_Targeted 61 NA WGS ESP
#> Gran_32_T Gran_32_T BarkerTejedaTC_2024_Targeted 58 NA WGS ESP
#> Gran_28_T Gran_28_T BarkerTejedaTC_2024_Targeted 62 NA WGS ESPThis is equivalent to inspecting
as.data.frame(colData(one_study)), but the modality
accessor is convenient when you want the metadata table directly for
custom joins or downstream analysis.
original_profile <- HuMMANet_load_modality(
study_id,
"OriginalMetaboliteProfile"
)
class(original_profile)
#> [1] "SummarizedExperiment"
#> attr(,"package")
#> [1] "SummarizedExperiment"
dim(assay(original_profile))
#> [1] 14 122
head(rownames(original_profile))
#> [1] "GABA" "Choline" "Cadaverine" "Putrescine"
#> [5] "Tryptophan" "Aspartic Acid"
head(colnames(original_profile))
#> [1] "Moth_50_T" "Moth_66_T" "Gran_22_T" "Gran_49_T" "Gran_32_T" "Gran_28_T"The quantitative assay matrix is accessed with
assay(original_profile). As in all
SummarizedExperiment objects, rows correspond to features
and columns correspond to samples. This makes it straightforward to
extract the original study-specific metabolite matrix while preserving
its feature identities.
harmonized_profile <- HuMMANet_load_modality(
study_id,
"harmonizedMetaboliteProfile"
)
class(harmonized_profile)
#> [1] "SummarizedExperiment"
#> attr(,"package")
#> [1] "SummarizedExperiment"
dim(assay(harmonized_profile))
#> [1] 14 122
head(
rowData(harmonized_profile)[, seq_len(min(6, ncol(rowData(harmonized_profile))))]
)
#> DataFrame with 6 rows and 6 columns
#> HuMANet_ID Query_Name Database_Source InChIKey
#> <character> <character> <character> <character>
#> (+/-)-Tryptophan NA NA NA NA
#> 1,5-Diaminopentane NA NA NA NA
#> 16.89_propanoic acid NA NA NA NA
#> 20.13_butanoic acid NA NA NA NA
#> 26.93_hexanoic acid NA NA NA NA
#> 4-Aminobutyrate NA NA NA NA
#> HMDB_ID PubChem_CID
#> <character> <numeric>
#> (+/-)-Tryptophan NA NA
#> 1,5-Diaminopentane NA NA
#> 16.89_propanoic acid NA NA
#> 20.13_butanoic acid NA NA
#> 26.93_hexanoic acid NA NA
#> 4-Aminobutyrate NA NAThe harmonized metabolite profile is especially useful for
cross-study comparison because the feature layer is paired with
identifier-level annotation in rowData(harmonized_profile).
This allows users to inspect harmonized metabolite identifiers, names,
and external database mappings alongside the quantitative matrix.
taxa_profile <- HuMMANet_load_modality(
study_id,
"taxaAbundanceProfile"
)
class(taxa_profile)
#> [1] "SummarizedExperiment"
#> attr(,"package")
#> [1] "SummarizedExperiment"
dim(assay(taxa_profile))
#> [1] 677 122
head(rownames(taxa_profile))
#> [1] "Abiotrophia_defectiva" "Abiotrophia_sp_HMSC24B09"
#> [3] "Acetobacter_sp_CAG_267" "Acholeplasma_sp_CAG_878"
#> [5] "Acidaminococcus_fermentans" "Acidaminococcus_intestini"
head(colnames(taxa_profile))
#> [1] "Moth_50_T" "Moth_66_T" "Gran_22_T" "Gran_49_T" "Gran_32_T" "Gran_28_T"This assay stores the microbial feature abundance matrix for the
study. Because it is also represented as a
SummarizedExperiment, it can be handled with the same
accessors as the metabolite assays, which keeps the study workflow
consistent across modalities.
pathway_annotations <- HuMMANet_load_modality(
study_id,
"metabolitePathwayAnnotations"
)
dim(pathway_annotations)
#> [1] 2058 11
head(pathway_annotations[, seq_len(min(6, ncol(pathway_annotations)))])
#> HuMANet_ID Query_Name Annotation_ID_Type Annotation_ID_Value
#> 1 HMAN000001 Asp HMDB|KEGG C00049|HMDB0000191
#> 2 HMAN000001 Asp HMDB|KEGG C00049|HMDB0000191
#> 3 HMAN000001 Asp HMDB|KEGG C00049|HMDB0000191
#> 4 HMAN000007 Gly HMDB|KEGG C00037|HMDB0000123
#> 5 HMAN000001 Asp HMDB|KEGG C00049|HMDB0000191
#> 6 HMAN000009 HISTIDINE HMDB|KEGG C00135|HMDB0000177
#> InChIKey PubChem_CID
#> 1 CKLJMWTZIZZHCS-REOHCLBHSA-N 5960
#> 2 CKLJMWTZIZZHCS-REOHCLBHSA-N 5960
#> 3 CKLJMWTZIZZHCS-REOHCLBHSA-N 5960
#> 4 DHMQDGOQFOQNFH-UHFFFAOYSA-N 750
#> 5 CKLJMWTZIZZHCS-REOHCLBHSA-N 5960
#> 6 HNDVDQJCIGZPNO-YFKPBYRVSA-N 6274Unlike the assay modalities, annotation resources such as pathways,
diseases, microbial producer annotations, and drug similarity matches
are long-form tables. They are returned as data.frame
objects because they are naturally represented as annotations rather
than sample-by-feature matrices.
names(metadata(one_study))
#> [1] "metaboliteIdentifierMapping" "microbialProducerAnnotations"
#> [3] "metaboliteDiseaseAssociations" "metabolitePathwayAnnotations"
#> [5] "drugBankSimilarityMatches" "drugCentralSimilarityMatches"The metadata() slot of the study object stores the
annotation resources that were requested during loading. This is helpful
when you want to keep the study object intact and access all associated
annotation layers from a single container instead of managing separate
tables manually.
harm_mat <- assay(harmonized_profile)
taxa_mat <- assay(taxa_profile)
dim(harm_mat)
#> [1] 14 122
dim(taxa_mat)
#> [1] 677 122These extracted matrices can be used directly in downstream workflows
for ordination, association testing, machine learning, or other custom
analyses. The companion rowData() and
colData() accessors preserve the feature-level and
sample-level context required for reproducible analysis.
sample_keep <- rownames(colData(one_study))[seq_len(min(5, nrow(colData(one_study))))]
study_subset <- one_study[, sample_keep]
study_subset
#> A MultiAssayExperiment object of 3 listed
#> experiments with user-defined names and respective classes.
#> Containing an ExperimentList class object of length 3:
#> [1] taxaAbundanceProfile: SummarizedExperiment with 677 rows and 5 columns
#> [2] OriginalMetaboliteProfile: SummarizedExperiment with 14 rows and 5 columns
#> [3] harmonizedMetaboliteProfile: SummarizedExperiment with 14 rows and 5 columns
#> Functionality:
#> experiments() - obtain the ExperimentList instance
#> colData() - the primary/phenotype DataFrame
#> sampleMap() - the sample coordination DataFrame
#> `$`, `[`, `[[` - extract colData columns, subset, or experiment
#> *Format() - convert into a long or wide DataFrame
#> assays() - convert ExperimentList to a SimpleList of matrices
#> exportClass() - save data to flat filesSubsetting a MultiAssayExperiment by columns subsets the
shared sample space and keeps the assay-level containers synchronized.
This is one of the main advantages of using standard Bioconductor
containers for paired multi-omics data, because users do not need to
manually subset each assay and metadata table separately.
selected_studies <- studies[seq_len(min(2, length(studies)))]
multi <- HuMMANet(
studies = selected_studies,
modalities = c("MetadataProfile", "OriginalMetaboliteProfile")
)
names(multi)
#> [1] "BarkerTejedaTC_2024_Targeted" "BarkerTejedaTC_2024_Untargeted"
class(multi[[1]])
#> [1] "MultiAssayExperiment"
#> attr(,"package")
#> [1] "MultiAssayExperiment"
names(experiments(multi[[1]]))
#> [1] "OriginalMetaboliteProfile"The top-level HuMMANet() convenience function returns a
named list of study-level MultiAssayExperiment objects.
This is useful when iterating across multiple studies while preserving
the same container structure for each one.
sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats4 stats graphics grDevices utils datasets methods
#> [8] base
#>
#> other attached packages:
#> [1] MultiAssayExperiment_1.39.1 SummarizedExperiment_1.43.0
#> [3] Biobase_2.73.2 GenomicRanges_1.65.4
#> [5] Seqinfo_1.3.2 IRanges_2.47.5
#> [7] S4Vectors_0.51.10 BiocGenerics_0.59.12
#> [9] generics_0.1.4 MatrixGenerics_1.25.0
#> [11] matrixStats_1.5.0 HuMMANet_0.99.3
#> [13] BiocStyle_2.41.0
#>
#> loaded via a namespace (and not attached):
#> [1] KEGGREST_1.53.6 xfun_0.61 bslib_0.12.0
#> [4] httr2_1.3.0 lattice_0.23-1 vctrs_0.7.3
#> [7] tools_4.6.1 curl_8.0.0 tibble_3.3.1
#> [10] AnnotationDbi_1.75.2 RSQLite_3.53.3 blob_1.3.0
#> [13] pkgconfig_2.0.3 BiocBaseUtils_1.15.1 Matrix_1.7-6
#> [16] dbplyr_2.6.0 lifecycle_1.0.5 compiler_4.6.1
#> [19] Biostrings_2.81.9 htmltools_0.5.9 sys_3.4.3
#> [22] buildtools_1.0.0 sass_0.4.10 yaml_2.3.12
#> [25] pillar_1.11.1 crayon_1.5.3 jquerylib_0.1.4
#> [28] DelayedArray_0.39.8 cachem_1.1.0 abind_1.4-8
#> [31] ExperimentHub_3.3.2 AnnotationHub_4.3.2 tidyselect_1.2.1
#> [34] digest_0.6.39 purrr_1.2.2 dplyr_1.2.1
#> [37] BiocVersion_3.24.0 maketools_1.3.2 grid_4.6.1
#> [40] fastmap_1.2.0 SparseArray_1.13.4 cli_3.6.6
#> [43] magrittr_2.0.5 S4Arrays_1.13.2 withr_3.0.3
#> [46] filelock_1.0.3 rappdirs_0.3.4 bit64_4.8.6
#> [49] rmarkdown_2.32 XVector_0.53.0 httr_1.4.9
#> [52] bit_4.6.0 otel_0.2.0 png_0.1-9
#> [55] memoise_2.0.1 evaluate_1.0.5 knitr_1.52
#> [58] BiocFileCache_3.3.0 rlang_1.3.0 glue_1.8.1
#> [61] DBI_1.3.0 BiocManager_1.30.27 jsonlite_2.0.0
#> [64] R6_2.6.1