Nature的经典配色是什么?代码如何运用在科研绘图当中呢?

今天小编就来带你开箱:单细胞绘图的方法。快来mark这篇干货推文吧!

一、为什么但细胞比例图这么重要呢?

在单细胞转录组(scRNA-seq)的分析里面,细胞比例图(Cell Propotion Plot)是每一篇高分文章的加分项。

细胞比例图主要回答了:在不同的样本/条件/时间点之间,各细胞亚群的组成比例有没有变化?

这张图往往出现在:疾病 vs 正常对照的细胞组成差异、治疗前后细胞亚群的重塑等变化。

图画得好不好,直接影响了审稿人对数据和文章的第一印象。

二、搞清楚有哪些画法

1. 堆叠柱状图(Stacked Bar Plot):最经典,最常见,信息密度高。适合展示多样本整体组成对比。

2. 分组柱状图(Stacked Bar Plot):适合比较少量细胞类型的精确数值,便于统计检验。

3. 桑基图 / 冲积图(Alluvial / Sankey Plot):适合展示细胞状态转变,视觉冲击力强,Nature 文章最爱。

4. 气泡比例图(Bubble Proportion Plot):适合多维度展示,兼顾比例和统计显著性。

5. 小提琴/箱线图叠加比例图:适合多样本统计检验场景,可以直接展示p值。

三、Nature的配色从哪里来的?

高分文章的配色有三个共同特点:饱和度适中(不刺眼,印刷友好)、色相区分度高(色盲友好)、有层次感(主色+辅色搭配)。

NPG 配色(Nature Publishing Group)

#E64B35 #4DBBD5 #00A087 #3C5488 #F39B7F #8491B4 #91D1C2 #DC0000

D3 Category10(单细胞最常用)

#1F77B4 #FF7F0E #2CA02C #D62728 #9467BD #8C564B #E377C2 #7F7F7F

推荐使用 ggsci 包,一行代码 scale_fill_npg() 即可调用 NPG 配色,无需手动输入色值。

四、代码实践

首先,要准备好代码环境:

<!--
 /* Font Definitions */
 @font-face
	{font-family:"MS Mincho";
	panose-1:2 2 6 9 4 2 5 8 3 4;
	mso-font-charset:128;
	mso-generic-font-family:modern;
	mso-font-pitch:fixed;
	mso-font-signature:-536870145 1791491579 134217746 0 131231 0;}
@font-face
	{font-family:"Cambria Math";
	panose-1:2 4 5 3 5 4 6 3 2 4;
	mso-font-charset:0;
	mso-generic-font-family:roman;
	mso-font-pitch:variable;
	mso-font-signature:-536869121 1107305727 33554432 0 415 0;}
@font-face
	{font-family:Cambria;
	panose-1:2 4 5 3 5 4 6 3 2 4;
	mso-font-charset:0;
	mso-generic-font-family:roman;
	mso-font-pitch:variable;
	mso-font-signature:-536869121 1107305727 33554432 0 415 0;}
@font-face
	{font-family:"\@MS Mincho";
	panose-1:2 2 6 9 4 2 5 8 3 4;
	mso-font-charset:128;
	mso-generic-font-family:modern;
	mso-font-pitch:fixed;
	mso-font-signature:-536870145 1791491579 134217746 0 131231 0;}
 /* Style Definitions */
 p.MsoNormal, li.MsoNormal, div.MsoNormal
	{mso-style-unhide:no;
	mso-style-qformat:yes;
	mso-style-parent:"";
margin-top:0cm;
margin-right:0cm;
margin-bottom:10.0pt;
margin-left:0cm;
line-height:115%;
	mso-pagination:widow-orphan;
font-size:11.0pt;
font-family:"Cambria",serif;
	mso-ascii-font-family:Cambria;
	mso-ascii-theme-font:minor-latin;
	mso-fareast-font-family:"MS Mincho";
	mso-fareast-theme-font:minor-fareast;
	mso-hansi-font-family:Cambria;
	mso-hansi-theme-font:minor-latin;
	mso-bidi-font-family:"Times New Roman";
	mso-bidi-theme-font:minor-bidi;
	mso-fareast-language:EN-US;}
