R语言绘制环形树状图:从数据准备到ggtree出图全攻略

发布时间:2026/10/5 15:56:57
R语言绘制环形树状图:从数据准备到ggtree出图全攻略 如果你经常处理聚类、层级关系的数据迟早会遇到一个需求把一棵树画成圆形。环形树状图circular dendrogram就是把传统的树状图按照极坐标卷成一个圈既能在有限的画布里塞进更多分类节点视觉上也比直线树更容易做“中心发散”式的信息展示。我用R画环形树状图已经好几年期间试过ape、ggtree、ggraph、circlize等一堆包踩过的坑足够写一篇长文。今天这篇就来拆解R绘制环形树状图的过程从数据格式讲到出图细节把这条路上最实用的方案都盘一遍。适合正在做物种分类、基因家族聚类、话题聚合、目录结构可视化的朋友也适合被各种包绕晕的R语言新手。1. 环形树状图的本质与R生态选型1.1 环形树状图解决了什么问题树状图是展示层级关系最经典的方式从根节点开始不断二分或多分最后到达每一个叶子节点。比如16S扩增子测序里的物种注释树、基因家族进化树、文档目录结构、用户聚类结果本质上都是一棵树。但直线型树状图有一个很现实的问题叶子一多图就变得特别宽。40个叶子可能还好上百个叶子的时候标签几乎挤成一条线。环形树状图把根节点放在圆心分支朝外辐射叶子均匀分布在圆周上等于把那条越来越长的“横轴”卷了起来。这样一来同样的画布面积可以展示更多叶子中心区域还能腾出来叠加热图、点图、柱状图等信息层信息密度一下子高很多。另一个好处是视觉重心集中。环形结构天然有一种“从中心向外探索”的引导感适合做汇报、论文配图、Dashboard可视化。对于需要展示“层级分组数值”三重信息的场景环形树状图几乎是最合适的选择。1.2 R生态里的主流方案对比R里能做环形树状图的包不少但路线差别很大。ggtree是Bioconductor上的系统发育树可视化包底层是ggplot2用图层语法组织图形元素。它专门为树状图设计对phylo对象支持极好还能通过%%把外部数据关联到树上再按分组着色、加标签、叠加热图。ggraph是ggplot2的“图可视化”扩展包面向网络图和树图内置多种布局算法。dendrogram布局配合circular参数也能画环形树状图适合和igraph、tidygraph生态一起用。ape是系统发育分析的经典包负责树的读取、操作、计算。它本身也能画简易树图但画不出漂亮的环形效果通常需要配合circlize或作为ggtree的数据来源。circlize是Circular Layout的专用包主要用在基因组圈图、复杂热图、轨道图等场景。理论上也能画环形聚类树但它的定位不是专门画树树干细节、标签旋转、子树操作都需要手动处理学习成本高。工具安装来源核心优势主要局限推荐场景ggtreeBioconductor树图专用图层语法生态丰富依赖较多版本更新快科研树图、物种注释树、聚类树ggraphCRAN布局灵活网络图树图通吃树形布局控制不如ggtree精细网络图与树图混搭apeCRAN树对象读写与操作的标准工具绘图能力一般树数据处理、配合其他包circlizeCRAN环形布局能力强轨道系统强大画树不是核心功能配置繁琐圈图、热图轨道、复杂环形可视化1.3 我为什么最终把ggtree当主力个人实测下来ggtree是最适合“用R画环形树状图”的选择原因很直接它把树当作ggplot对象来处理你可以像搭积木一样往上面加图层画完底色再加标签、加高亮、加外部注释每一步都能检查。对经常要反复调整的科研汇报图来说这种工作流太重要了。ggtree对phylo对象的支持非常彻底。ape读取树之后直接丢给ggtree就能画中间不需要转换。它还有一个设计得非常好的点用%%操作符把数据框关联到树节点上。一旦关联好就能直接在aes()里使用数据框的列名比如按分组着色、按数值调点大小。这种玩法在ape原生绘图里很难实现。当然ggtree也有小毛病版本升级偶尔会改参数名早期版本的layout参数和现在就不完全一样中文字体在不同操作系统上的处理也需要单独设置。但这些坑都有成熟的解决方案后面会详细写。2. 数据准备从Newick到phylo对象2.1 树文件格式基础画树之前先得有一棵“树”。R生态里树的标准对象是phylo它本质是一个列表里面保存了节点关系、叶子名称、分支长度等信息。但大多数时候我们拿到的不是phylo对象而是文本格式的树文件。最常见的文本格式是Newick也叫New Hampshire格式。它用括号嵌套表达层级关系冒号后面跟分支长度。比如((A:0.1,B:0.2):0.3,C:0.4);这棵树的含义是A和B先聚成一个内部节点分支长度分别是0.1和0.2这个内部节点再到C分支长度0.4整个根部分的内部节点到根的距离是0.3。括号的嵌套层次就是树的拓扑结构。Nexus格式比Newick更复杂带文件头块常见于MrBayes、PAUP等软件输出。R默认不会自动识别这两种格式需要靠ape包来读取。phylo对象的结构包括edge两列矩阵表示每条有向边的起点和终点节点ID。tip.label叶子节点名称向量。Nnode内部节点数量。edge.length每条边的长度可选。很多ggtree报错归根结底是传进来的对象不是合法phylo或者树文件本身有问题。2.2 用ape读取和校验树文件使用ape读取树文件是最基础也最稳妥的操作。install.packages(ape) library(ape) # 读取Newick格式 tree - read.tree(tree.nwk) # 读取Nexus格式 tree2 - read.nexus(tree.nex) # 查看树对象结构 str(tree)读进来之后不要急着画先做几个基本校验# 检查是否为二叉树 is.binary(tree) # 检查是否为超度量树所有叶子到根距离一致 is.ultrametric(tree) # 检查叶子名称是否有重复 sum(duplicated(tree$tip.label)) # 查看叶子数量 Ntip(tree)这些检查看起来简单实际上能救回大量时间。我遇到过文件本身没问题、但叶子名里有隐藏空格的情况倒是read.tree读进来不报错可后续分组关联时怎么都对不上排查半天才发现是名称匹配出了问题。所以在数据准备阶段多花一分钟后面能省一小时。2.3 从分组表构建树对象不是所有场景都有现成的Newick文件。更多时候你手里是一张样本表或者距离矩阵需要先聚类再画树。这个过程通常用hclust完成然后转成phylo对象。set.seed(42) # 造一个10x10的距离矩阵示例 mat - matrix(runif(100), nrow 10) rownames(mat) - paste0(Sample, 1:10) # 计算距离并做层次聚类 d - dist(mat) hc - hclust(d, method ward.D2) # 转成phylo对象 library(ape) tree - as.phylo(hc)这里的关键是as.phylo()方法。ape提供了从hclust对象转phylo的接口转换过后分支长度就是hclust合并时的高度。如果不需要展示真实距离只想看拓扑结构到ggtree里画图时用branch.lengthnone即可。如果手里的是分组关系表比如物种分类层级表也可以用其他方式构建树。常见做法是先构造一个“父节点-子节点”对应表再借助tidytree或ape的相应函数构建phylo对象。对于纯新手我更推荐先用hclust路线因为距离矩阵和聚类结果是最容易理解的数据形态。2.4 数据清洗与常见数据坑树数据清洗的重点不复杂但每一条都可能让人抓狂。第一是重复名称。如果两个叶子节点叫同一个名字ggtree画出来可能不报错但做分组关联、旋转节点、折叠分支时结果会很奇怪。建议在读取后立刻检查重复有重复的就改名。第二是根节点位置。有些树文件里带外类群outgroup画出来整棵树的拓扑会显得很“歪”。如果不需要外类群直接用ape::drop.tip()删掉如果需要调整根的位置用ape::root()重新设根。第三是分支长度缺失。读入后tree$edge.length可能为NULL。这种情况画普通树图时会变成“全等长”外观环形布局下反而不难看因为叶子分布均匀。但如果你想让分支长度体现真实距离就一定要保证edge.length存在且非负。data.frame条件有时也会出问题。比如叶子名首尾有空格或者大小写不一致ggtree的%%关联时就会漏匹配。一个比较有效的习惯是在读取后立即用trimws()处理tip.label再做后续操作。3. 实操用ggtree绘制环形树状图全流程3.1 安装环境ggtree不是CRAN包需要从Bioconductor安装。第一步先安装BiocManager再用它安装ggtree。install.packages(BiocManager) BiocManager::install(ggtree)如果公司网络或校园网对Bioconductor源访问较慢可以手动指定镜像options(BioC_mirror https://mirrors.tuna.tsinghua.edu.cn/bioconductor)安装完成后每次使用前加载即可。ggtree会同时加载ggplot2、tidytree等依赖包第一次加载稍慢属于正常现象。3.2 最小可复现案例先用随机生成的树跑一个最小示例确认环境没问题。library(ggtree) library(ape) set.seed(2024) tree - rtree(40) # 随机生成40个叶子的树 # 环形树状图忽略分支长度 p - ggtree(tree, layout circular, branch.length none) print(p)两行核心代码就够。layout circular是环形布局的关键参数branch.length none表示忽略分支长度让所有叶子均匀分布在圆圈上这也是物种分类树里最常用的展示方式。如果想要保留分支长度画出来的环形树会体现真实距离代码更简单p - ggtree(tree, layout circular)此时树的分支会按edge.length的实际值绘制。建议两种方式都试一次对比一下视觉差异然后根据汇报需求选择。看到环形骨架后就可以往上加图层了。3.3 按分组映射颜色与图例环形树状图最常见的增强操作是按分组给树枝和标签着色。这需要先把分组信息关联到树上。# 构造分组信息 group - rep(c(GroupA, GroupB, GroupC), length.out 40) tip_info - data.frame(tip tree$tip.label, group group) # 关联数据并着色 p - ggtree(tree, layout circular, branch.length none) %% tip_info aes(color group) geom_tiplab(size 2.5) scale_color_manual(values c(#E64B35, #4DBBD5, #00A087)) theme(legend.position right) print(p)这里最核心的是%%操作符。它的作用是“把自定义数据框附加到树上”后续aes(color group)才能在树图层里读取到group列。不写这一步color group会报错因为它不知道group从哪里来。细心的读者会发现加了aes(color group)之后树枝和标签都会被分色。如果只想给标签着色可以用geom_tiplab(aes(color group))单独控制如果只想给高亮点着色同样在对应图层里指定。3.4 叠加点、热图、条形图等图层环形布局的中心区域是空的外围也有大量空间非常适合继续叠加信息。先看怎么在叶子上加点和数值映射# 构造一个数值变量 value - rnorm(40) tip_info$value - value p - ggtree(tree, layout circular, branch.length none) %% tip_info geom_tippoint(aes(color value), size 3) scale_color_gradient(low blue, high red) geom_tiplab(size 2, offset 0.2)geom_tippoint会在每个叶子节点位置画点aes(color value)让点颜色按数值连续变化。这样图中的点既是位置标记又是一个连续数值列适合展示丰度、表达量、置信度等指标。如果希望把某个节点之下的整个分支高亮出来用geom_hilight# 先找到某个内部节点 node_id - MRCA(tree, tip c(t1, t10)) p - ggtree(tree, layout circular) geom_hilight(node node_id, fill #F8766D, alpha 0.3)MRCA()是ggtree封装的“寻找最近共同祖先节点”函数只要给两个或多个叶子名称就能找到它们的共同祖先然后以高亮背景圈出这一整个演化支。这个操作在物种差异展示、聚类簇突出显示中特别常用。如果想在环形树外围再加一层柱状图或热图单靠ggtree已经不够需要搭配ggtreeExtra包。它是ggtree的姊妹扩展包专门解决“围绕树的外圈叠加不同图层”的问题这里先不展开后面会介绍基础用法。3.5 出图设置与高清导出画图只是第一步把图导出成论文或汇报可用的高质量文件同样有讲究。# 保存为PDF矢量图 ggsave(circular_tree.pdf, p, width 8, height 8, dpi 300) # 保存为PNG位图 ggsave(circular_tree.png, p, width 8, height 8, dpi 300, bg white)关于尺寸我个人的经验是叶子在40个以内8x8英寸就够了叶子超过60个建议宽度和高度都调到10以上。标签字号也需要配合调整叶子越多标签就要越小否则一定会重叠。在中文环境下导出PNG最容易遇到“中文变方块”的问题。最省事的解决办法是安装showtext包然后在使用前开启自动渲染install.packages(showtext) library(showtext) showtext_auto()showtext会把字体渲染交给系统字体库中文字体通常就不会丢失。也可以直接用cairo_pdf()设备写入PDF文件对中文支持也比较好cairo_pdf(circular_tree_cn.pdf, width 8, height 8) print(p) dev.off()4. 备选方案与进阶美化4.1 用ggraph实现圆形布局ggraph是通用图可视化包如果你已经熟悉tidygraph生态画环形树状图也很快。它的dendrogram布局配合circular TRUE一步就能转成环形。library(ggraph) library(igraph) # 用hclust结果画树 hc - hclust(dist(iris[, 1:4]), method ward.D2) ggraph(hc, layout dendrogram, circular TRUE) geom_edge_elbow() geom_node_point() geom_node_text(aes(label label), size 2, nudge_y 0.1) coord_cartesian(clip off)注意ggraph接受的数据对象比较灵活可以是hclust对象、dendrogram对象也可以是tbl_graph对象。它和ggtree的差异主要体现在图层命名上ggtree用geom_tiplabggraph用geom_node_textggtree用geom_treeggraph用geom_edge_*。ggraph的优势是灵活尤其是画带有网络关系的数据时非常顺手。但如果你的核心需求就是“画一棵环形树”我仍然首推ggtree因为gttree在树节点的筛选、祖先查找、子树旋转等树逻辑上更完善。ggraph更适合“树只是整张大图的一部分”的场景。4.2 ape circlize绘制环形聚类图还有一种场景是不只要树还要在树的外圈叠加很多轨道数据比如基因组测序覆盖度、样品分组带状图、外部热图轨道。这时候circlize反而比ggtree更顺手。library(circlize) # 先做聚类 dend - as.dendrogram(hclust(dist(iris[, 1:4]), method ward.D2)) # 绘制环形聚类树 circlize_dendrogram(dend, labels TRUE)circlize_dendrogram的默认效果比较简洁配合circos.track()可以继续叠加热图、散点、连接线等元素。它适合做“圈图”而不是“树图”定位差异要搞清楚。从实际经验来说如果你做的是一张“以树为主角”的图用ggtree最省心如果你的目标是“一张基因组圈图里面刚好有一棵树要做聚类信息展示”那circlize更合适。两者不是替代关系更像是不同工具链里的最佳选择。4.3 配色与主题定制环形树状图的美化核心是两件事配色和标签。配色方面离散分组变量建议用RColorBrewer的Set2、Paired、Dark2配色连续数值变量建议用viridis配色色觉友好且打印后灰度区分度高library(RColorBrewer) cols - brewer.pal(6, Set2) p - p scale_color_manual(values cols)连续色可以用scale_color_viridis_c()p - p scale_color_viridis_c()标签方面有几个参数值得花时间调试size控制字号叶子多时调小。offset控制标签离树末端的距离通常设为0.1到0.5之间。fontface设为italic常用于物种名的斜体。还有一个很实用的技巧circular布局下ggtree会自动计算标签旋转角度让文字始终沿着圆周方向排列。但如果你觉得旋转后的文字不好读可以强制让标签保持水平p - p geom_tiplab(size 2.5, hjust 0, angle 0)angle 0会把所有标签强制设为水平方向适合叶子数量较少的图。5. 常见问题与排错技巧实录5.1 中文标签变成方块这是出现频率最高的问题。现象很直观标签全部变成一个个小方框。常见原因及解决办法如下R绘图设备里没有中文字体。最省事的是先装showtext并开启showtext_auto()。如果你在Windows上开了showtext仍然无效可以手动指定中文字体library(showtext) font_add(heiti, simhei.ttf) showtext_auto()如果使用PDF输出建议改用cairo_pdf设备它对中文字体的支持好很多。注意先开showtext_auto()再执行ggsave顺序不能反。如果已经画完图再开字体渲染重新保存前最好重新print一次图对象。5.2 标签互相挤压重叠环形树叶子一多标签重叠几乎不可避免。我的排查顺序是这样的第一把图幅调大。这是成本最低的调整。宽度和高度从8改成10、12肉眼可见地缓解。第二把字号调小。geom_tiplab(size 2.5)改成2甚至1.5。科研配图里标签能看清即可不需要大字。第三把标签往外推。增加offset值让标签离树体远一点会给标签之间留出更多空隙。第四如果仍然重叠可以选择只标注部分关心的叶子。构造一个包含“需要显示的标签”的列没想显示的设为NA然后用geom_tiplab(aes(label label_show))画标签。5.3 分支角度和数据对不上有时候发现某个聚类支在图上角度很奇怪或者你想调整某个子树的朝向可以直接在顶层旋转。# 查看节点编号 p_data - p$data # 旋转某个内部节点之下的子树 p_new - rotate(p, node 17)node编号可以直接从p$data里查也可以通过MRCA()寻找node - MRCA(tree, tip c(t1, t5)) p_new - rotate(p, node)注意rotate()返回的是一个新的ggtree对象不是直接修改原对象。所以务必赋值给新变量再print展示。5.4 大数据树卡顿与裁剪当树有几千甚至几万个叶子时ggtree画起来非常吃力打开RStudio预览都能卡半天。这时候不要硬画先做裁剪。常用的裁剪思路有三个第一个用ape::drop.tip()删掉不关心的叶子。比如只想展示丰度Top50的物种就把其他叶子全部删掉。第二个用ape::extract.clade()提取某个感兴趣的子树的叶子。鼠标定位到某一支后单独画那一支。第三个用ggtree的collapse()折叠某个内部节点将大支折叠成一个三角形节点以“压缩”展示面。p - ggtree(tree, layout circular) p - collapse(p, node node_id)折叠后的三角形节点可以通过geom_nodelab()来标记让读者知道这个三角形代表一个大的演化支。5.5 PDF导出后字体丢失PDF导出后字体丢失本质上是字体嵌入问题。Windows下这种情况尤其明显换个环境打开PDF字体就变成替代字体。解决思路使用showtext_auto()后再导出。使用cairo_pdf()导出嵌入字体效果更好。如果期刊要求矢量图建议用cairo_pdf输出后转成EPS或SVG再交给AI或Inkscape微调。5.6 快速排错速查表现象可能原因解决办法读文件报错Newick/Nexus格式不对检查文件头、括号配对换read.tree/read.nexus叶子名对不上分组空格、大小写不一致trimws()清理用%%关联前确认列名aes里找不到列没有用%%关联数据先关联数据框再在图层中使用列名节点编号未知不知道内部节点IDp$data查ID或用MRCA()按叶子找节点中文变方块字体缺少/未渲染showtext_auto()cairo_pdf指定中文字体导出PDF字体变化字体未嵌入使用cairo_pdf或showtext图太卡叶子太多drop.tip裁剪collapse折叠只画子树标签重叠图幅/字号/间距不合适调大画布减小字号增大offset6. 写在最后一些零碎但实用的经验画环形树状图这件事真正花时间的往往不是绘图本身而是数据整理和图形微调。我个人的习惯是先把tip.label检查干净再制定分组和颜色方案最后才动笔画图。颜色方案提前定好能省掉大量反复改aesthetic的功夫。还有一个很推荐的工作流把读树、清理、画图、导出封装成一个可重复执行的R脚本等到需要改色、换字体、调布局时只要改几个参数就能重新出图。对做科研或者经常出数据报告的人来说这种脚本化的方式比每次手动点鼠标高效太多。另外如果一张环形树状图怎么调都显得乱不妨后退一步想想是不是信息太多环形结构擅长展示层级关系但一图塞太多内容反而会让读者抓不到重点。把“树本身”和“外围注释”分成两张图或者把重点信息用高亮、颜色突出通常比在单张图里塞满所有维度更有效。希望这篇文章能帮你少走一些弯路。环形树状图的难点从来不在包而在你想清楚“这棵树要为谁表达什么”剩下的只是把数据交给R而已。祝出图顺利。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询