The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

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

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.