.MsoChpDefault
	{mso-style-type:export-only;
	mso-default-props:yes;
	mso-bidi-font-size:11.0pt;
font-family:"Cambria",serif;
	mso-bidi-font-family:"Times New Roman";
	mso-bidi-theme-font:minor-bidi;
	mso-font-kerning:0pt;
	mso-ligatures:none;
	mso-fareast-language:EN-US;}
.MsoPapDefault
	{mso-style-type:export-only;
margin-bottom:10.0pt;
line-height:115%;}
 /* Page Definitions */
 @page
	{mso-page-border-surround-header:no;
	mso-page-border-surround-footer:no;}
@page WordSection1
	{size:612.0pt 792.0pt;
margin:72.0pt 90.0pt 72.0pt 90.0pt;
	mso-header-margin:36.0pt;
	mso-footer-margin:36.0pt;
	mso-paper-source:0;}
div.WordSection1
	{page:WordSection1;}
-->
install.packages(c("ggplot2",
"dplyr", "tidyr", "ggsci",
"ggalluvial", "RColorBrewer"))
BiocManager::install("scater")

图一:经典堆叠柱状图


<!--
 /* Font Definitions */
 @font-face
	{font-family:"MS Mincho";
	panose-1:2 2 6 9 4 2 5 8 3 4;
	mso-font-charset:128;
	mso-generic-font-family:modern;
	mso-font-pitch:fixed;
	mso-font-signature:-536870145 1791491579 134217746 0 131231 0;}
@font-face
	{font-family:"Cambria Math";
	panose-1:2 4 5 3 5 4 6 3 2 4;
	mso-font-charset:0;
	mso-generic-font-family:roman;
	mso-font-pitch:variable;
	mso-font-signature:-536869121 1107305727 33554432 0 415 0;}
@font-face
	{font-family:Cambria;
	panose-1:2 4 5 3 5 4 6 3 2 4;
	mso-font-charset:0;
	mso-generic-font-family:roman;
	mso-font-pitch:variable;
	mso-font-signature:-536869121 1107305727 33554432 0 415 0;}
@font-face
	{font-family:"\@MS Mincho";
	panose-1:2 2 6 9 4 2 5 8 3 4;
	mso-font-charset:128;
	mso-generic-font-family:modern;
	mso-font-pitch:fixed;
	mso-font-signature:-536870145 1791491579 134217746 0 131231 0;}
 /* Style Definitions */
 p.MsoNormal, li.MsoNormal, div.MsoNormal
	{mso-style-unhide:no;
	mso-style-qformat:yes;
	mso-style-parent:"";
	margin-top:0cm;
	margin-right:0cm;
	margin-bottom:10.0pt;
	margin-left:0cm;
	line-height:115%;
	mso-pagination:widow-orphan;
	font-size:11.0pt;
	font-family:"Cambria",serif;
	mso-ascii-font-family:Cambria;
	mso-ascii-theme-font:minor-latin;
	mso-fareast-font-family:"MS Mincho";
	mso-fareast-theme-font:minor-fareast;
	mso-hansi-font-family:Cambria;
	mso-hansi-theme-font:minor-latin;
	mso-bidi-font-family:"Times New Roman";
	mso-bidi-theme-font:minor-bidi;
	mso-fareast-language:EN-US;}
.MsoChpDefault
	{mso-style-type:export-only;
	mso-default-props:yes;
	mso-bidi-font-size:11.0pt;
	font-family:"Cambria",serif;
	mso-bidi-font-family:"Times New Roman";
	mso-bidi-theme-font:minor-bidi;
	mso-font-kerning:0pt;
	mso-ligatures:none;
	mso-fareast-language:EN-US;}
.MsoPapDefault
	{mso-style-type:export-only;
	margin-bottom:10.0pt;
	line-height:115%;}
 /* Page Definitions */
 @page
	{mso-page-border-surround-header:no;
	mso-page-border-surround-footer:no;}
