library(TiDEomics)
library(org.Mm.eg.db)

0.1 Introduction

This tutorial demonstrates the multi-dataset/multi-omics workflow of TiDEomics. It complements the single-dataset workflow in the TiDEomics Tutorial vignette (browseVignettes("TiDEomics")).

Key features covered:

  • Preparation of multiple datasets: prepare, filter and combine datasets, retaining shared and dataset-specific features.

  • Multi-dataset exploratory visualisation: plot temporal profiles, PCA, UMAP, and sample correlation matrices across datasets or omics layers.

  • Consensus co-expression module analysis: identify and interpret consensus WGCNA modules across datasets, including integrated visualisation with functional enrichment.

  • Feature level comparison: compare feature properties or feature set overlaps within or across datasets, with optional functional annotation.

In TiDEomics, a dataset is one SummarizedExperiment object. Multiple datasets or omics layers are provided as a named list, e.g. list(Exp1 = se1, Exp2 = se2).

0.2 Example input

The tutorial dataset was derived from GSE263759 (Traxler et al. 2025) as described in the TiDEomics Tutorial vignette. tutorial_data and tutorial_sample_info provide the sample expression and annotations:

data(tutorial_data, package = "TiDEomics")
data(tutorial_sample_info, package = "TiDEomics")

tutorial_data[1:5, 1:5]

Exp1 uses tutorial_data directly. Exp2 is generated from the same table by adding noise and suffixing its first 50 feature names with _Exp2, to simulate differences between datasets or omics layers.

tutorial_data_2 <- tutorial_data
exp1_values <- as.matrix(tutorial_data[, -1])
gene_sd <- apply(exp1_values, 1, sd)

set.seed(5678)
noise <- matrix(
    rnorm(length(exp1_values)),
    nrow = nrow(exp1_values)
)
tutorial_data_2[, -1] <- exp1_values + noise * 0.5 * gene_sd
tutorial_data_2$Feature[seq_len(50)] <- paste0(
    tutorial_data_2$Feature[seq_len(50)], "_Exp2"
)

The two datasets each have 50 dataset-specific features, while all other features are shared:

shared_features <- intersect(tutorial_data$Feature, tutorial_data_2$Feature)
length(shared_features)
#> [1] 450

0.3 Workflow

0.3.1 Multi-dataset preparation

combine_tide() accepts a list of prepare_tide() outputs and returns the filtered objects including all features or shared features only.

tide_results <- list()
tide_results[["Exp1"]] <- prepare_tide(
    data = tutorial_data, sample_ann = tutorial_sample_info,
    feature_threshold = 0, keep = "threshold", residual_threshold = 100)
#> No Subject column specified. Samples treated as independent. For repeated-measures designs, set subject_col to the column identifying biological subjects.
#> Converting 'Group' column to factor. Default order is alphabetical.
#> Converting 'Replicate' column to factor. Default order is numerical.
#> Converting 'Batch' column to factor. Default order is numerical.
#> Normalising to group baseline at each feature's first non-NA time point.
#> Preparing TiDEomics input: 500 features, 40 samples, 4 groups
#> Filtering criteria: >=50% values >0 in >=2 of groups: IFNbeta, IFNgamma, LPS, untreated
#> Cross-group filter (Exp_ratio >= 0.5 in >= 2 groups): kept 340 of 500 features (68%)
#> --- assay: orig ---
#> LMM: exp ~ (1|Group) + (1|Time)  |  Output: Group, Time, Residual
#> 340 features evaluated: 267 successful; 0 errors; 73 warnings; 0 skipped before fitting for insufficient observations or variance.
#> Residual filter (threshold): kept 334 of 340 features (98.2%)
#> --- assay: norm ---
#> LMM: exp ~ (1|Group) + (1|Time)  |  Output: Group, Time, Residual
#> 340 features evaluated: 267 successful; 0 errors; 73 warnings; 0 skipped before fitting for insufficient observations or variance.
#> Residual filter (threshold): kept 333 of 340 features (97.9%)
tide_results[["Exp2"]] <- prepare_tide(
    data = tutorial_data_2, sample_ann = tutorial_sample_info,
    feature_threshold = 0, keep = "threshold", residual_threshold = 100)
