Getting Started with SingleCellComplexHeatMap

XueCheng

2025-06-24

Introduction

The SingleCellComplexHeatMap package provides a powerful and flexible way to visualize single-cell RNA-seq data using complex heatmaps that simultaneously display both gene expression levels (as color intensity) and expression percentages (as circle sizes). This dual-information approach allows for comprehensive visualization of expression patterns across different cell types and conditions.

Key Features

Installation

# Install from GitHub
devtools::install_github("FanXuRong/SingleCellComplexHeatMap")

Quick Start

Loading Libraries and Data

library(SingleCellComplexHeatMap)
library(Seurat)
library(dplyr)
# Load optional color packages for testing
if (requireNamespace("ggsci", quietly = TRUE)) {
  library(ggsci)
}
if (requireNamespace("viridis", quietly = TRUE)) {
  library(viridis)
}

# For this vignette, we'll use the built-in pbmc_small dataset
data("pbmc_small", package = "SeuratObject")
seurat_obj <- pbmc_small

# Add example metadata for demonstration
set.seed(123)
seurat_obj$timepoint <- sample(c("Mock", "6hpi", "24hpi"), ncol(seurat_obj), replace = TRUE)
seurat_obj$celltype <- sample(c("T_cell", "B_cell", "Monocyte"), ncol(seurat_obj), replace = TRUE)
seurat_obj$group <- paste(seurat_obj$timepoint, seurat_obj$celltype, sep = "_")

# Check the structure
head(seurat_obj@meta.data)
#>                   orig.ident nCount_RNA nFeature_RNA RNA_snn_res.0.8
#> ATGCCAGAACGACT SeuratProject         70           47               0
#> CATGGCCTGTGCAT SeuratProject         85           52               0
#> GAACCTGATGAACC SeuratProject         87           50               1
#> TGACTGGATTCTCA SeuratProject        127           56               0
#> AGTCAGACTGCACA SeuratProject        173           53               0
#> TCTGATACACGTGT SeuratProject         70           48               0
#>                letter.idents groups RNA_snn_res.1 timepoint celltype
#> ATGCCAGAACGACT             A     g2             0     24hpi   B_cell
#> CATGGCCTGTGCAT             A     g1             0     24hpi Monocyte
#> GAACCTGATGAACC             B     g2             0     24hpi Monocyte
#> TGACTGGATTCTCA             A     g2             0      6hpi   T_cell
#> AGTCAGACTGCACA             A     g2             0     24hpi   B_cell
#> TCTGATACACGTGT             A     g1             0      6hpi   T_cell
#>                         group
#> ATGCCAGAACGACT   24hpi_B_cell
#> CATGGCCTGTGCAT 24hpi_Monocyte
#> GAACCTGATGAACC 24hpi_Monocyte
#> TGACTGGATTCTCA    6hpi_T_cell
#> AGTCAGACTGCACA   24hpi_B_cell
#> TCTGATACACGTGT    6hpi_T_cell

Basic Usage

The simplest way to create a complex heatmap is to provide a Seurat object and a list of features:

# Define genes to visualize
features <- c("CD3D", "CD3E", "CD8A", "IL32", "CD79A")

# Create basic heatmap
heatmap_basic <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  group_by = "group"
)

Advanced Usage with Gene Grouping

For more complex analyses, you can group genes by functional categories:

# Define gene groups
gene_groups <- list(
  "T_cell_markers" = c("CD3D", "CD3E", "CD8A", "IL32"),
  "B_cell_markers" = c("CD79A", "CD79B", "MS4A1"),
  "Activation_markers" = c("GZMK", "CCL5")
)

# Get all genes from groups
all_genes <- c("CD3D", "CD3E", "CD8A", "IL32","CD79A", "CD79B", "MS4A1","GZMK", "CCL5")

# Create advanced heatmap with gene grouping
heatmap_advanced <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = all_genes,
  gene_classification = gene_groups,
  group_by = "group",
  time_points_order = c("Mock", "6hpi", "24hpi"),
  cell_types_order = c("T_cell", "B_cell", "Monocyte"),
  color_range = c(-2, 0, 2),
  color_palette = c("navy", "white", "firebrick"),
  max_circle_size = 3,
  split_by = "time"
)

Understanding the Visualization

Dual Information Display

