1 1. 环境准备

need <- c("tidyverse", "ggridges")
for (p in need) if (!requireNamespace(p, quietly = TRUE)) install.packages(p)
library(tidyverse)
library(ggridges)

可选:如果需要中文字体、粗体标题等,可以在此处使用 showtext 加载系统字体。

2 2. 生成与示例一致结构的“宽表”数据

行是 gene,列是 6 种细胞类型(注意包含空格的 "Naive T_1"),数值是“表达量”。不同细胞类型分布的均值与方差略有差异,以便形成可区分的分布形状。

n_gene    <- 60
gene_ids  <- sprintf("ENSG%011d", sample(1e6:2e6, n_gene))
celltypes <- c("Activated_T_1","HSC_1","pDC_1","NK_1","Naive T_1","B_1")

pars <- tibble::tibble(
  celltype = celltypes,
  mu = c(2.5, 2.45, 1.7, 1.75, 1.55, 1.9),
  sd = c(0.50, 0.55, 0.40, 0.45, 0.35, 0.40)
)

mat <- sapply(seq_len(nrow(pars)), function(i) {
  p <- pars[i, ]
  pmax(0, rnorm(n_gene, mean = p$mu, sd = p$sd))  # 去掉负数
})
colnames(mat) <- celltypes

dt <- as.data.frame(mat, check.names = FALSE)   # 允许 "Naive T_1" 中的空格
rownames(dt) <- gene_ids

dplyr::glimpse(dt)
## Rows: 60
## Columns: 6
## $ Activated_T_1 <dbl> 2.739711, 3.186531, 1.926158, 1.274651, 2.048718, 2.3012…
## $ HSC_1         <dbl> 2.214168, 1.685839, 2.587993, 2.425241, 1.691519, 2.4186…
## $ pDC_1         <dbl> 2.2770109, 1.9473736, 1.0929460, 1.2718167, 1.9542178, 1…
## $ NK_1          <dbl> 2.4254951, 1.6314599, 1.3724899, 1.5585572, 2.1258247, 1…
## $ `Naive T_1`   <dbl> 1.5833058, 1.1894117, 1.2095039, 1.4992016, 2.0699164, 0…
## $ B_1           <dbl> 1.806713, 1.663321, 1.717159, 2.098401, 1.569358, 2.0424…

3 3. 宽表转长表(适合 ggplot)

dtt <- dt |>
  tibble::rownames_to_column("gene") |>
  tidyr::pivot_longer(-gene, names_to = "celltype", values_to = "exp") |>
  dplyr::mutate(celltype = factor(celltype, levels = celltypes))

head(dtt)

4 4. 科研配色

我们自定义一个干净的主题,并用 viridisLite 生成分类型颜色。若你已有品牌色,只需替换 pal

theme_sci <- function(base_size = 12) {
  ggplot2::theme_minimal(base_size = base_size) +
    ggplot2::theme(
      panel.grid.major.x = element_blank(),
      panel.grid.minor = element_blank(),
      axis.title = element_text(face = "bold"),
      axis.text.x = element_text(angle = 25, hjust = 1),
      legend.position = "top",
      legend.title = element_blank(),
      plot.title = element_text(face = "bold", size = base_size + 2)
    )
}

pal <- viridisLite::viridis(length(celltypes), option = "viridis", begin = 0.08, end = 0.92)
pal
## [1] "#481E6FFF" "#3B518BFF" "#287C8EFF" "#20A486FF" "#5EC962FF" "#C9E120FF"

5 5. 组合叠加图:小提琴 + 箱线 + 抖动散点

library(showtext)
font_add("Heiti TC Light", regular = "STHeiti Light.ttc")
showtext_auto()

p_combo <- ggplot(dtt, aes(x = celltype, y = exp, fill = celltype, color = celltype)) +
  geom_violin(alpha = 0.85, scale = "width", trim = TRUE) +
  geom_boxplot(width = 0.28, fill = "white", alpha = 0.98,
               outlier.shape = 16, outlier.size = 1.2, colour = "white") +
  geom_point(position = position_jitter(width = 0.10, height = 0),
             size = 1.2, alpha = 0.65, show.legend = FALSE) +
  scale_fill_manual(values = pal) +
  scale_color_manual(values = pal) +
  labs(x = "celltype", y = "exp", title = "小提琴 + 箱线 + 抖动散点") +
  theme_sci()

p_combo
Violin + Boxplot + Jitter 组合(配色)

Violin + Boxplot + Jitter 组合(配色)

若样本量差异很大,可把 geom_violin(scale = "width") 改为 scale = "area" 或者给 geom_boxplot(varwidth = TRUE),强调样本量。

6 6. 山脊图:核密度 + 中位数线 + 刻痕/抖动

使用 geom_density_ridges() 叠加:
- 核密度形状
- 中位数白线quantile_lines = TRUE, quantiles = 2
- 竖线刻痕/点jittered_points = TRUE + point_shape = "|"),帮助定位样本分布

p_ridge <- ggplot(dtt, aes(x = exp, y = celltype, fill = celltype)) +
  geom_density_ridges(
    alpha = 0.88, color = "white",
    rel_min_height = 0.01,
    scale = 1.8,
    quantile_lines = TRUE, quantiles = 2,
    jittered_points = TRUE,
    point_shape = "|", point_size = 4,
    position = position_points_jitter(height = 0)
  ) +
  scale_fill_manual(values = pal) +
  labs(x = "exp", y = "celltype", title = "山脊图:核密度 + 中位数线 + 刻痕") +
  theme_sci()

p_ridge
Ridgeline with density, median lines, and rug-like ticks

Ridgeline with density, median lines, and rug-like ticks

7 7. 导出图片(可选)

dir.create("figs", showWarnings = FALSE)
ggsave("figs/combo_violin_box_jitter.png", p_combo, width = 8, height = 5, dpi = 320)
ggsave("figs/ridges_density_quantile.png", p_ridge, width = 8, height = 5.8, dpi = 320)

8 8. 关键参数速查(教学笔记)