#> No Subject column specified. Samples treated as independent. For repeated-measures designs, set subject_col to the column identifying biological subjects.
#> Converting 'Group' column to factor. Default order is alphabetical.
#> Converting 'Replicate' column to factor. Default order is numerical.
#> Converting 'Batch' column to factor. Default order is numerical.
#> Normalising to group baseline at each feature's first non-NA time point.
#> Preparing TiDEomics input: 500 features, 40 samples, 4 groups
#> Filtering criteria: >=50% values >0 in >=2 of groups: IFNbeta, IFNgamma, LPS, untreated
#> Cross-group filter (Exp_ratio >= 0.5 in >= 2 groups): kept 459 of 500 features (91.8%)
#> --- assay: orig ---
#> LMM: exp ~ (1|Group) + (1|Time)  |  Output: Group, Time, Residual
#> 459 features evaluated: 289 successful; 0 errors; 170 warnings; 0 skipped before fitting for insufficient observations or variance.
#> Residual filter (threshold): kept 426 of 459 features (92.8%)
#> --- assay: norm ---
#> LMM: exp ~ (1|Group) + (1|Time)  |  Output: Group, Time, Residual
#> 459 features evaluated: 306 successful; 0 errors; 153 warnings; 0 skipped before fitting for insufficient observations or variance.
#> Residual filter (threshold): kept 437 of 459 features (95.2%)

combined <- combine_tide(tide_results, assay = 2)
#> Combining 2 prepare_tide() results using assay 'norm'
#> Shared filtered features: 289 (Exp1=333, Exp2=437)
names(combined)
#> [1] "se_list_filtered"        "se_list_merged"         
#> [3] "se_list_filtered_shared" "se_list_merged_shared"  
#> [5] "variance_decomp"         "shared_features"        
#> [7] "assay"                   "filter_summary"
combined$filter_summary

The returned filter_summary reports the number and percentage of features retained in each dataset. The se_list_filtered and se_list_merged objects include dataset specific features, while se_list_filtered_shared and se_list_merged_shared contain only features retained by every dataset.

0.3.2 Multi-dataset visualisation

plot_trend() plots the mean and standard deviation of selected features over time. The multi_layout argument controls the arrangement of datasets and features, while independent_y controls whether panels use independent or shared y-axis ranges:

plot_trend(combined$se_list_filtered,
    features = combined$shared_features[1:4], assay = 2,
    multi_layout = "wrap", independent_y = FALSE, ncol = 2
)
#> Group not specified. Plotting all groups: IFNbeta, IFNgamma, LPS, untreated


plot_trend(combined$se_list_filtered,
    features = combined$shared_features[1:4], assay = 2,
    multi_layout = "overlay", independent_y = TRUE, ncol = 2
)
#> Group not specified. Plotting all groups: IFNbeta, IFNgamma, LPS, untreated


plot_trend(combined$se_list_filtered,
    features = combined$shared_features[1:4], assay = 2,
    multi_layout = "vertical", independent_y = FALSE
)
#> Group not specified. Plotting all groups: IFNbeta, IFNgamma, LPS, untreated


plot_trend(combined$se_list_filtered,
    features = combined$shared_features[1:4], assay = 2,
    multi_layout = "horizontal", independent_y = TRUE
)
#> Group not specified. Plotting all groups: IFNbeta, IFNgamma, LPS, untreated

plot_pca(), plot_umap(), and plot_cor_matrix() accept the dataset list and plot the datasets side by side. With shared_only = TRUE, PCA, UMAP, and correlation matrices are computed on the features shared by all datasets, otherwise all eligible features are used.

PC <- plot_pca(combined$se_list_filtered,
    assay = 2,
    shared_only = TRUE,
    plot_screeplot = TRUE,
    plot_loadings = FALSE,
    plot_morepc = FALSE
)
#> Subsetting features: Exp1=289, Exp2=289
#> Warning: Using size for a discrete variable is not advised.
#> Using size for a discrete variable is not advised.
PC$plot

umap_out <- plot_umap(combined$se_list_filtered,
    seed = 1234, assay = 2,
    shared_only = FALSE
)
#> Exp1: using n_neighbors = 8
#> Warning: Using size for a discrete variable is not advised.
#> Exp2: using n_neighbors = 8
#> Warning: Using size for a discrete variable is not advised.
umap_out$plot