@page WordSection1
	{size:612.0pt 792.0pt;
	margin:72.0pt 90.0pt 72.0pt 90.0pt;
	mso-header-margin:36.0pt;
	mso-footer-margin:36.0pt;
	mso-paper-source:0;}
div.WordSection1
	{page:WordSection1;}
-->
library(ggplot2)
library(dplyr)
library(ggsci)
set.seed(42)
cell_types <- c("T cell","B cell","NK
cell","Monocyte","DC","Macrophage","Neutrophil","Plasma
cell")
samples <- paste0("Sample_", 1:8)
df <- expand.grid(CellType = cell_types, Sample = samples) %>%
  mutate(Proportion = runif(n(), 0.02,
0.35),
         Group =
ifelse(grepl("1|2|3|4", Sample), "Control",
"Disease")) %>%
  group_by(Sample) %>%
  mutate(Proportion = Proportion /
sum(Proportion)) %>%
  ungroup()
p1 <- ggplot(df, aes(x = Sample, y = Proportion, fill = CellType)) +
  geom_bar(stat = "identity",
width = 0.75, color = "white", linewidth = 0.3) +
  scale_fill_npg() +
  scale_y_continuous(labels =
scales::percent_format()) +
  facet_grid(~ Group, scales =
"free_x", space = "free") +
  labs(title = "Cell Type
Composition Across Samples",
       x = NULL, y = "Cell
Proportion", fill = "Cell Type") +
  theme_classic(base_size = 12) +
  theme(axis.text.x = element_text(angle
= 45, hjust = 1, size = 9),
        strip.background =
element_rect(fill = "#F0F0F0", color = NA),
        strip.text = element_text(face =
"bold", size = 11),
        legend.position =
"right",
        legend.key.size = unit(0.4,
"cm"),
        plot.title = element_text(face =
"bold", hjust = 0.5))
ggsave("stacked_bar_NPG.pdf", p1, width = 9, height = 5, useDingbats
= FALSE)

color = "white" 加白色描边让各色块边界清晰;useDingbats = FALSE 保证 PDF 字体在 Illustrator 中可编辑。

图2:冲积图(Alluvial Plot)——这图好啊,这图可太吸睛了(下面代码建议收藏)

<!--
 /* Font Definitions */
 @font-face
	{font-family:"MS Mincho";
	panose-1:2 2 6 9 4 2 5 8 3 4;
	mso-font-charset:128;
	mso-generic-font-family:modern;
	mso-font-pitch:fixed;
	mso-font-signature:-536870145 1791491579 134217746 0 131231 0;}
@font-face
	{font-family:"Cambria Math";
	panose-1:2 4 5 3 5 4 6 3 2 4;
	mso-font-charset:0;
	mso-generic-font-family:roman;
	mso-font-pitch:variable;
	mso-font-signature:-536869121 1107305727 33554432 0 415 0;}
@font-face
	{font-family:Cambria;
	panose-1:2 4 5 3 5 4 6 3 2 4;
	mso-font-charset:0;
	mso-generic-font-family:roman;
	mso-font-pitch:variable;
	mso-font-signature:-536869121 1107305727 33554432 0 415 0;}
@font-face
	{font-family:"\@MS Mincho";
	panose-1:2 2 6 9 4 2 5 8 3 4;
	mso-font-charset:128;
	mso-generic-font-family:modern;
	mso-font-pitch:fixed;
	mso-font-signature:-536870145 1791491579 134217746 0 131231 0;}
 /* Style Definitions */
 p.MsoNormal, li.MsoNormal, div.MsoNormal
	{mso-style-unhide:no;
	mso-style-qformat:yes;
	mso-style-parent:"";
	margin-top:0cm;
	margin-right:0cm;
	margin-bottom:10.0pt;
	margin-left:0cm;
	line-height:115%;
	mso-pagination:widow-orphan;
	font-size:11.0pt;
	font-family:"Cambria",serif;
	mso-ascii-font-family:Cambria;
	mso-ascii-theme-font:minor-latin;
	mso-fareast-font-family:"MS Mincho";
	mso-fareast-theme-font:minor-fareast;
	mso-hansi-font-family:Cambria;
	mso-hansi-theme-font:minor-latin;
	mso-bidi-font-family:"Times New Roman";
	mso-bidi-theme-font:minor-bidi;
	mso-fareast-language:EN-US;}
.MsoChpDefault
	{mso-style-type:export-only;
	mso-default-props:yes;
	mso-bidi-font-size:11.0pt;
	font-family:"Cambria",serif;
	mso-bidi-font-family:"Times New Roman";
	mso-bidi-theme-font:minor-bidi;
	mso-font-kerning:0pt;
	mso-ligatures:none;
	mso-fareast-language:EN-US;}
.MsoPapDefault
	{mso-style-type:export-only;
	margin-bottom:10.0pt;
	line-height:115%;}
 /* Page Definitions */
 @page
	{mso-page-border-surround-header:no;
	mso-page-border-surround-footer:no;}
@page WordSection1
	{size:612.0pt 792.0pt;
	margin:72.0pt 90.0pt 72.0pt 90.0pt;
	mso-header-margin:36.0pt;
	mso-footer-margin:36.0pt;
	mso-paper-source:0;}
div.WordSection1
	{page:WordSection1;}
-->
library(ggalluvial)
df_alluvial <- data.frame(
  Cluster   = rep(paste0("C", 1:8), each = 3),
  CellType  = rep(cell_types[1:3], 8),
  Condition = rep(c("Normal",
"Early", "Late"), 8),
  Freq      = sample(50:500, 24, replace = TRUE)
)
p3 <- ggplot(df_alluvial,
       aes(axis1 = Cluster, axis2 =
CellType, axis3 = Condition, y = Freq)) +
  geom_alluvium(aes(fill = CellType),
width = 1/12, alpha = 0.75, knot.pos = 0.4) +
  geom_stratum(width = 1/6, fill =
"grey90", color = "grey50", linewidth = 0.3) +
  geom_text(stat = "stratum",
aes(label = after_stat(stratum)), size = 3, fontface = "bold") +
  scale_fill_d3("category10") +
  scale_x_discrete(limits =
c("Cluster", "Cell Type", "Condition"), expand =
c(0.1, 0.1)) +
  labs(title = "Cell State
Transition Across Conditions",
       y = "Cell Count", fill =
"Cell Type") +
  theme_minimal(base_size = 12) +
  theme(panel.grid = element_blank(),
        axis.text.y = element_blank(),
        plot.title = element_text(face =
"bold", hjust = 0.5, size = 14))
ggsave("alluvial_D3.pdf", p3, width = 10, height = 7, useDingbats =
FALSE)

封装函数:一键出图

<!--
 /* Font Definitions */
 @font-face
	{font-family:"MS Mincho";
	panose-1:2 2 6 9 4 2 5 8 3 4;
	mso-font-charset:128;
	mso-generic-font-family:modern;
	mso-font-pitch:fixed;
	mso-font-signature:-536870145 1791491579 134217746 0 131231 0;}
@font-face
	{font-family:"Cambria Math";
	panose-1:2 4 5 3 5 4 6 3 2 4;
	mso-font-charset:0;
	mso-generic-font-family:roman;
	mso-font-pitch:variable;
	mso-font-signature:-536869121 1107305727 33554432 0 415 0;}
@font-face
	{font-family:Cambria;
	panose-1:2 4 5 3 5 4 6 3 2 4;
	mso-font-charset:0;
	mso-generic-font-family:roman;
	mso-font-pitch:variable;
	mso-font-signature:-536869121 1107305727 33554432 0 415 0;}
@font-face
	{font-family:"\@MS Mincho";
	panose-1:2 2 6 9 4 2 5 8 3 4;
	mso-font-charset:128;
	mso-generic-font-family:modern;
	mso-font-pitch:fixed;
	mso-font-signature:-536870145 1791491579 134217746 0 131231 0;}
 /* Style Definitions */
 p.MsoNormal, li.MsoNormal, div.MsoNormal
	{mso-style-unhide:no;
	mso-style-qformat:yes;
	mso-style-parent:"";
	margin-top:0cm;
	margin-right:0cm;
	margin-bottom:10.0pt;
	margin-left:0cm;
	line-height:115%;
	mso-pagination:widow-orphan;
	font-size:11.0pt;
	font-family:"Cambria",serif;
	mso-ascii-font-family:Cambria;
	mso-ascii-theme-font:minor-latin;
	mso-fareast-font-family:"MS Mincho";
	mso-fareast-theme-font:minor-fareast;
	mso-hansi-font-family:Cambria;
	mso-hansi-theme-font:minor-latin;
	mso-bidi-font-family:"Times New Roman";
	mso-bidi-theme-font:minor-bidi;
	mso-fareast-language:EN-US;}
.MsoChpDefault
	{mso-style-type:export-only;
	mso-default-props:yes;
	mso-bidi-font-size:11.0pt;
	font-family:"Cambria",serif;
	mso-bidi-font-family:"Times New Roman";
	mso-bidi-theme-font:minor-bidi;
	mso-font-kerning:0pt;
	mso-ligatures:none;
	mso-fareast-language:EN-US;}
.MsoPapDefault
	{mso-style-type:export-only;
	margin-bottom:10.0pt;
	line-height:115%;}
 /* Page Definitions */
 @page
	{mso-page-border-surround-header:no;
	mso-page-border-surround-footer:no;}
@page WordSection1
	{size:612.0pt 792.0pt;
	margin:72.0pt 90.0pt 72.0pt 90.0pt;
	mso-header-margin:36.0pt;
	mso-footer-margin:36.0pt;
	mso-paper-source:0;}
div.WordSection1
	{page:WordSection1;}
-->
plot_cell_proportion <- function(seurat_obj,
                                 
group_by = "orig.ident",
                                 
cell_type_col = "celltype",
                                  palette
= "npg") {
  df <- seurat_obj@meta.data %>%
    count(.data[[group_by]],
.data[[cell_type_col]]) %>%
    group_by(.data[[group_by]]) %>%
    mutate(prop = n / sum(n)) %>%
    ungroup()
  color_fn <- switch(palette,
    "npg"    = scale_fill_npg(),
    "nejm"   = scale_fill_nejm(),
    "d3"     = scale_fill_d3(),
    "lancet" =
scale_fill_lancet()
  )
  ggplot(df, aes(x = .data[[group_by]], y
= prop, fill = .data[[cell_type_col]])) +
    geom_bar(stat = "identity",
width = 0.75, color = "white", linewidth = 0.3) +
    color_fn +
    scale_y_continuous(labels =
scales::percent_format()) +
    labs(x = NULL, y = "Cell
Proportion", fill = "Cell Type") +
    theme_classic(base_size = 12) +
    theme(axis.text.x =
element_text(angle = 45, hjust = 1))
}
# 使用示例
# plot_cell_proportion(pbmc, group_by = "sample", cell_type_col =
"celltype", palette = "npg")

五、让图拥有“高级感”的5个细节

【字体】 普通:默认 Arial → 高分:Helvetica / Myriad Pro

【描边】 普通:无描边 → 高分:白色细描边 0.3pt

【图例】 普通:右侧大图例 → 高分:精简图例,key.size 缩小

【导出】 普通:PNG 300dpi → 高分:PDF 矢量图

【配色】 普通:默认 ggplot2 彩虹色 → 高分:ggsci NPG / NEJM / D3

六、常见的踩坑!

Q:颜色太多,超过 10 种细胞类型怎么办?
A:用 colorRampPalette 在 NPG 基础上插值扩展,或使用 Polychrome 包的 palette36,专为多类别设计。

Q:PDF 导出后字体变成方块?
A:加 useDingbats = FALSE,或者用 cairo_pdf() 替代 pdf()。

Q:Seurat 对象如何直接提取比例数据?
A:用table(seurat@meta.data$sample, seurat@meta.data$celltype) 提取,再用 prop.table() 转换为比例。

有关图像编辑和图像修改更多的建议,可以也直接在百沐一下中进行提问!

一张好的细胞比例图 = 合适的图形类型 + Nature 级配色 + 精细主题调整 + 矢量格式导出

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