1. 放大扇形
默认情况下,扇形的宽度(即角度大小)是根据数据范围自动确定的,一般也不会去修改其宽度。如果修改扇形的大小,会使得不同分类之间不好比较。
在某些情况下,我们可能想通过设置扇形的宽度来凸显其内部结构,或突出展示扇形内的某一区域的信息。
要突出显示扇形区域,最简单的方式是将其作为一个新的分类,添加到数据中,并设置对应扇形的宽度
例如,我们有如下数据
df <- data.frame(
sectors = sample(letters[1:6], 400, replace = TRUE),
x = rnorm(400),
y = rnorm(400),
stringsAsFactors = FALSE
)
现在,我们想讲扇形 a
和扇形 b
的前 10
个点进行放大,先提取对应的数据
zoom_df_a <- df[df$sectors == "a", ]
zoom_df_b <- df[df$sectors == "b", ]
zoom_df_b <- zoom_df_b[order(zoom_df_b[, 2])[1:10], ]
zoom_df <- rbind(zoom_df_a, zoom_df_b)
然后为这两份数据添加扇形名称,并追加到绘图数据中
zoom_df$sectors <- paste0("zoom_", zoom_df$sectors)
df2 <- rbind(df, zoom_df)
现在,需要为不同扇形分配宽度,原始数据和放大区域的数据应为 1:1
,即各占半圆
# 根据极差分配宽度
xrange <- tapply(df2$x, df2$sectors, function(x) max(x) - min(x))
normal_sector_index <- unique(df$sectors)
zoomed_sector_index <- unique(zoom_df$sectors)
# 将宽度标准化为 1
sector.width <- c(xrange[normal_sector_index] / sum(xrange[normal_sector_index]),
xrange[zoomed_sector_index] / sum(xrange[zoomed_sector_index]))
> xrange
a b c d e f zoom_a zoom_b
6.597049 5.010210 4.674496 4.286670 4.439024 4.881668 6.597049 1.885344
> sector.width
d f e a b c zoom_a zoom_b
0.1434191 0.1633259 0.1485164 0.2207174 0.1676266 0.1563946 0.7777344 0.2222656
绘制轨迹
# 设置起始角度,并关闭溢出警告
circos.par(start.degree = 90, points.overflow.warning = FALSE)
# 在初始化时使用 sector.width 指定各扇形的宽度
circos.initialize(df2$sectors, x = df2$x, sector.width = sector.width)
circos.track(
df2$sectors, x = df2$x, y = df2$y,
panel.fun = function(x, y) {
circos.points(x, y, col = "red", pch = 16, cex = 0.5)
# 添加扇形名称
circos.text(
CELL_META$xcenter,
CELL_META$cell.ylim[2] + mm_y(2),
CELL_META$sector.index,
niceFacing = TRUE
)
})
我们可以添加链接,用于标识放大区域的来源。颜色我们采用 16
进制 RBGA
格式,后两位为透明度(即 80
)
circos.link(
"a", get.cell.meta.data("cell.xlim", sector.index = "a"),
"zoom_a", get.cell.meta.data("cell.xlim", sector.index = "zoom_a"),
border = NA, col = "#dfc27d80"
)
circos.link(
"b", c(zoom_df_b[1, 2], zoom_df_b[10, 2]),
"zoom_b", get.cell.meta.data("cell.xlim", sector.index = "zoom_b"),
rou1 = get.cell.meta.data("cell.top.radius", sector.index = "b"),
border = NA, col = "#80cdc180"
)
circos.clear()
2. 局部可视化
如果我们只是想显示某一类别的数据,可以在绘制轨迹时指定 sectors
为某一分类。或者在 circos.par()
函数中设置 canvas.xlim
和 canvas.ylim
参数的值,可以显示圆形的某一部分。
画布中 x
和 y
的值都是在 [-1,1]
范围之间,如果设置 x
、y
都为 [0,1]
则只显示四分之一圆。例如
df <- data.frame(
sectors = rep("a", 100),
x = runif(100),
y = runif(100)
)
sectors <- letters[1:4]
# 按列排列两幅图
par(mfcol = c(1, 2))
circos.par("start.degree" = 90, "gap.degree" = 0)
# 将整个圆分割为 4 个扇形
circos.initialize(sectors, xlim = c(0, 1))
# 绘制第一个四分之一圆
circos.track(
df$sectors, x = df$x, y = df$y,
panel.fun = function(x, y) {
circos.points(x, y, pch = 16, cex = 0.5, col = 2)
}
)
circos.track(df$sectors, x = df$x, y = df$y,
panel.fun = function(x, y) {
circos.lines(1:100/100, y, col = 3)
})
circos.clear()
# 添加矩形框和数值
rect(0, 0, 1, 1)
text(0, 0, 0, col = "red", adj = c(0.5, 1))
text(1, 0, 1, col = "red", adj = c(0.5, 1))
text(0, 1, 1, col = "red", adj = c(0.5, 0))
# 设置画布范围,以及间隔,间隔 270 意味着宽度为 90
par(mar = c(1, 1, 1, 1))
circos.par("canvas.xlim" = c(0, 1), "canvas.ylim" = c(0, 1),
"start.degree" = 90, "gap.after" = 270)
circos.initialize(sectors = df$sectors, xlim = c(0, 1))
circos.track(
df$sectors, x = df$x, y = df$y,
panel.fun = function(x, y) {
circos.points(x, y, pch = 16, cex = 0.5, col = 2)
}
)
circos.track(
df$sectors, x = df$x, y = df$y,
panel.fun = function(x, y) {
circos.lines(1:100/100, y, col = 3)
}
)
circos.clear()
# 添加外框以及数值
box()
par(xpd = NA)
text(0, 0, 0, col = "red", adj = c(0.5, 1))
text(1, 0, 1, col = "red", adj = c(0.5, 1))
text(0, 1, 1, col = "red", adj = c(0.5, 0))
获取代码:https://github.com/dxsbiocc/learn/blob/main/R/plot/circos_part.R
在某一单元格绘制图形的方式有两种,上面是通过指定扇形对应的数据的方式来绘制的,还有一种方式是,先创建一个空的轨迹,然后使用 circos.update()
来添加图形
circos.initialize(sectors, xlim = c(0, 1))
circos.track(
df$sectors, x = df$x, y = df$y,
panel.fun = function(x, y) {
circos.points(x, y, pch = 16, cex = 0.5, col = 5)
}
)
# 先创建空轨迹,然后添加图形
circos.track(ylim = range(df$y), bg.border = NA)
circos.update(sector.index = "a", bg.border = "black")
circos.points(df$x, df$y, pch = 16, cex = 0.5, col = 4)
circos.track(sectors = sectors, ylim = c(0, 1))
circos.clear()
3. 多图组合
circlize
是基于基础的 R
图形系统,使用 par(new = TRUE)
可以将一个新的图形作为一个新的图层,添加到之前的画布中。
通过与 canvas.xlim
和 canvas.ylim
参数结合使用,可以绘制出更加复杂的组合图
例如,我们可以组合两个包含不同分类的圆形
# 获取当前图形参数,用于复原
op <- par(no.readonly = TRUE)
# 设置一行三列图形排布
par(mar = c(2, 2, 2, 2), mfrow = c(1, 3))
# 1. 第一幅图
plot_circos1 <- function() {
sectors <- letters[1:4]
circos.initialize(sectors = sectors, xlim = c(0, 1))
circos.trackPlotRegion(
ylim = c(0, 1),
panel.fun = function(x, y) {
circos.text(
0.5, 0.5, "inner circos", col = 6,
niceFacing = TRUE, facing = "bending.outside"
)
}
)
circos.clear()
}
plot_circos1()
# 添加外框
box()
# 添加坐标轴
axis(side = 1)
axis(side = 2)
# 2. 第二幅图
# 设置更大的 canvas.xlim 和 canvas.ylim
# 同样的 xlim = c(0, 1),会绘制更小的圆形
plot_circos2 <- function() {
circos.par("canvas.xlim" = c(-2, 2), "canvas.ylim" = c(-2, 2))
sectors <- letters[1:3]
circos.initialize(sectors = sectors, xlim = c(0, 1))
circos.trackPlotRegion(
ylim = c(0, 1),
panel.fun = function(x, y) {
circos.text(
0.5, 0.5, "inner circos", col = 7,
niceFacing = TRUE, facing = "bending.outside"
)
}
)
circos.clear()
}
plot_circos2()
box()
axis(side = 1)
axis(side = 2)
# 3. 第三幅图
plot_circos1()
# 添加图层
par(new = TRUE)
plot_circos2()
# 复原参数
par(op)
获取代码:https://github.com/dxsbiocc/learn/blob/main/R/plot/circos_combine_circular.R
也可以将一个圆形分割为不同的块,每块之间相互分离。例如,通过合并两个圆形,每个圆形绘制一半,可以达到将圆形分离为两部分的目的
# 获取当前图形参数,用于复原
op <- par(no.readonly = TRUE)
# 设置一行三列图形排布
par(mar = c(2, 2, 2, 2), mfrow = c(1, 3))
# 1. 第一幅图
plot_circos1 <- function() {
circos.par("canvas.xlim" = c(-1, 1.5), "canvas.ylim" = c(-1, 1.5), start.degree = -45)
circos.initialize(sectors = letters[1:4], xlim = c(0, 1))
# 添加空轨迹
circos.trackPlotRegion(ylim = c(0, 1), bg.col = NA, bg.border = NA)
# 绘制 a、b 两个扇形区域,并添加文本
circos.updatePlotRegion(sector.index = "a")
circos.text(0.5, 0.5, "first one", niceFacing = TRUE,
facing = "bending.outside")
circos.updatePlotRegion(sector.index = "b")
circos.text(0.5, 0.5, "first one", niceFacing = TRUE,
facing = "bending.outside")
highlight.sector(
c("a", "b"), track.index = 1,
col = "#5aae6180"
)
circos.clear()
}
plot_circos1()
# 添加外框和轴
box()
axis(side = 1)
axis(side = 2)
# 2. 第二幅图
plot_circos2 <- function() {
circos.par("canvas.xlim" = c(-1.5, 1), "canvas.ylim" = c(-1.5, 1), start.degree = -45)
circos.initialize(sectors = letters[1:4], xlim = c(0, 1))
circos.trackPlotRegion(ylim = c(0, 1), bg.col = NA, bg.border = NA)
# 绘制 c、d 两个扇形区域,并添加文本
circos.updatePlotRegion(sector.index = "d")
circos.text(0.5, 0.5, "second one", niceFacing = TRUE,
facing = "bending.outside")
circos.updatePlotRegion(sector.index = "c")
circos.text(0.5, 0.5, "second one", niceFacing = TRUE,
facing = "bending.outside")
highlight.sector(
c("d", "c"), track.index = 1,
col = "#9970ab80"
)
circos.clear()
}
plot_circos2()
# 添加外框和轴
box()
axis(side = 1)
axis(side = 2)
# 3. 第三幅图
plot_circos1()
# 添加图层
par(new = TRUE)
plot_circos2()
# 复原参数
par(op)
获取代码:https://github.com/dxsbiocc/learn/blob/main/R/plot/circos_combine_separated.R
最后一个例子,可以为不同扇形设置不同的半径,例如
sectors <- letters[1:4]
lim = c(1, 1.1, 1.2, 1.3)
# 绘制 4 个圆形
for(i in 1:4) {
# 每个圆形设置不同的范围
circos.par("canvas.xlim" = c(-lim[i], lim[i]),
"canvas.ylim" = c(-lim[i], lim[i]),
"track.height" = 0.4)
circos.initialize(sectors, xlim = c(0, 1))
# 添加空白轨迹
circos.track(ylim = c(0, 1), bg.border = NA)
# 每个圆绘制一个扇形
circos.update(sector.index = sectors[i], bg.border = "black",
bg.col = i + 1)
circos.points(runif(10), runif(10), pch = 21, col = "black", bg = "white")
circos.clear()
par(new = TRUE)
}
par(new = FALSE)
4. 多图排列
circlize
是基于基础 R
图形系统的,所以,可以使用 par
或 layout
来排列多张圆形图。前面的例子中,我们使用的都是 par
函数的 mfrow
(按行排列)或 mfcol
(按列排列)来排列多个图形。
下面,举例说明 layout
的使用
layout(matrix(1:9, 3, 3))
for(i in 1:9) {
sectors = 1:6
par(mar = c(0.5, 0.5, 0.5, 0.5))
circos.par(cell.padding = c(0, 0, 0, 0))
circos.initialize(sectors, xlim = c(0, 1))
circos.track(ylim = c(0, 1), track.height = 0.05,
bg.col = rand_color(8), bg.border = NA)
for(i in 1:5) {
se = sample(1:6, 2)
circos.link(se[1], runif(2), se[2], runif(2),
col = rand_color(1, transparency = 0.4), border = NA)
}
circos.clear()
}