plot_cor_matrix(combined$se_list_filtered,
    method = "spearman",
    assay = 2,
    shared_only = FALSE,
    label_rep = TRUE, label_batch = TRUE,
    cellwidth = 1.5, cellheight = 1.5
)
#> Cophenetic correlation between datasets (Pearson): 0.96

When datasets contain the same samples, plot_cor_matrix() reports clustering similarity using correlation of cophenetic distances.

The other TiDEomics functions can be applied separately to each dataset (e.g. combined$se_list_filtered$Exp1 and combined$se_list_filtered$Exp2), as described in the main tutorial.

0.3.3 Consensus WGCNA

Consensus WGCNA identifies co-expression modules that are present across all or most datasets (Zhang and Horvath 2005; Langfelder and Horvath 2008, 2012). Only features shared by all datasets are included, so the se_list_filtered_shared from combine_tide() output is used.

First, scale-free topology fit is assessed and the suitable power is picked for each dataset with prepare_WGCNA():

inp1 <- prepare_WGCNA(combined$se_list_filtered_shared$Exp1,
    assay = 2, powers = seq(1, 20),
    networkType = "signed", RsquaredCut = 0.8
)
#> Allowing multi-threading with up to 72 threads.
#> pickSoftThreshold: will use block size 289.
#>  pickSoftThreshold: calculating connectivity for given powers...
#>    ..working on genes 1 through 289 of 289
#>    Power SFT.R.sq  slope truncated.R.sq mean.k. median.k. max.k.
#> 1      1   0.7930  3.770         0.7570  157.00   162.000 185.00
#> 2      2   0.0471  0.298         0.0103   94.20    96.900 133.00
#> 3      3   0.3230 -0.857         0.3510   60.50    61.400 101.00
#> 4      4   0.5250 -0.991         0.6590   40.90    40.300  79.10
#> 5      5   0.5780 -1.060         0.7930   28.70    27.400  63.30
#> 6      6   0.6320 -1.110         0.8610   20.80    18.700  51.60
#> 7      7   0.7020 -1.140         0.9010   15.50    13.100  42.70
#> 8      8   0.7670 -1.140         0.9530   11.70     9.670  35.70
#> 9      9   0.7970 -1.160         0.9650    9.09     7.150  30.10
#> 10    10   0.8070 -1.170         0.9760    7.14     5.330  25.70
#> 11    11   0.7980 -1.230         0.9690    5.70     4.100  22.00
#> 12    12   0.7640 -1.290         0.9320    4.60     3.300  19.10
#> 13    13   0.7800 -1.290         0.9330    3.75     2.640  16.60
#> 14    14   0.7940 -1.340         0.9440    3.09     2.120  14.50
#> 15    15   0.8010 -1.380         0.9200    2.57     1.740  12.80
#> 16    16   0.8520 -1.380         0.9700    2.15     1.430  11.30
#> 17    17   0.8570 -1.350         0.9720    1.81     1.210  10.00
#> 18    18   0.8560 -1.350         0.9520    1.54     0.998   8.95
#> 19    19   0.8580 -1.360         0.9490    1.31     0.832   8.02
#> 20    20   0.8790 -1.400         0.9520    1.13     0.676   7.21
#> WGCNA powerEstimate: 10 (unsigned SFT.R.sq > 0.8); custom_powerEstimate: 10 (signed R-squared > 0.8).
inp1$plot


