Author: XueCheng
Date: 2025-06-24

# 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

- Dual Information Display: Shows both expression intensity and percentage in a single visualization
- Gene Grouping: Organize genes by functional categories with color-coded annotations
- Time Course Support: Handle time series data with proper ordering and visualization
- Cell Type Annotations: Annotate and group different cell types
- Flexible Display Options: Show/hide different annotation layers
- Highly Customizable: Extensive parameters for colors, sizes, fonts, and layouts
- Seurat Integration: Direct integration with Seurat objects

# 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

- Ensure your metadata columns are properly formatted
- Use consistent naming conventions for time points and cell types
- Consider the number of features - too many can make the plot cluttered

## 2. Color Selection

- Use diverging color palettes for expression data
- Ensure sufficient contrast for accessibility
- Consider colorblind-friendly palettes

## 3. Performance Optimization

- For large datasets, consider subsetting cells or using representative clusters
- Reduce visual complexity with smaller circle sizes and fonts
- Use `merge_legends = TRUE` for cleaner layouts

## 4. Publication Guidelines

- Use high DPI (300+) for publication figures
- Ensure font sizes are readable in the final format
- Include scale bars and legends
- Consider splitting complex plots into multiple panels

# 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