Contents

0.1 Introduction

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:

Each study is represented as a MultiAssayExperiment object:

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:

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.

0.2 Installation

Install from Bioconductor:

if (!requireNamespace("BiocManager", quietly = TRUE)) {
    install.packages("BiocManager")
}
BiocManager::install("HuMMANet")

Install the development version from GitHub:

if (!requireNamespace("remotes", quietly = TRUE)) {
    install.packages("remotes")
}
remotes::install_github("ShivangiV-IIITD/HuMMANet")

0.3 Load the package

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':
#> 
#>     Filter, Find, Map, Position, Reduce, anyDuplicated, aperm, append,
#>     as.data.frame, basename, cbind, colnames, dirname, do.call,
#>     duplicated, eval, evalq, get, grep, grepl, is.unsorted, lapply,
#>     mapply, match, mget, order, paste, pmax, pmax.int, pmin, pmin.int,
#>     rank, rbind, 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':
#> 
#>     I, expand.grid, 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)

0.4 Discover available studies

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.

0.5 Inspect the study index

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                            TRUE

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

0.6 Load one study

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         ESP

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

0.7 Inspect the study structure

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_T

experiments(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.

0.8 Load the study 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     ESP

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

0.9 Inspect the original metabolite profile

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.

0.10 Load the harmonized metabolite profile

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          NA

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

0.11 Inspect the taxa abundance profile

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.

0.12 Load an annotation table

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        6274

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

0.13 Inspect study-level annotation metadata

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.

0.14 Extract assay matrices for downstream analysis

harm_mat <- assay(harmonized_profile)
taxa_mat <- assay(taxa_profile)

dim(harm_mat)
#> [1]  14 122
dim(taxa_mat)
#> [1] 677 122

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

0.15 Subset a study object

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 files

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

0.16 Load multiple studies and modalities

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.

0.17 Session information

sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.4 LTS
#> 
#> Matrix products: default
#> BLAS:   /home/biocbuild/bbs-3.24-bioc/R/lib/libRblas.so 
#> LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=en_GB              LC_COLLATE=C              
#>  [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: America/New_York
#> 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      sass_0.4.10         
#> [22] yaml_2.3.12          pillar_1.11.1        crayon_1.5.3        
#> [25] jquerylib_0.1.4      DelayedArray_0.39.6  cachem_1.1.0        
#> [28] abind_1.4-8          ExperimentHub_3.3.2  AnnotationHub_4.3.2 
#> [31] tidyselect_1.2.1     digest_0.6.39        purrr_1.2.2         
#> [34] dplyr_1.2.1          bookdown_0.48        BiocVersion_3.24.0  
#> [37] grid_4.6.1           fastmap_1.2.0        SparseArray_1.13.2  
#> [40] cli_3.6.6            magrittr_2.0.5       S4Arrays_1.13.0     
#> [43] withr_3.0.3          filelock_1.0.3       rappdirs_0.3.4      
#> [46] bit64_4.8.6          rmarkdown_2.32       XVector_0.53.0      
#> [49] httr_1.4.9           bit_4.6.0            otel_0.2.0          
#> [52] png_0.1-9            memoise_2.0.1        evaluate_1.0.5      
#> [55] knitr_1.52           BiocFileCache_3.3.0  rlang_1.3.0         
#> [58] glue_1.8.1           DBI_1.3.0            BiocManager_1.30.27 
#> [61] jsonlite_2.0.0       R6_2.6.1