inp2 <- prepare_WGCNA(combined$se_list_filtered_shared$Exp2,
    assay = 2, powers = seq(1, 20),
    networkType = "signed", RsquaredCut = 0.8
)
#> Allowing multi-threading with up to 72 threads.
#> pickSoftThreshold: will use block size 289.
#>  pickSoftThreshold: calculating connectivity for given powers...
#>    ..working on genes 1 through 289 of 289
#>    Power SFT.R.sq  slope truncated.R.sq mean.k. median.k. max.k.
#> 1      1    0.778  4.300          0.755 154.000   158.000 178.00
#> 2      2    0.111  0.549          0.296  89.000    90.900 120.00
#> 3      3    0.254 -0.737          0.434  54.300    54.400  84.50
#> 4      4    0.586 -1.130          0.701  34.600    34.000  61.70
#> 5      5    0.653 -1.230          0.777  22.900    21.900  46.30
#> 6      6    0.729 -1.300          0.883  15.600    14.400  35.50
#> 7      7    0.790 -1.320          0.934  10.900     9.570  27.60
#> 8      8    0.779 -1.310          0.893   7.770     6.470  21.80
#> 9      9    0.805 -1.330          0.935   5.640     4.500  17.50
#> 10    10    0.827 -1.360          0.959   4.160     3.180  14.10
#> 11    11    0.837 -1.380          0.958   3.120     2.320  11.50
#> 12    12    0.811 -1.430          0.919   2.360     1.670   9.43
#> 13    13    0.836 -1.390          0.963   1.810     1.250   7.80
#> 14    14    0.815 -1.450          0.967   1.400     0.963   6.49
#> 15    15    0.825 -1.460          0.950   1.100     0.744   5.43
#> 16    16    0.852 -1.490          0.960   0.863     0.580   4.56
#> 17    17    0.853 -1.520          0.931   0.686     0.441   3.85
#> 18    18    0.873 -1.520          0.953   0.548     0.345   3.27
#> 19    19    0.882 -1.530          0.950   0.441     0.274   2.79
#> 20    20    0.878 -1.530          0.936   0.357     0.218   2.38
#> WGCNA powerEstimate: 9 (unsigned SFT.R.sq > 0.8); custom_powerEstimate: 9 (signed R-squared > 0.8).
inp2$plot

run_consensus_WGCNA() then computes consensus network and modules across the datasets.

cons <- run_consensus_WGCNA(
    nets = list(Exp1 = inp1, Exp2 = inp2),
    powers = c(
        Exp1 = inp1$custom_powerEstimate,
        Exp2 = inp2$custom_powerEstimate
    ),
    minModuleSize = 10
)
#> Powers differ across datasets (Exp1=10, Exp2=9). Passing per-dataset powers.
#> Common features across 2 datasets: 289
#>   Exp1: 289 features
#>   Exp2: 289 features
#> Running blockwise consensus WGCNA...
#> Allowing multi-threading with up to 72 threads.
#>  Calculating consensus modules and module eigengenes block-wise from all genes
#>  Calculating topological overlaps block-wise from all genes
#>    Flagging genes and samples with too many missing values...
#>     ..step 1
#>  ....Working on set 1
#>     TOM calculation: adjacency..
#>     ..will use 72 parallel threads.
#>      Fraction of slow calculations: 0.000000
#>     ..connectivity..
#>     ..matrix multiplication (system BLAS)..
#>     ..normalization..
#>     ..done.
#>  ....Working on set 2
#>     TOM calculation: adjacency..
#>     ..will use 72 parallel threads.
#>      Fraction of slow calculations: 0.000000
#>     ..connectivity..
#>     ..matrix multiplication (system BLAS)..
#>     ..normalization..
#>     ..done.
#>  ..Working on block 1 .
#>  ....Working on set 1
#>  ....Working on set 2
#>  ....Calculating consensus network
#>  ..Working on block 1 .
#>  ....clustering and detecting modules..
#>  ....calculating eigengenes..
#>  ....checking consensus modules for statistical meaningfulness..
#>  ....checking for genes that should be reassigned..
#>  ..merging consensus modules that are too close..
#>      mergeCloseModules: Merging modules whose distance is less than 0.15
#>        Calculating new MEs...
#> Consensus WGCNA complete.

The features with the highest module membership (kME) per module can be extracted with extract_consensus_hubs(). The result is a table containing consensus and dataset-specific hub feature sets:

hubs <- extract_consensus_hubs(cons, top_n = 1)
head(hubs)

Module feature counts and module membership summaries can be reported with summarise_module_metrics():

module_metrics <- summarise_module_metrics(cons)
module_metrics$Consensus

0.3.4 Visualisation of co-expressed modules

The feature-module assignment can be extracted with WGCNA_module() and visualised by heatmap, using the replicate-merged expression data of each dataset, as the se_list_merged_shared output from combine_tide():

cons_module <- WGCNA_module(cons$network, exclude_grey = TRUE)

plot_modules_v() stacks the per-dataset module profiles vertically:

