高分Panel复现系列|单细胞数据下给你的富集通路接上蛋白/基因

高分Panel复现系列|单细胞数据下给你的富集通路接上蛋白/基因
单细胞语境下的“蛋白/基因 → 细胞状态”连接图。左侧展示差异蛋白或 marker弧线表示这些蛋白归入不同免疫细胞激活状态右侧气泡图再补充每个细胞状态的比例、显著性和命中数量。图片来源项目内容文章Spatial multi-omics implicate the interaction between Tpex and B cells in tertiary lymphoid structures after neoadjuvant therapy期刊/年份Cancer Discovery2026 OnlineFirst图号原文截图面板 FDOI/链接https://doi.org/10.1158/2159-8290.CD-25-0806这篇文章聚焦新辅助治疗后三级淋巴结构中 Tpex 与 B 细胞互作。截图这个 panel 用蛋白/基因集合连接到不同免疫细胞激活状态例如 T cell activation、B cell activation、Myeloid leukocyte activation 等。图片解读这类图可以拆成三层左侧蛋白/基因列表每一行是一个蛋白或基因旁边短色条可以标记来源、分组或蛋白类别。中间弧线流向每条弧线表示该蛋白被归入某个单细胞免疫状态。右侧气泡图横轴是 Protein ratio颜色表示-log10(p-value)点大小表示 Protein count。这里的核心表达是哪些蛋白集合更集中地指向 T 细胞、B 细胞、髓系细胞、肥大细胞或巨噬细胞相关激活状态。输入数据建议准备两张输入表。1. 蛋白-细胞状态连接表protein_cellstate_links.csv列名含义protein_label左侧展示的蛋白或基因名gene_y蛋白在纵轴上的排列位置cell_state蛋白对应的单细胞状态cell_state_y_link弧线连接到细胞状态色块的位置tick_color左侧短色条颜色2. 细胞状态统计表cellstate_enrichment.csv列名含义cell_state细胞状态名称protein_count命中蛋白数量protein_ratio命中蛋白比例neg_log10_p-log10(p-value)cell_state_color细胞状态颜色cell_state_y细胞状态在纵轴上的中心位置library(tidyverse)library(scales)links-read_csv(protein_cellstate_links.csv,show_col_typesFALSE)cell_info-read_csv(cellstate_enrichment.csv,show_col_typesFALSE)需要示例数据的后台添加小编领取调整好数据结构以下代码可以直接复制粘贴运行。第一步固定细胞状态顺序和颜色cell_levels-cell_info$cell_state links-links|mutate(cell_statefactor(cell_state,levelscell_levels))cell_info-cell_info|mutate(cell_statefactor(cell_state,levelscell_levels))cell_cols-setNames(cell_info$cell_state_color,cell_info$cell_state)第二步定义右侧气泡图区域这里把气泡图作为整体画布中的一个小区域来画。这样更容易还原原图中“弧线图 右侧嵌入气泡图”的版式。panel_xmin-6.45panel_xmax-8.35panel_ymin-0panel_ymax-max(cell_info$cell_state_y4.8)dot_df-cell_info|mutate(dot_xrescale(protein_ratio,toc(panel_xmin0.18,panel_xmax-0.18),fromc(0,0.18)),dot_yc(47,35,22,11,5),dot_colcol_numeric(palettec(#1d2cff,#7c169f,#e23b2e,#ff0000),domainc(5,32))(neg_log10_p),dot_sizerescale(protein_count,toc(2.4,10.5),fromc(5,65)))第三步准备图例和细胞状态色块x_ticks-tibble(ratioc(0.05,0.10),xrescale(ratio,toc(panel_xmin0.18,panel_xmax-0.18),fromc(0,0.18)))color_legend-tibble(xseq(panel_xmin0.18,panel_xmin0.85,length.out80),y91,valueseq(5,32,length.out80))|mutate(fill_colcol_numeric(palettec(#1d2cff,#7c169f,#e23b2e,#ff0000),domainc(5,32))(value))size_legend-tibble(xc(panel_xmin0.18,panel_xmin0.70,panel_xmin1.26),y76,countc(20,40,60),point_sizec(3.7,5.6,7.6))cell_bar_df-cell_info|mutate(xmin5.98,xmax6.18,ymincell_state_y-4.8,ymaxcell_state_y4.8)第四步绘制蛋白到细胞状态的弧线p-ggplot()geom_curve(datalinks,aes(x0.64,ygene_y,xend5.86,yendcell_state_y_link,colorcell_state),curvature-0.34,linewidth0.24,alpha0.36)geom_segment(datalinks,aes(x0.50,xend0.62,ygene_y,yendgene_y,colortick_color),linewidth1.0,show.legendFALSE)geom_text(datalinks,aes(x0.47,ygene_y,labelprotein_label),hjust1,size1.34,colorblack)第五步添加细胞状态色块和名称p-pgeom_rect(datacell_bar_df,aes(xminxmin,xmaxxmax,yminymin,ymaxymax,fillcell_state),colorNA,show.legendFALSE)geom_text(datacell_info,aes(x5.83,ycell_state_y,labelcell_state),hjust1,size4.0,colorblack)第六步添加右侧气泡图p-pannotate(rect,xminpanel_xmin,xmaxpanel_xmax,yminpanel_ymin,ymaxpanel_ymax,fillNA,colorblack,linewidth0.55)geom_point(datadot_df,aes(xdot_x,ydot_y),colordot_df$dot_col,sizedot_df$dot_size)geom_segment(datax_ticks,aes(xx,xendx,ypanel_ymin,yendpanel_ymin-1.15),linewidth0.45,colorblack)geom_text(datax_ticks,aes(xx,ypanel_ymin-3.0,labelsprintf(%.2f,ratio)),size2.85,colorblack)annotate(text,xmean(c(panel_xmin,panel_xmax)),ypanel_ymin-7.0,labelProtein ratio,size3.8)第七步添加图例并导出p-pgeom_tile(datacolor_legend,aes(xx,yy),fillcolor_legend$fill_col,width0.012,height1.6)annotate(text,xpanel_xmin0.54,y95.0,label-log10(p-value),size3.4)annotate(text,xpanel_xmin0.25,y87.7,label10,size3.0)annotate(text,xpanel_xmin0.53,y87.7,label20,size3.0)annotate(text,xpanel_xmin0.78,y87.7,label30,size3.0)annotate(text,xpanel_xmin0.62,y81.5,labelProtein count,size3.2)geom_point(datasize_legend,aes(xx,yy),sizesize_legend$point_size,colorblack)geom_text(datasize_legend,aes(xx,yy-4.6,labelcount),size2.85)scale_color_manual(valuesc(cell_cols,setNames(cell_info$cell_state_color,cell_info$cell_state_color)))scale_fill_manual(valuescell_cols)coord_cartesian(xlimc(0,8.70),ylimc(-9.5,132),clipoff)theme_void()theme(legend.positionnone,plot.marginmargin(8,8,8,5))ggsave(protein_cellstate_flow_bubble.png,p,width6.0,height8.2,dpi360,bgwhite)ggsave(protein_cellstate_flow_bubble.pdf,p,width6.0,height8.2,bgwhite)完整代码library(tidyverse)library(scales)links-read_csv(protein_cellstate_links.csv,show_col_typesFALSE)cell_info-read_csv(cellstate_enrichment.csv,show_col_typesFALSE)cell_levels-cell_info$cell_state links-links|mutate(cell_statefactor(cell_state,levelscell_levels))cell_info-cell_info|mutate(cell_statefactor(cell_state,levelscell_levels))cell_cols-setNames(cell_info$cell_state_color,cell_info$cell_state)panel_xmin-6.45panel_xmax-8.35panel_ymin-0panel_ymax-max(cell_info$cell_state_y4.8)dot_df-cell_info|mutate(dot_xrescale(protein_ratio,toc(panel_xmin0.18,panel_xmax-0.18),fromc(0,0.18)),dot_yc(47,35,22,11,5),dot_colcol_numeric(palettec(#1d2cff,#7c169f,#e23b2e,#ff0000),domainc(5,32))(neg_log10_p),dot_sizerescale(protein_count,toc(2.4,10.5),fromc(5,65)))x_ticks-tibble(ratioc(0.05,0.10),xrescale(ratio,toc(panel_xmin0.18,panel_xmax-0.18),fromc(0,0.18)))color_legend-tibble(xseq(panel_xmin0.18,panel_xmin0.85,length.out80),y91,valueseq(5,32,length.out80))|mutate(fill_colcol_numeric(palettec(#1d2cff,#7c169f,#e23b2e,#ff0000),domainc(5,32))(value))size_legend-tibble(xc(panel_xmin0.18,panel_xmin0.70,panel_xmin1.26),y76,countc(20,40,60),point_sizec(3.7,5.6,7.6))cell_bar_df-cell_info|mutate(xmin5.98,xmax6.18,ymincell_state_y-4.8,ymaxcell_state_y4.8)p-ggplot()geom_curve(datalinks,aes(x0.64,ygene_y,xend5.86,yendcell_state_y_link,colorcell_state),curvature-0.34,linewidth0.24,alpha0.36)geom_segment(datalinks,aes(x0.50,xend0.62,ygene_y,yendgene_y,colortick_color),linewidth1.0,show.legendFALSE)geom_text(datalinks,aes(x0.47,ygene_y,labelprotein_label),hjust1,size1.34,colorblack)geom_rect(datacell_bar_df,aes(xminxmin,xmaxxmax,yminymin,ymaxymax,fillcell_state),colorNA,show.legendFALSE)geom_text(datacell_info,aes(x5.83,ycell_state_y,labelcell_state),hjust1,size4.0,colorblack)annotate(rect,xminpanel_xmin,xmaxpanel_xmax,yminpanel_ymin,ymaxpanel_ymax,fillNA,colorblack,linewidth0.55)geom_point(datadot_df,aes(xdot_x,ydot_y),colordot_df$dot_col,sizedot_df$dot_size)geom_segment(datax_ticks,aes(xx,xendx,ypanel_ymin,yendpanel_ymin-1.15),linewidth0.45,colorblack)geom_text(datax_ticks,aes(xx,ypanel_ymin-3.0,labelsprintf(%.2f,ratio)),size2.85,colorblack)annotate(text,xmean(c(panel_xmin,panel_xmax)),ypanel_ymin-7.0,labelProtein ratio,size3.8)geom_tile(datacolor_legend,aes(xx,yy),fillcolor_legend$fill_col,width0.012,height1.6)annotate(text,xpanel_xmin0.54,y95.0,label-log10(p-value),size3.4)annotate(text,xpanel_xmin0.25,y87.7,label10,size3.0)annotate(text,xpanel_xmin0.53,y87.7,label20,size3.0)annotate(text,xpanel_xmin0.78,y87.7,label30,size3.0)annotate(text,xpanel_xmin0.62,y81.5,labelProtein count,size3.2)geom_point(datasize_legend,aes(xx,yy),sizesize_legend$point_size,colorblack)geom_text(datasize_legend,aes(xx,yy-4.6,labelcount),size2.85)scale_color_manual(valuesc(cell_cols,setNames(cell_info$cell_state_color,cell_info$cell_state_color)))scale_fill_manual(valuescell_cols)coord_cartesian(xlimc(0,8.70),ylimc(-9.5,132),clipoff)theme_void()theme(legend.positionnone,plot.marginmargin(8,8,8,5))ggsave(protein_cellstate_flow_bubble.png,p,width6.0,height8.2,dpi360,bgwhite)ggsave(protein_cellstate_flow_bubble.pdf,p,width6.0,height8.2,bgwhite)复现结果参考链接PubMedhttps://pubmed.ncbi.nlm.nih.gov/42339989/DOIhttps://doi.org/10.1158/2159-8290.CD-25-0806