The complex heatmap displays two types of information simultaneously:

  1. Color Intensity: Represents the scaled expression level of each gene
  2. Circle Size: Represents the percentage of cells expressing each gene

This dual approach allows you to distinguish between genes that are: - Highly expressed in few cells (small intense circles) - Moderately expressed in many cells (large moderate circles) - Highly expressed in many cells (large intense circles)

Data Preparation Requirements

For optimal functionality, your group identifiers should follow the format "timepoint_celltype":

# Example of proper data preparation
seurat_obj@meta.data <- seurat_obj@meta.data %>%
  mutate(
    # Create combined group for time course + cell type analysis
    time_celltype = paste(timepoint, celltype, sep = "_"),
    
    # Or create other combinations as needed
    cluster_time = paste(RNA_snn_res.0.8, timepoint, sep = "_")
  )

head(seurat_obj@meta.data[, c("timepoint", "celltype", "time_celltype","cluster_time")])
#>                timepoint celltype  time_celltype cluster_time
#> ATGCCAGAACGACT     24hpi   B_cell   24hpi_B_cell      0_24hpi
#> CATGGCCTGTGCAT     24hpi Monocyte 24hpi_Monocyte      0_24hpi
#> GAACCTGATGAACC     24hpi Monocyte 24hpi_Monocyte      1_24hpi
#> TGACTGGATTCTCA      6hpi   T_cell    6hpi_T_cell       0_6hpi
#> AGTCAGACTGCACA     24hpi   B_cell   24hpi_B_cell      0_24hpi
#> TCTGATACACGTGT      6hpi   T_cell    6hpi_T_cell       0_6hpi

Customization Options

Custom Annotation Titles

Here, we test the ability to change the default titles for the annotation tracks.

heatmap <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  gene_classification = gene_groups,
  group_by = "group",
  time_points_order = c("Mock", "6hpi", "24hpi"),
  
  # NEW: Custom annotation titles
  gene_group_title = "Gene Function",
  time_point_title = "Time Point", 
  cell_type_title = "Cell Type",
  
  split_by = "time"
)

Color Schemes

You can customize colors for different components:

# Custom color schemes
heatmap_colors <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  gene_classification = gene_groups,
  group_by = "group",
  color_range = c(-1.5, 0, 1.5,3),  # 4-point gradient
  color_palette = c("darkblue", "blue", "white", "red"), # 4 colors
  gene_color_palette = "Spectral",
  time_color_palette = "Set2",
  celltype_color_palette = "Pastel1"
)

Using ggsci Palettes

if (requireNamespace("ggsci", quietly = TRUE)) {
  heatmap_colors <- create_single_cell_complex_heatmap(
    seurat_object = seurat_obj,
    features = features,
    gene_classification = gene_groups,
    group_by = "group",
    time_points_order = c("Mock", "6hpi", "24hpi"),
    
    # NEW: Using ggsci color vectors
    gene_color_palette = pal_npg()(3),
    time_color_palette = pal_lancet()(3),
    celltype_color_palette = pal_jama()(4),
    
    # Custom expression heatmap colors
    color_range = c(-2, 0, 2),
    color_palette = c("#2166AC", "#F7F7F7", "#B2182B")
  )
} 

2.2 Using viridis and Custom Colors

if (requireNamespace("ggsci", quietly = TRUE)) {
  heatmap_colors <- create_single_cell_complex_heatmap(
    seurat_object = seurat_obj,
    features = features,
    gene_classification = gene_groups,
    group_by = "group",
    time_points_order = c("Mock", "6hpi", "24hpi"),
    
    # NEW: Using ggsci color vectors
    gene_color_palette = pal_npg()(3),
    time_color_palette = pal_lancet()(3),
    celltype_color_palette = pal_jama()(4),
    
    # Custom expression heatmap colors
    color_range = c(-2, 0, 2),
    color_palette = c("#2166AC", "#F7F7F7", "#B2182B")
  )
} 

Font and Size Adjustments

# Publication-ready styling
heatmap_publication <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = all_genes,
  gene_classification = gene_groups,
  group_by = "group",
  max_circle_size = 2.5,
  row_fontsize = 12,
  col_fontsize = 12,
  row_title_fontsize = 14,
  col_title_fontsize = 12,
  percentage_legend_title = "Fraction of cells",
  percentage_legend_labels = c("0", "20", "40", "60", "80"),
  legend_side = "right"
)