plot_modules_v(cons_module, combined$se_list_merged_shared,
    assay = 2, scale = TRUE,
    height_ratio = 2)
#> Warning: Removed 804 rows containing non-finite outside the scale range
#> (`stat_summary()`).
#> Removed 804 rows containing non-finite outside the scale range
#> (`stat_summary()`).

plot_modules_h() provides the horizontal layout with optional enrichment annotations, similar to the single dataset usage. Module profile y-axis scaling, group annotations, marked features, and legend position can also be customised.

In the below example, the hub features are coloured by categories, and the module profiles use shared y-axis across all modules and datasets.

hub_colours <- c(
    "Consensus hub" = "#0072B2",
    "Exp1 hub" = "#D55E00",
    "Exp2 hub" = "#009E73"
)

plot_modules_h(cons_module, combined$se_list_merged_shared,
    assay = 2, scale = TRUE,
    mark_features = hubs,
    feature_colours = hub_colours,
    profile_scale = "shared", # or "dataset", "free"
    group_annotation = "both", # or "name", "colour", "none"
    legend_position = "right", # or "bottom"
    heatmap_width = 5, heatmap_height = 8,
    profile_width = 3
)

plot_modules_h() also supports selection of specific modules to display with selected_modules and wrapping long enrichment terms with enrich_wrap_width:

module_background <- WGCNA_module(cons$network, exclude_grey = FALSE)$Feature

go_modules <- enrichGO_list(cons_module,
    OrgDb = org.Mm.eg.db,
    universe = module_background,
    category = "BP",
    pvalueCutoff = 1, qvalueCutoff = 1) # keep all terms for visualisation
#> Performing GO enrichment for category: BP
#> Processing gene list: 1
#> Processing gene list: 2
#> Processing gene list: 3
#> Processing gene list: 4
#> Processing gene list: 5
#> Processing gene list: 6
#> Processing gene list: 7
#> Merging GO enrichment results across gene lists for each category.

plot_modules_h(cons_module, combined$se_list_merged_shared,
    assay = 2, scale = TRUE,
    mark_features = hubs,
    feature_colours = hub_colours,
    enrich_list = go_modules$all,
    enrich_category = c("BP", "Features"),
    enrich_wrap_width = 4,
    profile_scale = "free", # or "dataset", "shared"
    group_annotation = "colour", # or "name", "both", "none"
    legend_position = "right",
    heatmap_width = 3, heatmap_height = 8,
    profile_width = 3,
    selected_modules = c(2, 5)
)

0.3.5 Feature overlap or property comparison

Feature overlap or property comparisons can be across datasets or omics layers, or within a single dataset. For example, one can compare specific contrasts or properties of different modules from one dataset.

The feature overlap can be visualised with plot_upset_h() and plot_upset_v(), based on ComplexHeatmap::UpSet() (Gu, Eils, and Schlesner 2016; Gu 2022).

For plot_upset_h(), functional enrichment terms (Yu 2024; Xu et al. 2024; Wu et al. 2021; Yu et al. 2012) for the feature set intersections can be added as a horizontally aligned textbox:

set_list <- lapply(combined$se_list_filtered, rownames)
background_features <- unique(unlist(set_list))

feature_sets <- list(
    Exp1_Exp2 = intersect(set_list$Exp1, set_list$Exp2),
    Exp1 = setdiff(set_list$Exp1, set_list$Exp2),
    Exp2 = setdiff(set_list$Exp2, set_list$Exp1)
)

go_all <- enrichGO_list(feature_sets,
    OrgDb = org.Mm.eg.db,
    universe = background_features,
    category = "BP",
    pvalueCutoff = 1, qvalueCutoff = 1) # keep all terms for visualisation
#> Performing GO enrichment for category: BP
#> Processing gene list: Exp1_Exp2
#> Processing gene list: Exp1
#> Processing gene list: Exp2
#> Merging GO enrichment results across gene lists for each category.

plot_upset_h(set_list,
    enrich_list = go_all$all,
    enrich_category = "BP",
    enrich_top_n = 2,
    enrich_wrap_width = 7
)
#> Intersection pattern to label mapping:
#>   11: Exp1_Exp2
#>   10: Exp1
#>   01: Exp2

