
单细胞文章里经常会看到这种“上方环形组成图 下方配对折线图”的组合图。它的优点是既能展示每个大类细胞内部亚群组成又能直观看出不同组织状态之间细胞比例的变化方向。图片来源项目内容文章Single-cell landscape of intratumoral heterogeneity and tumor microenvironment remolding in pre-nodal metastases of breast cancer期刊/年份Journal of Translational Medicine, 2024图号Fig. 5g这篇文章整合乳腺癌单细胞数据比较了原发肿瘤和转移前淋巴结相关样本中的细胞组成差异。Fig. 5g 主要展示了 Fibroblast、Macrophage、Endothelial cell 和 B cell 内部亚群在两种状态之间的比例变化。图片解读这是一张典型的 细胞亚群组成变化图。它可以拆成三层上方环形图每个大类细胞各有两个环形图分别对应Primary tumors和Pre-nodal metastases。中间色块标题标出当前 panel 属于哪一类细胞例如 Fibroblast、Macrophage、Endothelial cell、B cell。下方配对折线图每一条线代表一个细胞亚群连接该亚群在两种状态下的比例。读图时重点看线的方向线向上说明该亚群在Pre-nodal metastases中比例升高线向下说明该亚群在Pre-nodal metastases中比例下降线接近水平说明两种状态下比例变化不明显。这类图特别适合展示单细胞分析中的“细胞组成重塑”尤其适合比较不同组织状态、治疗前后或疾病阶段之间的细胞比例变化。输入数据列名含义cell_type大类细胞例如 Fibroblast、Macrophage、Endothelial cell、B cellsubtype细胞亚群例如 iCAF、SPP1 Macrophage、Vein、Naivesample_group分组信息例如 Primary tumors、Pre-nodal metastasescell_prop该细胞亚群在对应分组中的比例数据结构可以理解为cell_typesubtypesample_groupcell_propFibroblastiCAFPrimary tumors0.330FibroblastiCAFPre-nodal metastases0.320MacrophageSPP1 MacrophagePrimary tumors0.480MacrophageSPP1 MacrophagePre-nodal metastases0.395其中最关键的是cell_prop。下方折线图直接使用它作为 y 轴上方环形图则需要在每个cell_type sample_group内部重新归一化得到每个亚群在该大类细胞中的组成占比。library(tidyverse)dat-read_csv(input_data.csv,show_col_typesFALSE)需要示例数据的后台添加小编领取调整好数据结构以下代码可以直接复制粘贴运行。第一步整理分组顺序和绘图标签首先固定细胞大类的显示顺序并把 x 轴分组改成换行标签这样更接近原图排版。library(tidyverse)library(patchwork)library(scales)dat-read_csv(input_data.csv,show_col_typesFALSE)cell_order-c(Fibroblast,Macrophage,Endothelial cell,B cell)dat-dat|mutate(cell_typefactor(cell_type,levelscell_order),group_labelfactor(sample_group,levelsc(Primary tumors,Pre-nodal metastases),labelsc(Primary\ntumors,Pre-nodal\nmetastases)))|group_by(cell_type,sample_group)|mutate(donut_sharecell_prop/sum(cell_prop))|ungroup()这里有一个关键点donut_share是环形图使用的比例它是在每个大类细胞和每个分组内部计算的。第二步设置颜色和点形状原图中不同细胞大类使用不同色系Fibroblast绿色系Macrophage橙棕色系Endothelial cell红色系B cell紫色系。同时不同大类细胞也使用不同点形状便于在图例中区分。pal-c(iCAF#A9CF86,mCAF#73C8BE,vCAF#8DD0AD,SPP1 Macrophage#EEDCCB,FOLR2 Macrophage#EDB97C,IDO1 Macrophage#B87935,CPA3 Macrophage#F1BA43,S100A9 Macrophage#BC8B2F,CXCL9 Macrophage#8D5B2B,Vein#FB7373,Artery#D94355,Capillary#C66B6F,Lymphatic#C90024,Naive#C7B2D8,Memory#6F439F,Plasma cell#8E1F70)strip_col-c(Fibroblast#69AD88,Macrophage#F0B86F,Endothelial cell#EF6D6A,B cell#A79CBB)shape_val-c(Fibroblast22,Macrophage23,Endothelial cell24,B cell25)第三步画上方环形组成图环形图本质上是一个极坐标柱状图。这里用geom_col()先画堆叠柱再用coord_polar()转成环形。make_donut-function(d){ggplot(d,aes(x2,ydonut_share,fillsubtype))geom_col(width1,colorwhite,linewidth0.5)coord_polar(thetay)xlim(0.6,2.5)facet_wrap(~group_label,nrow1)scale_fill_manual(valuespal)guides(fillnone)theme_void()theme(strip.textelement_blank())}这里的xlim(0.6, 2.5)用来制造中间空洞让饼图变成环形图。第四步画中间的细胞类型标题条原图每个 panel 中间都有一个彩色标题条用来标注当前细胞类型。make_strip-function(ct){ggplot()annotate(rect,xmin0,xmax1,ymin0,ymax1,fillstrip_col[[ct]])annotate(text,x0.5,y0.5,labelct,size4.5)coord_cartesian(xlimc(0,1),ylimc(0,1),expandFALSE)theme_void()}这个部分不依赖真实数据只是为了复现原图的 panel 标题风格。第五步画下方配对折线图下方折线图是整张图的核心。每个细胞亚群在两个分组中各有一个点中间用线连接。make_line-function(d,ct,show_yFALSE){y_range-range(d$cell_prop,na.rmTRUE)pad-diff(y_range)*0.120.015ggplot(d,aes(group_label,cell_prop,groupsubtype,colorsubtype))geom_line(linewidth1.0,alpha0.85)geom_point(aes(fillsubtype),shapeshape_val[[ct]],size5.5,alpha0.50,stroke0.25)scale_color_manual(valuespal,nameNULL)scale_fill_manual(valuespal,guidenone)scale_y_continuous(limitsc(max(-0.05,y_range[1]-pad),y_range[2]pad),breakspretty_breaks(4))guides(colorguide_legend(ncol4,byrowTRUE))labs(xNULL,yif(show_y)Cell proportionelseNULL)theme_classic(base_size12)theme(axis.text.xelement_text(angle60,hjust1,vjust1),axis.lineelement_blank(),panel.borderelement_rect(colorblack,fillNA,linewidth0.6),legend.positionbottom)}这里有一个容易报错的小细节。y 轴标题建议写成labs(xNULL,yif(show_y)Cell proportionelseNULL)不要写成labs(xNULL,yifelse(show_y,Cell proportion,NULL))因为ifelse()不适合返回NULL有时会触发 replacement length zero 相关报错。第六步组合单个细胞类型 panel每个细胞类型 panel 由三部分上下拼接环形组成图彩色标题条配对折线图。plot_one_cell-function(ct,show_yFALSE){d-filter(dat,cell_typect)make_donut(d)/make_strip(ct)/make_line(d,ct,show_y)plot_layout(heightsc(0.18,0.08,0.74))}patchwork的/表示上下拼接plot_layout(heights ...)用来控制三层图的高度比例。第七步横向拼接并导出图片最后把四个细胞大类横向拼接并统一收集图例。fig-wrap_plots(plot_one_cell(Fibroblast,TRUE),plot_one_cell(Macrophage),plot_one_cell(Endothelial cell),plot_one_cell(B cell),nrow1)plot_layout(guidescollect)plot_annotation(titleg)theme(legend.positionbottom,legend.boxvertical,legend.key.widthunit(0.9,cm),legend.textelement_text(size8.5),plot.titleelement_text(size24,facebold))ggsave(paired_line_donut_panel_g.png,fig,width12,height8.5,dpi320,bgwhite)ggsave(paired_line_donut_panel_g.pdf,fig,width12,height8.5,bgwhite)复现结果完整代码library(tidyverse)library(patchwork)library(scales)dat-read_csv(input_data.csv,show_col_typesFALSE)cell_order-c(Fibroblast,Macrophage,Endothelial cell,B cell)dat-dat|mutate(cell_typefactor(cell_type,levelscell_order),group_labelfactor(sample_group,levelsc(Primary tumors,Pre-nodal metastases),labelsc(Primary\ntumors,Pre-nodal\nmetastases)))|group_by(cell_type,sample_group)|mutate(donut_sharecell_prop/sum(cell_prop))|ungroup()pal-c(iCAF#A9CF86,mCAF#73C8BE,vCAF#8DD0AD,SPP1 Macrophage#EEDCCB,FOLR2 Macrophage#EDB97C,IDO1 Macrophage#B87935,CPA3 Macrophage#F1BA43,S100A9 Macrophage#BC8B2F,CXCL9 Macrophage#8D5B2B,Vein#FB7373,Artery#D94355,Capillary#C66B6F,Lymphatic#C90024,Naive#C7B2D8,Memory#6F439F,Plasma cell#8E1F70)strip_col-c(Fibroblast#69AD88,Macrophage#F0B86F,Endothelial cell#EF6D6A,B cell#A79CBB)shape_val-c(Fibroblast22,Macrophage23,Endothelial cell24,B cell25)make_donut-function(d){ggplot(d,aes(x2,ydonut_share,fillsubtype))geom_col(width1,colorwhite,linewidth0.5)coord_polar(thetay)xlim(0.6,2.5)facet_wrap(~group_label,nrow1)scale_fill_manual(valuespal)guides(fillnone)theme_void()theme(strip.textelement_blank())}make_strip-function(ct){ggplot()annotate(rect,xmin0,xmax1,ymin0,ymax1,fillstrip_col[[ct]])annotate(text,x0.5,y0.5,labelct,size4.5)coord_cartesian(xlimc(0,1),ylimc(0,1),expandFALSE)theme_void()}make_line-function(d,ct,show_yFALSE){y_range-range(d$cell_prop,na.rmTRUE)pad-diff(y_range)*0.120.015ggplot(d,aes(group_label,cell_prop,groupsubtype,colorsubtype))geom_line(linewidth1.0,alpha0.85)geom_point(aes(fillsubtype),shapeshape_val[[ct]],size5.5,alpha0.50,stroke0.25)scale_color_manual(valuespal,nameNULL)scale_fill_manual(valuespal,guidenone)scale_y_continuous(limitsc(max(-0.05,y_range[1]-pad),y_range[2]pad),breakspretty_breaks(4))guides(colorguide_legend(ncol4,byrowTRUE))labs(xNULL,yif(show_y)Cell proportionelseNULL)theme_classic(base_size12)theme(axis.text.xelement_text(angle60,hjust1,vjust1),axis.lineelement_blank(),panel.borderelement_rect(colorblack,fillNA,linewidth0.6),legend.positionbottom)}plot_one_cell-function(ct,show_yFALSE){d-filter(dat,cell_typect)make_donut(d)/make_strip(ct)/make_line(d,ct,show_y)plot_layout(heightsc(0.18,0.08,0.74))}fig-wrap_plots(plot_one_cell(Fibroblast,TRUE),plot_one_cell(Macrophage),plot_one_cell(Endothelial cell),plot_one_cell(B cell),nrow1)plot_layout(guidescollect)plot_annotation(titleg)theme(legend.positionbottom,legend.boxvertical,legend.key.widthunit(0.9,cm),legend.textelement_text(size8.5),plot.titleelement_text(size24,facebold))ggsave(paired_line_donut_panel_g.png,fig,width12,height8.5,dpi320,bgwhite)ggsave(paired_line_donut_panel_g.pdf,fig,width12,height8.5,bgwhite)参考链接DOI: https://doi.org/10.1186/s12967-024-05625-6文章链接: https://pmc.ncbi.nlm.nih.gov/articles/PMC11363495/