Visual Element Control

This test demonstrates how to remove cell borders and column annotations for a cleaner look.

heatmap_con <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  gene_classification = gene_groups,
  group_by = "group",
  
  # NEW: Visual control parameters
  show_cell_borders = FALSE,
  show_column_annotation = FALSE,
  
  # Other parameters for a clean plot
  split_by = "none",
  cluster_cells = TRUE
)

Gene Name Mapping

This test shows how to replace default gene names (e.g., symbols) with custom labels on the y-axis.

# Create a mapping for a subset of genes
gene_mapping <- c(
  "CD3D" = "T-cell Receptor CD3d",
  "CD79A" = "B-cell Antigen Receptor CD79a",
  "GZMK" = "Granzyme K",
  "NKG7" = "Natural Killer Cell Granule Protein 7"
)

heatmap_map <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  gene_classification = gene_groups,
  group_by = "group",
  
  # NEW: Gene name mapping
  gene_name_mapping = gene_mapping,
  
  row_fontsize = 9
)

Clustering Control

You can control clustering behavior for both genes and cells:

# Custom clustering
heatmap_clustering <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  group_by = "group",
  cluster_cells = TRUE,
  cluster_features = TRUE,
  clustering_distance_rows = "pearson",
  clustering_method_rows = "ward.D2",
  clustering_distance_cols = "euclidean",
  clustering_method_cols = "complete"
)

Different Use Cases

Case 1: Time Course Analysis

For time course experiments, focus on temporal patterns:

# Time course focused analysis
heatmap_time <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  group_by = "group",
  time_points_order = c("Mock", "6hpi", "24hpi"),
  cell_types_order = c("T_cell", "B_cell", "Monocyte"),
  split_by = "time",
  show_celltype_annotation = TRUE,
  show_time_annotation = TRUE
)

Case 2: Cell Type Comparison

For cell type-focused analysis:

# Cell type focused analysis
heatmap_celltype <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  group_by = "celltype",
  split_by = "celltype",
  show_time_annotation = FALSE,
  show_celltype_annotation = TRUE
)

Case 3: Simple Expression Analysis

For basic expression visualization without grouping:

# Simple analysis
heatmap_sample <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  gene_classification = NULL,  # No gene grouping
  group_by = "group",
  show_time_annotation = FALSE,
  show_celltype_annotation = FALSE,
  split_by = "none"
)

Comprehensive Example

This final example combines several new and existing features to create a highly customized, publication-ready plot.

create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  gene_classification = gene_groups,
  group_by = "group",
  time_points_order = c("Mock", "6hpi", "24hpi"),
  
  # --- New Features ---
  gene_group_title = "Functional Category",
  time_point_title = "Time Post-Infection",
  cell_type_title = "Cell Identity",
  show_cell_borders = TRUE,
  cell_border_color = "white",
  gene_name_mapping = c("MS4A1" = "CD20"),
  
  # --- Color Customization ---
  color_range = c(-2, 0, 2),
  color_palette = c("#0072B2", "white", "#D55E00"), # Colorblind-friendly
  gene_color_palette = "Dark2",
  time_color_palette = "Set2",
  celltype_color_palette = "Paired",
  
  # --- Layout and Font ---
  row_fontsize = 10,
  col_fontsize = 9,
  row_title_fontsize = 12,
  col_title_fontsize = 12,
  col_name_rotation = 45,
  legend_side = "right",
  merge_legends = TRUE,
  
  # --- Clustering and Splitting ---
  cluster_features = FALSE, # Rows are already grouped
  cluster_cells = FALSE,    # Columns are already grouped
  split_by = "time"
)

Working with Helper Functions

The package provides helper functions for step-by-step analysis:

Step 1: Prepare Expression Matrices

# Prepare matrices
matrices <- prepare_expression_matrices(
  seurat_object = seurat_obj,
  features = features,
  group_by = "group",
  idents = NULL  # Use all groups
)

