✅环形布局(circular layout) 是一种高效的可视化方式,可以用来展示大量信息。R 语言中的 circlize 软件包实现了这种环形布局的生成,并且对现有软件功能进行了扩展。
这个包的灵活性来源于它使用了底层绘图函数,因此用户可以根据自己的需求,轻松地定义更高层次的图形函数,用来实现特定的可视化目的。
此外,circlize 与 R 语言强大的计算与可视化环境无缝衔接,使用户可以更方便、更自由地设计图形,以便更好地理解多维数据背后的复杂模式。
📌参考网址:https://jokergoo.github.io/circlize_book/book/make-fun-of-the-package.html
set.seed(999)n = 1000df = data.frame(sectors = sample(letters[1:8], n, replace = TRUE),x = rnorm(n), y = runif(n))library(circlize)circos.par("track.height" = 0.1)circos.initialize(df$sectors, x = df$x)circos.track(df$sectors, y = df$y,panel.fun = function(x, y) {circos.text(CELL_META$xcenter,CELL_META$cell.ylim[2] + mm_y(5),CELL_META$sector.index)circos.axis(labels.cex = 0.6)})col = rep(c("#FF0000", "#00FF00"), 4)circos.trackPoints(df$sectors, df$x, df$y, col = col, pch = 16, cex = 0.5)circos.text(-1, 0.5, "text", sector.index = "a", track.index = 1)bgcol = rep(c("#EFEFEF", "#CCCCCC"), 4)circos.trackHist(df$sectors, df$x, bin.size = 0.2, bg.col = bgcol, col = NA)circos.track(df$sectors, x = df$x, y = df$y,panel.fun = function(x, y) {ind = sample(length(x), 10)x2 = x[ind]y2 = y[ind]od = order(x2)circos.lines(x2[od], y2[od])})circos.track(ylim = c(0, 1), panel.fun = function(x, y) {xlim = CELL_META$xlimylim = CELL_META$ylimbreaks = seq(xlim[1], xlim[2], by = 0.1)n_breaks = length(breaks)circos.rect(breaks[-n_breaks], rep(ylim[1], n_breaks - 1),breaks[-1], rep(ylim[2], n_breaks - 1),col = rand_color(n_breaks), border = NA)})circos.link("a", 0, "b", 0, h = 0.4)circos.link("c", c(-0.5, 0.5), "d", c(-0.5,0.5), col = "red",border = "blue", h = 0.2)circos.link("e", 0, "g", c(-1,1), col = "green", border = "black", lwd = 2, lty = 2)circos.clear()
🧩 有三个“坐标系统”
数据坐标系统(data coordinate system)
就像普通散点图那样,有 x 轴、y 轴。
x 和 y 的范围是你原始数据的数值范围。
每个扇区(cell)都有自己独立的坐标范围。
极坐标系统(polar coordinate system)
circlize 会把这些矩形坐标“折”成圆形布局。
比如:x 的值变成角度,y 的值变成从圆心向外的半径。
这样所有数据就被映射到一个圆上。
画布坐标系统(canvas coordinate system)
最后 circlize 再把极坐标变成图形设备上实际绘制的位置(屏幕或 PDF 的像素坐标)。
这是 R 真正绘图的地方。
🌀当 circlize 画多条轨道(tracks)时,是从外圈开始,一圈一圈往内圈画。
第一个 circos.track() 生成的轨道在最外面,第二个在里面,依次往圆心方向堆叠。
绘制环形图的步骤很简单,必须按照以下顺序进行:
initialize layout(初始化布局)
用
circos.initialize()或circos.initializeWithIdeogram()定义哪些扇区(sectors)存在,以及每个扇区的 x 轴范围。
这一步就相当于“画圆的骨架”。
create track(创建轨道)
用
circos.track()或circos.trackPlotRegion()。每个轨道(track)是圆上一圈“带状区域”。
一个圆可以有多条轨道,从外圈到内圈排列。
add graphics(添加图形元素)
在已有的轨道上加点、线、文字、矩形等。
比如
circos.points(),circos.lines(),circos.text(),circos.rect()等。可以随时添加,只要该轨道已经存在。
重复 create track → add graphics
可以继续创建新的轨道,再往里面加图形。
clear(清理)
最后用
circos.clear()清空画布,为下一幅图做准备。不清理的话,下次画图可能会受前一次布局影响。
√ circlize 的完整绘图流程是:
初始化布局 → 创建轨道 → 在轨道里加图形(推荐用 panel.fun) → 重复添加更多轨道 → 清理画布。
library(circlize)# 数据df <- data.frame(sector = rep(letters[1:4], each = 10),x = runif(40),y = runif(40))# 1. 初始化圆circos.initialize(factors = letters[1:4],xlim = cbind(c(0,0,0,0), c(1,1,1,1)))# 2. 创建轨道circos.track(ylim = c(0, 1))# 3. 手动遍历每个扇区,添加点和线for(s in letters[1:4]) {df2 <- subset(df, sector == s)circos.points(df2$x, df2$y, sector.index = s, col = "blue", pch = 16)circos.lines(df2$x, df2$y, sector.index = s, col = "red")}circos.clear()
🧩手动添加图形
先创建轨道;
再自己按扇区筛数据;
用 circos.points()、circos.lines() 等函数画图;
用 sector.index 告诉 circlize 你要画在哪个扇区。
👉 一个环形布局是由 扇区(sectors) 和 轨道(tracks) 组成的。
sector(扇区):
相当于圆的一块“蛋糕切片”,通常代表一个类别或一个染色体(在基因组图中)。
比如:有 8 个扇区,就像圆被切成 8 份。track(轨道):
是围绕圆的一条“带状区域”,一圈一圈往里排。
每条轨道可以放一种图层,比如散点图、柱状图、线图等。
🎯 红色圆环 → 表示一条轨道(一整圈)。
蓝色扇形 → 表示一个扇区(圆的一部分)
🧱 cell(单元格)
当一个 扇区 和 一条 轨道 相交时,就得到一个“格子”——这就是 cell(单元格)。
每个 cell 就像一个独立的小绘图区域(类似普通图的坐标系)。
在 circlize 里,你所有的数据点(点、线、柱等)其实都是画在这些 cell 里的。
✅ circlize 的圆形图结构 =
扇区(sector) × 轨道(track) → 组成很多 cell(绘图单元)。
sectors = c("d", "f", "e", "c", "g", "b", "a")s1 = factor(sectors)circos.initialize(s1, xlim = c(0, 1))s2 = factor(sectors, levels = sectors)circos.initialize(s2, xlim = c(0, 1))
顺序自定义
✅ 用
circos.initialize()建立环形布局时:每个扇区对应一个分类;
扇区的宽度与它的 x 范围成比例;
x 范围可以:
自动计算(传入 x 向量);
或者手动指定(用 xlim 矩阵或固定区间)。
circos.track(sectors, y = y)circos.track(sectors, ylim = c(0, 1))circos.track(sectors, x = x, y = y)# assume `sectors` only covers a subset of sectors# You will only see cells that are covered in `sectors` have borderscircos.track(sectors, y = y)# You will see all cells have borderscircos.track(ylim = c(0, 1))circos.track(ylim = ranges(y))
最后 circlize 再把极坐标变成图形设备上实际绘制的位置(屏幕或 PDF 的像素坐标)。
🧩 在环形图里,x 方向的范围(横向)是由 sector 决定的,而 y 方向的范围(纵向)是由 track 决定的。
也就是说——
同一个扇区里,所有轨道的横坐标范围一样;
同一条轨道上,所有扇区的纵坐标范围一样。
🧮在 circlize 的环形图中:
x 范围(沿圆周)由
circos.initialize()决定,所有轨道共享;y 范围(径向)由
circos.track()决定,同一轨道共享;所以定义一个新轨道时,只需指定
ylim = c(最小值, 最大值)即可。
🧩 circos.par() 函数
🧭 1️⃣ start.degree —— 起始角度
决定第一个扇区从哪个角度开始画。
角度是用标准极坐标系的方向(逆时针)来计算的。
🔹 2️⃣ gap.degree —— 扇区间的间隔角度
控制相邻两个扇区之间的空隙角度(单位:度)。
可以是:
-
一个数:所有扇区之间间距相同;
一个向量:每个间隙独立设置(长度 = 扇区数量)。
注意:第一个间隙在第一个扇区之后。
🔹 3️⃣ gap.after
和
gap.degree本质一样,只是名字更直观。改
gap.after会同步修改gap.degree,反之亦然。
🎨 4️⃣ track.margin —— 轨道的外边距(margin)
类似网页 CSS 里的 margin:是轨道绘图区外侧的留白。
只控制上下边距(左右间距由
gap.after决定)。
🎨 5️⃣ cell.padding —— 单元格内边距(padding)
也是类似 CSS 的概念,是绘图区内部的留白,位于边框之内。
四个值依次代表:下、左、上、右。
⚙️ 6️⃣ unit.circle.segments
circlize 画圆或曲线时,其实是用很多小直线段拼成的。
这个参数控制“圆被分成多少个小段”:
-
数值越大 → 曲线更光滑,但 PDF 文件更大;
数值越小 → 曲线更粗糙,但文件更小。
🌀 7️⃣ track.height
控制每条轨道的默认高度(相对于圆半径的比例)。
⚠️ 8️⃣ points.overflow.warning
circlize 默认会警告你,如果有点、线或文字画出了绘图区域外。
📏 9️⃣ canvas.xlim / canvas.ylim
控制整个绘图画布(Canvas)在 x 和 y 方向上的范围。
默认都是
c(-1, 1),也就是单位圆正好放在画布中心。
🔲 🔳 10️⃣ circle.margin
控制整个圆形图相对于画布边界的外边距。
有三种写法:
-
length = 1:四个方向都一样;length = 2:第一个值控制左右,第二个控制上下;length = 4:依次控制左、右、下、上。
🔄 11️⃣ clock.wise
控制画扇区的顺序方向。
-
TRUE(默认):按顺时针方向排列扇区;FALSE:逆时针。
🔁 12️⃣ xaxis.clock.wise
控制每个 cell 中 x 轴方向是否是顺时针。
从 circlize 0.4.11 版本开始才有。
默认为
TRUE(顺时针),可改成FALSE让 x 轴反向。
🧩
先用
circos.track()创建轨道(指定 y 范围或数据);如果不指定
sectors,会为所有扇区创建相同设置;若给出
sectors,x,y,长度必须相同;最推荐在
panel.fun中加图形,circlize 会自动分配数据;可以通过
bg.col、bg.border或在panel.fun中手动画背景,实现不同扇区的样式;可用
CELL_META$cell.xlim/ylim来获取当前扇区的绘图区范围。
🧩
panel.fun 是 circlize 绘图的核心函数。
它在每个扇区创建后立即执行;
circlize 会自动把该扇区的数据(x、y)传进去;
你可以在其中自由地画点、线、文字;
如果
x或y没提供,对应的参数就是NULL。
library(EBImage)circos.par("points.overflow.warning" = FALSE)circos.initialize(pokemon_name, xlim = c(0, 1))circos.track(ylim = c(0, 1), panel.fun = function(x, y) {pos = circlize:::polar2Cartesian(circlize(CELL_META$xcenter, CELL_META$ycenter))image = EBImage::readImage(pokemon_src[CELL_META$sector.numeric.index])circos.text(CELL_META$xcenter, CELL_META$cell.ylim[1] - mm_y(2),CELL_META$sector.index, facing = "clockwise", niceFacing = TRUE,adj = c(1, 0.5), cex = 0.6)rasterImage(image,xleft = pos[1, 1] - 0.05, ybottom = pos[1, 2] - 0.05,xright = pos[1, 1] + 0.05, ytop = pos[1, 2]+ 0.05)}, bg.border = 1, track.height = 0.15)
好啦,本期分享分享就到这结束啦,感谢大家关注支持,祝大家科研顺利~




