R 数据可视化 —— circlize 高级布局

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.xlimcanvas.ylim 参数的值,可以显示圆形的某一部分。

画布中 xy 的值都是在 [-1,1] 范围之间,如果设置 xy 都为 [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.xlimcanvas.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 图形系统的,所以,可以使用 parlayout 来排列多张圆形图。前面的例子中,我们使用的都是 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()
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

名本无名

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值