# Check the structure
dim(matrices$exp_mat)
#> [1] 5 9
dim(matrices$percent_mat)
#> [1] 5 9
head(matrices$dotplot_data)
#>         avg.exp pct.exp features.plot             id avg.exp.scaled
#> CD3D  105.56974   25.00          CD3D   24hpi_B_cell     0.40882740
#> CD3E   27.84701   18.75          CD3E   24hpi_B_cell    -0.90555550
#> CD8A   20.66799   12.50          CD8A   24hpi_B_cell     0.91030585
#> IL32   70.30069   37.50          IL32   24hpi_B_cell    -0.07758342
#> CD79A  56.25748   31.25         CD79A   24hpi_B_cell     0.58606789
#> CD3D1 192.27796   62.50          CD3D 24hpi_Monocyte     1.25892500

Step 2: Create Gene Annotations

# Create gene annotations
if (!is.null(gene_groups)) {
  gene_ann <- create_gene_annotations(
    exp_mat = matrices$exp_mat,
    percent_mat = matrices$percent_mat,
    gene_classification = gene_groups,
    color_palette = "Set1"
  )
  
  # Check results
  dim(gene_ann$exp_mat_ordered)
  levels(gene_ann$annotation_df$GeneGroup)
}
#> [1] "T_cell_markers"     "B_cell_markers"     "Activation_markers"

Step 3: Create Cell Annotations

# Create cell annotations
cell_ann <- create_cell_annotations(
  exp_mat = matrices$exp_mat,
  percent_mat = matrices$percent_mat,
  time_points_order = c("Mock", "6hpi", "24hpi"),
  cell_types_order = c("T_cell", "B_cell", "Monocyte"),
  show_time_annotation = TRUE,
  show_celltype_annotation = TRUE
)

# Check results
dim(cell_ann$exp_mat_ordered)
#> [1] 5 9
head(cell_ann$annotation_df)
#>              id Time Point Cell Type
#> 1   Mock_T_cell       Mock    T_cell
#> 2   Mock_B_cell       Mock    B_cell
#> 3 Mock_Monocyte       Mock  Monocyte
#> 4   6hpi_T_cell       6hpi    T_cell
#> 5   6hpi_B_cell       6hpi    B_cell
#> 6 6hpi_Monocyte       6hpi  Monocyte

Saving and Exporting

You can save plots directly from the function:

# Save plot to file
heatmap_saved <- create_single_cell_complex_heatmap(
  seurat_object = seurat_obj,
  features = features,
  gene_classification = gene_groups,
  group_by = "group",
  save_plot = "my_heatmap.png",
  plot_width = 12,
  plot_height = 10,
  plot_dpi = 300
)

Tips and Best Practices

1. Data Preparation

2. Color Selection

3. Performance Optimization

4. Publication Guidelines

Troubleshooting

Common Issues

  1. “Features not found”: Check that gene names match exactly with your Seurat object
  2. “group_by column not found”: Verify the metadata column name exists
  3. Empty heatmap: Check that your idents parameter includes valid groups
  4. Clustering errors: Ensure you have enough features for clustering algorithms

Getting Help

If you encounter issues:

  1. Check the function documentation: ?create_single_cell_complex_heatmap
  2. Review the examples in this vignette
  3. File an issue on GitHub: https://github.com/FanXuRong/SingleCellComplexHeatMap/issues

Session Information