plot_upset_v() allows a vertically aligned enrichment term dot matrix below the Upset plot. The term ordering and colours can be customised with enrich_term_order and enrich_term_colours, and term_wrap_width controls term wrapping (similar to enrich_wrap_width for plot_upset_h()).

bp_terms <- unique(as.data.frame(go_all$all$BP)$ID)
enrich_term_colours <- stats::setNames(
    rep(c("#0072B2", "#D55E00", "#009E73", "black"),
        length.out = length(bp_terms)),
    bp_terms
)

plot_upset_v(set_list,
    enrich_list = go_all$all,
    enrich_category = "BP",
    enrich_top_n = 2,
    term_wrap_width = 50,
    enrich_term_order = "intersection", # or "rank"
    enrich_term_colours = enrich_term_colours,
    include_all = FALSE # see clusterProfiler::dotplot `includeAll` usage
)
#> Intersection pattern to label mapping:
#>   11: Exp1_Exp2
#>   10: Exp1
#>   01: Exp2

Per-feature properties can be compared with plot_scatter(). Here the feature mean abundance is compared between the two datasets:

exp1_stat <- data.frame(
    Feature = rownames(combined$se_list_filtered$Exp1),
    Mean = rowMeans(
        SummarizedExperiment::assay(combined$se_list_filtered$Exp1, 2),
        na.rm = TRUE
    )
)
exp2_stat <- data.frame(
    Feature = rownames(combined$se_list_filtered$Exp2),
    Mean = rowMeans(
        SummarizedExperiment::assay(combined$se_list_filtered$Exp2, 2),
        na.rm = TRUE
    )
)

plot_scatter(exp1_stat, exp2_stat,
    col1 = "Mean", col2 = "Mean",
    name1 = "Exp1 mean abundance",
    name2 = "Exp2 mean abundance",
    colour_threshold = c(x = 1, y = 1),
    label_threshold = c(x = 2.5, y = 2.5)
)

0.4 Session information

sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.5 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] org.Mm.eg.db_3.23.0         AnnotationDbi_1.75.2       
#>  [3] SummarizedExperiment_1.43.0 Biobase_2.73.2             
#>  [5] GenomicRanges_1.65.4        Seqinfo_1.3.2              
#>  [7] IRanges_2.47.5              S4Vectors_0.51.10          
#>  [9] BiocGenerics_0.59.12        generics_0.1.4             
#> [11] MatrixGenerics_1.25.0       matrixStats_1.5.0          
#> [13] TiDEomics_0.99.7            BiocStyle_2.41.0           
#> 
#> loaded via a namespace (and not attached):
#>   [1] segmented_2.2-2             fs_2.1.0                   
#>   [3] bitops_1.1-0                enrichplot_1.99.8          
#>   [5] httr_1.4.9                  RColorBrewer_1.1-3         
#>   [7] doParallel_1.0.17           ggsci_5.2.0                
#>   [9] DeeDeeExperiment_1.3.0      dynamicTreeCut_1.63-1      
#>  [11] tools_4.6.1                 backports_1.5.1            
#>  [13] R6_2.6.1                    lazyeval_0.2.3             
#>  [15] GetoptLong_1.1.1            withr_3.0.3                
#>  [17] gridExtra_2.3.1             preprocessCore_1.75.1      
#>  [19] WGCNA_1.74                  cli_3.6.6                  
#>  [21] Cairo_1.7-0                 scatterpie_0.2.6           
#>  [23] labeling_0.4.3              sass_0.4.10                
#>  [25] S7_0.2.2                    askpass_1.2.1              
#>  [27] pbapply_1.7-5               ggridges_0.5.7             
#>  [29] systemfonts_1.3.2           yulab.utils_0.2.5          
#>  [31] gson_0.2.2                  foreign_0.8-92             
#>  [33] DOSE_4.7.3                  dichromat_2.0-1            
#>  [35] limma_3.99.3                rstudioapi_0.19.0          
#>  [37] impute_1.87.0               RSQLite_3.53.3             
#>  [39] gridGraphics_0.5-1          shape_1.4.6.1              
#>  [41] crosstalk_1.2.2             gtools_3.9.5               
#>  [43] car_3.1-5                   dplyr_1.2.1                
#>  [45] GO.db_3.23.1                Matrix_1.7-6               
#>  [47] abind_1.4-8                 PCAtools_2.25.1            
#>  [49] lifecycle_1.0.5             edgeR_4.99.10              
#>  [51] yaml_2.3.12                 carData_3.0-6              
#>  [53] gplots_3.3.0                qvalue_2.45.0              
#>  [55] SparseArray_1.13.4          grid_4.6.1                 
#>  [57] blob_1.3.0                  promises_1.5.0             
#>  [59] dqrng_0.4.1                 crayon_1.5.3               
#>  [61] ggtangle_0.1.4              lattice_0.23-1             
#>  [63] msigdbr_26.1.1              beachmat_2.29.3            
#>  [65] cowplot_1.2.0               KEGGREST_1.53.6            
#>  [67] magick_2.9.1                pillar_1.11.1              
#>  [69] knitr_1.52                  ComplexHeatmap_2.29.0      
#>  [71] rjson_0.2.23                boot_1.3-32                
#>  [73] codetools_0.2-20            glue_1.8.1                 
#>  [75] ggiraph_0.9.6               ggfun_0.2.2                
#>  [77] fontLiberation_0.1.0        data.table_1.18.6.1        
#>  [79] vctrs_0.7.3                 png_0.1-9                  
#>  [81] treeio_1.37.1               Rdpack_2.6.6               
#>  [83] gtable_0.3.6                assertthat_0.2.1           
#>  [85] cachem_1.1.0                xfun_0.62                  
#>  [87] rbibutils_2.4.1             S4Arrays_1.13.2            
#>  [89] mime_0.13                   reformulas_0.4.4           
#>  [91] survival_3.8-12             aisdk_1.4.12               
#>  [93] SingleCellExperiment_1.35.2 iterators_1.0.14           
#>  [95] statmod_1.5.2               nlme_3.1-171               
#>  [97] ggtree_4.3.0                bit64_4.8.6                
#>  [99] fontquiver_0.2.1            bslib_0.12.0               
#> [101] irlba_2.4.1                 KernSmooth_2.23-27         
#> [103] otel_0.2.0                  rpart_4.1.27               
#> [105] colorspace_2.1-4            DBI_1.3.0                  
#> [107] Hmisc_5.3-0                 nnet_7.3-21                
#> [109] DESeq2_1.53.6               tidyselect_1.2.1           
#> [111] processx_3.9.0              bit_4.6.0                  
#> [113] compiler_4.6.1              httr2_1.3.0                
#> [115] htmlTable_2.5.0             plotly_4.12.1              
#> [117] randtests_1.0.2             fontBitstreamVera_0.1.1    
#> [119] DelayedArray_0.39.8         bookdown_0.48              
#> [121] checkmate_2.3.4             scales_1.4.0               
#> [123] caTools_1.18.4              callr_3.8.0                
#> [125] rappdirs_0.3.4              stringr_1.6.0              
#> [127] digest_0.6.39               minqa_1.2.8                
#> [129] rmarkdown_2.32              XVector_0.53.0             
#> [131] htmltools_0.5.9             pkgconfig_2.0.3            
#> [133] base64enc_0.1-6             umap_0.2.10.0              
#> [135] lme4_2.1-0                  sparseMatrixStats_1.25.0   
#> [137] fastmap_1.2.0               rlang_1.3.0                
#> [139] GlobalOptions_0.1.4         htmlwidgets_1.6.4          
#> [141] shiny_1.14.0                DelayedMatrixStats_1.35.0  
#> [143] ggh4x_0.3.1                 farver_2.1.2               
#> [145] jquerylib_0.1.4             jsonlite_2.0.0             
#> [147] BiocParallel_1.47.0         GOSemSim_2.39.4            
#> [149] BiocSingular_1.29.2         magrittr_2.0.5             
#> [151] Formula_1.2-6               ggplotify_0.1.4            
#> [153] patchwork_1.3.2             Rcpp_1.1.2                 
#> [155] babelgene_22.9              reticulate_1.47.0          
#> [157] ape_5.8-1                   ggnewscale_0.5.2           
#> [159] gdtools_0.5.2               stringi_1.8.9              
#> [161] MASS_7.3-66                 plyr_1.8.9                 
#> [163] shinyFiles_0.9.3            parallel_4.6.1             
#> [165] ggrepel_0.9.8               Biostrings_2.81.9          
#> [167] splines_4.6.1               circlize_0.4.18            
#> [169] locfit_1.5-9.12             ps_1.9.3                   
#> [171] igraph_2.3.4                ggpubr_1.0.0               
#> [173] fastcluster_1.3.0           ggsignif_0.6.4             
#> [175] enrichit_0.2.7              reshape2_1.4.5             
#> [177] ScaledMatrix_1.21.0         evaluate_1.0.5             
#> [179] BiocManager_1.30.27         nloptr_2.2.1               
#> [181] foreach_1.5.2               tweenr_2.0.3               
#> [183] httpuv_1.6.17               openssl_2.4.2              
#> [185] tidyr_1.3.2                 purrr_1.2.2                
#> [187] polyclip_1.10-7             clue_0.3-68                
#> [189] ggplot2_4.0.3               Trendy_1.35.0              
#> [191] BiocBaseUtils_1.15.1        ggforce_0.5.0              
#> [193] rsvd_1.0.6                  broom_1.0.13               
#> [195] xtable_1.8-8                RSpectra_0.16-2            
#> [197] tidytree_0.4.8              tidydr_0.0.7               
#> [199] rstatix_1.1.0               later_1.4.8                
#> [201] viridisLite_0.4.3           tibble_3.3.1               
#> [203] clusterProfiler_4.21.4      aplot_0.3.2                
#> [205] memoise_2.0.1               writexl_2.0.1              
#> [207] cluster_2.1.8.3