sessionInfo()
#> R version 4.3.3 (2024-02-29)
#> Platform: x86_64-conda-linux-gnu (64-bit)
#> Running under: CentOS Linux 7 (Core)
#> 
#> Matrix products: default
#> BLAS/LAPACK: /public3/home/fanxr/bin/miniforge3/envs/Monocle2/lib/libopenblasp-r0.3.28.so;  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=zh_CN.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=zh_CN.UTF-8        LC_COLLATE=C              
#>  [5] LC_MONETARY=zh_CN.UTF-8    LC_MESSAGES=zh_CN.UTF-8   
#>  [7] LC_PAPER=zh_CN.UTF-8       LC_NAME=C                 
#>  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#> [11] LC_MEASUREMENT=zh_CN.UTF-8 LC_IDENTIFICATION=C       
#> 
#> time zone: NA
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] viridis_0.6.5                  viridisLite_0.4.2             
#> [3] ggsci_3.2.0                    dplyr_1.1.4                   
#> [5] Seurat_5.1.0                   SeuratObject_5.0.2            
#> [7] sp_2.1-4                       SingleCellComplexHeatMap_0.1.2
#> 
#> loaded via a namespace (and not attached):
#>   [1] RColorBrewer_1.1-3     jsonlite_1.8.9         shape_1.4.6.1         
#>   [4] magrittr_2.0.3         spatstat.utils_3.1-2   farver_2.1.2          
#>   [7] rmarkdown_2.29         GlobalOptions_0.1.2    vctrs_0.6.5           
#>  [10] ROCR_1.0-11            Cairo_1.6-2            spatstat.explore_3.3-4
#>  [13] htmltools_0.5.8.1      sass_0.4.9             sctransform_0.4.1     
#>  [16] parallelly_1.41.0      KernSmooth_2.23-26     bslib_0.8.0           
#>  [19] htmlwidgets_1.6.4      ica_1.0-3              plyr_1.8.9            
#>  [22] plotly_4.10.4          zoo_1.8-12             cachem_1.1.0          
#>  [25] igraph_2.0.3           mime_0.12              lifecycle_1.0.4       
#>  [28] iterators_1.0.14       pkgconfig_2.0.3        Matrix_1.6-5          
#>  [31] R6_2.5.1               fastmap_1.2.0          fitdistrplus_1.2-2    
#>  [34] future_1.34.0          shiny_1.10.0           clue_0.3-66           
#>  [37] digest_0.6.37          colorspace_2.1-1       patchwork_1.3.0       
#>  [40] S4Vectors_0.40.2       tensor_1.5             RSpectra_0.16-2       
#>  [43] irlba_2.3.5.1          progressr_0.15.1       spatstat.sparse_3.1-0 
#>  [46] httr_1.4.7             polyclip_1.10-7        abind_1.4-5           
#>  [49] compiler_4.3.3         withr_3.0.2            doParallel_1.0.17     
#>  [52] fastDummies_1.7.5      MASS_7.3-60.0.1        rjson_0.2.23          
#>  [55] tools_4.3.3            lmtest_0.9-40          httpuv_1.6.15         
#>  [58] future.apply_1.11.3    goftest_1.2-3          glue_1.8.0            
#>  [61] nlme_3.1-165           promises_1.3.2         grid_4.3.3            
#>  [64] Rtsne_0.17             cluster_2.1.8          reshape2_1.4.4        
#>  [67] generics_0.1.3         gtable_0.3.6           spatstat.data_3.1-4   
#>  [70] tidyr_1.3.1            data.table_1.17.2      BiocGenerics_0.48.1   
#>  [73] spatstat.geom_3.3-4    RcppAnnoy_0.0.22       ggrepel_0.9.6         
#>  [76] RANN_2.6.2             foreach_1.5.2          pillar_1.10.1         
#>  [79] stringr_1.5.1          spam_2.11-1            RcppHNSW_0.6.0        
#>  [82] later_1.4.1            circlize_0.4.16        splines_4.3.3         
#>  [85] lattice_0.22-6         survival_3.8-3         deldir_2.0-4          
#>  [88] tidyselect_1.2.1       ComplexHeatmap_2.16.0  miniUI_0.1.1.1        
#>  [91] pbapply_1.7-2          knitr_1.49             gridExtra_2.3         
#>  [94] IRanges_2.36.0         scattermore_1.2        stats4_4.3.3          
#>  [97] xfun_0.50              matrixStats_1.5.0      stringi_1.8.4         
#> [100] lazyeval_0.2.2         yaml_2.3.10            evaluate_1.0.3        
#> [103] codetools_0.2-20       tibble_3.2.1           cli_3.6.3             
#> [106] uwot_0.2.2             xtable_1.8-4           reticulate_1.40.0     
#> [109] munsell_0.5.1          jquerylib_0.1.4        Rcpp_1.0.14           
#> [112] globals_0.16.3         spatstat.random_3.3-2  png_0.1-8             
#> [115] spatstat.univar_3.1-1  parallel_4.3.3         ggplot2_3.5.1         
#> [118] dotCall64_1.2          listenv_0.9.1          scales_1.3.0          
#> [121] ggridges_0.5.6         leiden_0.4.3.1         purrr_1.0.2           
#> [124] crayon_1.5.3           GetoptLong_1.0.5       rlang_1.1.5           
#> [127] cowplot_1.1.3

mirror server hosted at Truenetwork, Russian Federation.