References

Gu, Zuguang. 2022. “Complex Heatmap Visualization.” iMeta. https://doi.org/10.1002/imt2.43.
Gu, Zuguang, Roland Eils, and Matthias Schlesner. 2016. “Complex Heatmaps Reveal Patterns and Correlations in Multidimensional Genomic Data.” Bioinformatics. https://doi.org/10.1093/bioinformatics/btw313.
Langfelder, Peter, and Steve Horvath. 2008. “WGCNA: An r Package for Weighted Correlation Network Analysis.” BMC Bioinformatics, no. 1: 559. https://link.springer.com/article/10.1186/1471-2105-9-559.
———. 2012. “Fast R Functions for Robust Correlations and Hierarchical Clustering.” Journal of Statistical Software 46 (11): 1–17. https://www.jstatsoft.org/v46/i11/.
Traxler, Peter, Stephan Reichl, Lukas Folkman, Lisa Shaw, Victoria Fife, Amelie Nemc, Djurdja Pasajlic, et al. 2025. “Integrated Time-Series Analysis and High-Content CRISPR Screening Delineate the Dynamics of Macrophage Immune Regulation.” Cell Systems 16 (8): 101346. https://doi.org/https://doi.org/10.1016/j.cels.2025.101346.
Wu, Tianzhi, Erqiang Hu, Shuangbin Xu, Meijun Chen, Pingfan Guo, Zehan Dai, Tingze Feng, et al. 2021. “clusterProfiler 4.0: A Universal Enrichment Tool for Interpreting Omics Data.” The Innovation 2 (3): 100141. https://doi.org/10.1016/j.xinn.2021.100141.
Xu, Shuangbin, Erqiang Hu, Yantong Cai, Zijing Xie, Xiao Luo, Li Zhan, Wenli Tang, et al. 2024. “Using clusterProfiler to Characterize Multiomics Data.” Nature Protocols 19 (11): 3292–3320. https://doi.org/10.1038/s41596-024-01020-z.
Yu, Guangchuang. 2024. “Thirteen Years of clusterProfiler.” The Innovation 5 (6): 100722. https://doi.org/10.1016/j.xinn.2024.100722.
Yu, Guangchuang, Li-Gen Wang, Yanyan Han, and Qing-Yu He. 2012. “clusterProfiler: An r Package for Comparing Biological Themes Among Gene Clusters.” OMICS: A Journal of Integrative Biology 16 (5): 284–87. https://doi.org/10.1089/omi.2011.0118.
Zhang, Bin, and Steve Horvath. 2005. “A General Framework for Weighted Gene Co-Expression Network Analysis.” Statistical Applications in Genetics and Molecular Biology 4 (1): 1128.