R 數(shù)據(jù)可視化 —— circlize 復(fù)雜圖形與圖例

復(fù)雜圖像

我們可以使用一些簡(jiǎn)單的圖形對(duì)象圆米,將它們組合起來(lái)吊输,繪制成各種復(fù)雜的圖形

1. 圓形柱狀圖

我們介紹過(guò)了各種柱狀圖/條形圖的繪制,如堆積型激率、并列型咳燕,還有徑向柱狀圖,現(xiàn)在我們?cè)俳榻B一種圓形柱狀圖的繪制柱搜。

圓形柱狀圖就是將圓形布局中迟郎,每一條軌跡當(dāng)做一個(gè)柱子。例如聪蘸,我們有 9 個(gè)樣本宪肖,要繪制每個(gè)樣本的腫瘤純度信息

首先,構(gòu)造數(shù)據(jù)

category <- paste0("sample", "_", 1:9)
percent <- sort(sample(40:80, 9))
color <- rev(rainbow(length(percent)))

初始化健爬,以 12 點(diǎn)鐘方向作為起始控乾,同時(shí)只設(shè)置一個(gè)扇形,即整個(gè)圓就是一個(gè) sector

circos.par("start.degree" = 90, cell.padding = c(0, 0, 0, 0))
circos.initialize("a", xlim = c(0, 100))

添加圖形

circos.track(
  ylim = c(0.5, length(percent)+0.5), track.height = 0.8, 
  bg.border = NA,
  panel.fun = function(x, y) {
    xlim = CELL_META$xlim
    # 添加圓形中心線
    circos.segments(rep(xlim[1], 9), 1:9,
                    rep(xlim[2], 9), 1:9,
                    col = "#CCCCCC")
    # 添加 9 個(gè)圓形矩形
    circos.rect(rep(0, 9),
                1:9 - 0.45,
                percent,
                1:9 + 0.45,
                col = color,
                border = "white")
    # 添加文本信息
    circos.text(
      rep(xlim[1], 9),
      1:9,
      paste(category, " - ", percent, "%"),
      facing = "downward",
      adj = c(1.05, 0.5),
      cex = 0.8
    )
    # 添加軸信息
    breaks = seq(0, 85, by = 5)
    circos.axis(
      h = "top",
      major.at = breaks,
      labels = paste0(breaks, "%"),
      labels.cex = 0.6
    )
  })

2. 直方圖

可以使用 circos.trackHist() 函數(shù)在所有單元格中繪制直方圖娜遵,如果設(shè)置 draw.density = TRUE 則會(huì)繪制數(shù)據(jù)分布的密度曲線蜕衡。

默認(rèn)情況下,每個(gè)單元格的柱子的數(shù)量會(huì)根據(jù)數(shù)據(jù)自動(dòng)確定设拟,通過(guò)固定 bin.size 的值慨仿,可以讓所有的單元格繪制相同數(shù)量的柱子,有利于單元格之間的分布比較纳胧。

例如

x <- rnorm(1600)
sectors <- sample(letters[1:16], 1600, replace = TRUE)
circos.initialize(sectors, x = x)
circos.trackHist(
  sectors, x = x, col = "#a6cee3", 
  border = "#1f78b4"
  )
circos.trackHist(
  sectors, x = x, bin.size = 0.1,  
  col = "#b2df8a", border = "#33a02c"
  )
circos.trackHist(
  sectors, x = x, draw.density = TRUE, 
  col = "#fdbf6f", border = "#ff7f00"
  )
circos.clear()

3. 系統(tǒng)發(fā)育樹(shù)

圓形樹(shù)狀圖可以有多方面的應(yīng)用镰吆,比如系統(tǒng)發(fā)育樹(shù)的展示。

R 中有多種樹(shù)結(jié)構(gòu)類(lèi)跑慕,例如 hclust, dendrogramphylo万皿,它們之間可以進(jìn)行相互轉(zhuǎn)換摧找,所以我們只使用 dendrogram 類(lèi)來(lái)說(shuō)明

首先,導(dǎo)入 ape 包提供的鳥(niǎo)類(lèi)物種信息牢硅,并對(duì)數(shù)據(jù)進(jìn)行層次聚類(lèi)

library(ape)

data(bird.orders)
hc <- as.hclust(bird.orders)

然后蹬耘,使用 cutree() 將數(shù)進(jìn)行切割,劃分出 6 個(gè)物種

# 獲取鳥(niǎo)類(lèi)名稱
labels <- hc$labels
# 劃分為 6 個(gè)物種
ct <- cutree(hc, 6)
# 物種的數(shù)量
n <- length(labels) 
# 轉(zhuǎn)換為 dendrogram 結(jié)構(gòu)
dend <- as.dendrogram(hc)

因?yàn)闃?shù)形圖在內(nèi)側(cè)减余,所以要先繪制物種的標(biāo)簽

circos.par(cell.padding = c(0, 0, 0, 0))
# 只需要一個(gè)扇形
circos.initialize("a", xlim = c(0, n))
circos.track(
  ylim = c(0, 1), bg.border = NA, track.height = 0.3, 
  panel.fun = function(x, y) {
    for (i in seq_len(n)) {
      circos.text(
        i - 0.5, 0, labels[i], adj = c(0, 0.5),
        facing = "clockwise", niceFacing = TRUE,
        col = ct[labels[i]], cex = 0.5
      )
    }
  })

最后使用 circos.dendrogram() 函數(shù)繪制樹(shù)形圖

library(dendextend)

dend <- color_branches(dend, k = 6, col = 1:6)
dend_height <- attr(dend, "height")
circos.track(
  ylim = c(0, dend_height), bg.border = NA, 
  track.height = 0.4, 
  panel.fun = function(x, y) {
    circos.dendrogram(dend)
    }
  )
circos.clear()

可以使用 dendextend 包對(duì)樹(shù)形圖的屬性進(jìn)行設(shè)置综苔,樹(shù)形圖默認(rèn)是朝外的,可以設(shè)置 facing = "inside"佳励,使其朝向內(nèi)部

circos.dendrogram(dend, facing = "inside")

注意休里,要先繪制樹(shù)形圖后繪制標(biāo)簽

4. 手動(dòng)繪制圓形熱圖

既然我們可以繪制圓形樹(shù)狀圖,那么很容易想到赃承,在最外層使用 circos.rect() 函數(shù)添加一圈熱圖妙黍,就變成了圓形熱圖。

我們要繪制兩個(gè)獨(dú)立的熱圖瞧剖,首先拭嫁,構(gòu)造數(shù)據(jù)

mat <- matrix(rnorm(100*10), nrow = 100, ncol = 10)
col_fun <- colorRamp2(c(-2, 0, 2), c("#fc8d59", "#ffffbf", "#99d594"))
# 設(shè)置兩個(gè)扇形
sectors <- rep(letters[1:2], times = c(30, 70))
mat_list <- list(
  a = mat[sectors == "a", ],
  b = mat[sectors == "b", ]
  )
# 將聚類(lèi)結(jié)果轉(zhuǎn)換為 dendrogram 類(lèi)
dend_list <- list(
  a = as.dendrogram(hclust(dist(mat_list[["a"]]))),
  b = as.dendrogram(hclust(dist(mat_list[["b"]])))
)

我們需要從外到內(nèi)依次繪制,先繪制圓形熱圖

circos.par(cell.padding = c(0, 0, 0, 0), gap.degree = 5)
circos.initialize(sectors, xlim = cbind(c(0, 0), table(sectors)))
circos.track(
  ylim = c(0, 10), bg.border = NA, 
  panel.fun = function(x, y) {
    sector.index = CELL_META$sector.index
    m = mat_list[[sector.index]]
    dend = dend_list[[sector.index]]
    
    m2 = m[order.dendrogram(dend),]
    col_mat = col_fun(m2)
    nr = nrow(m2)
    nc = ncol(m2)
    for (i in 1:nc) {
      circos.rect(
        1:nr - 1, rep(nc - i, nr), 1:nr,
        rep(nc - i + 1, nr), border = col_mat[, i],
        col = col_mat[, i])
    }
})

因?yàn)槲覀冃枰L制了兩個(gè)樹(shù)狀圖抓于,要保證它們的高度一致做粤,可以去兩個(gè)當(dāng)中更高的那個(gè)來(lái)設(shè)置 ylim

# 獲取最大樹(shù)高
max_height <- max(sapply(dend_list, function(x) attr(x, "height")))
circos.track(
  ylim = c(0, max_height), bg.border = NA, 
  track.height = 0.3, panel.fun = function(x, y) {
    sector.index = get.cell.meta.data("sector.index")
    dend = dend_list[[sector.index]]
    circos.dendrogram(dend, max_height = max_height)
  }
)
circos.clear()

圖例

circlize 為用戶提供了完全自由的圖形設(shè)計(jì),但是缺少了對(duì)圖例的控制捉撮。

例如怕品,對(duì)于如下圖形

col_fun <- colorRamp2(c(-2, 0, 2), c("green", "blue", "red"))
circlize_plot <- function() {
  set.seed(123)
  sectors = letters[1:10]
  circos.initialize(sectors, xlim = c(0, 1))
  circos.track(ylim = c(0, 1), panel.fun = function(x, y) {
    circos.points(runif(20), runif(20), cex = 0.5, pch = 16, col = 2)
    circos.points(runif(20), runif(20), cex = 0.5, pch = 16, col = 3)
  })
  circos.track(ylim = c(0, 1), panel.fun = function(x, y) {
    circos.lines(sort(runif(20)), runif(20), col = 3)
    circos.lines(sort(runif(20)), runif(20), col = 7)
  })
  
  for(i in 1:10) {
    circos.link(sample(sectors, 1), sort(runif(10))[1:2], 
                sample(sectors, 1), sort(runif(10))[1:2],
                col = add_transparency(col_fun(rnorm(1))))
  }
  circos.clear()
}
circlize_plot()

現(xiàn)在要為這三個(gè)軌跡添加圖例,我們可以使用 ComplexHeatmapLegend() 函數(shù)來(lái)自定義圖例

library(ComplexHeatmap)
# 點(diǎn)
lgd_points <- Legend(
  at = c("label1", "label2"), type = "points", 
  legend_gp = gpar(col = 2:3), title_position = "topleft", 
  title = "Track1"
)
# 線
lgd_lines <- Legend(
  at = c("label3", "label4"), type = "lines", 
  legend_gp = gpar(col = 4:5, lwd = 2), 
  title_position = "topleft", title = "Track2"
)
# 顏色條
lgd_links <- Legend(
  at = c(-2, -1, 0, 1, 2), col_fun = col_fun, 
  title_position = "topleft", title = "Links"
)

要將這三個(gè)圖例合并在一起巾遭,可以使用 packLegend() 函數(shù)肉康,默認(rèn)按豎直方向添加

lgd_list_vertical <- packLegend(lgd_points, lgd_lines, lgd_links)

可以使用 draw() 函數(shù)來(lái)繪制圖例

draw(
  lgd_list_vertical, x = unit(5, "mm"), 
  y = unit(4, "mm"), just = c("left", "bottom")
)

雖然 circlize 是基于基礎(chǔ)圖形系統(tǒng),而 ComplexHeatmap 是基于 grid 繪圖系統(tǒng)灼舍,但是兩種系統(tǒng)可以混合使用吼和。事實(shí)上,它們是繪制在同一個(gè)圖形設(shè)備上的不同圖層

但是這種方法很容易出現(xiàn)圖例與圓形布局重疊的情況骑素,更好的方式是將圖例和圓形圖分為兩部分炫乓。

我們可以用 grid 包來(lái)對(duì)繪圖區(qū)域進(jìn)行重排,例如

grid.newpage()
circle_size = unit(1, "snpc")
pushViewport(
  viewport(
    x = 0, y = 0.5, width = circle_size, 
    height = circle_size, just = c("left", "center")
    )
  )
par(omi = c(0, 0, 0.5, 0.5), new = TRUE)
circlize_plot()
upViewport()
draw(lgd_list_vertical, x = circle_size, just = "left")

使用 par(new = TRUE) 防止基礎(chǔ)圖形繪制到新的圖片上献丑,并用 omi 參數(shù)來(lái)設(shè)置邊距末捣。

也可以設(shè)置水平排列的圖例

lgd_points <- Legend(
  at = c("label1", "label2"), type = "points", 
  legend_gp = gpar(col = 2:3), title_position = "topleft",
  title = "Track1", nrow = 1
)

lgd_lines <- Legend(
  at = c("label3", "label4"), type = "lines", 
  legend_gp = gpar(col = 4:5, lwd = 2), title_position = "topleft",
  title = "Track2", nrow = 1
)

lgd_links <- Legend(
  at = c(-2, -1, 0, 1, 2), col_fun = col_fun, 
  title_position = "topleft", title = "Links", direction = "horizontal"
  )

lgd_list_horizontal <- packLegend(
  lgd_points, lgd_lines, lgd_links, 
  direction = "horizontal"
)

plot.new()
pushViewport(viewport(
  x = 0.5, y = 1, width = circle_size, 
  height = circle_size, just = c("center", "top"))
  )
par(omi = c(0, 0, 0, 0), new = TRUE)
circlize_plot()
upViewport()

draw(lgd_list_horizontal, y = unit(1, "npc") - circle_size, just = "top")

這種方式也是要調(diào)整繪圖區(qū)域的大小,來(lái)顯示圖例

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末创橄,一起剝皮案震驚了整個(gè)濱河市塔粒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌筐摘,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異咖熟,居然都是意外死亡圃酵,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén)馍管,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)郭赐,“玉大人,你說(shuō)我怎么就攤上這事确沸“贫В” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵罗捎,是天一觀的道長(zhǎng)观谦。 經(jīng)常有香客問(wèn)我,道長(zhǎng)桨菜,這世上最難降的妖魔是什么豁状? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮倒得,結(jié)果婚禮上泻红,老公的妹妹穿的比我還像新娘。我一直安慰自己霞掺,他們只是感情好谊路,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著菩彬,像睡著了一般缠劝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挤巡,一...
    開(kāi)封第一講書(shū)人閱讀 51,775評(píng)論 1 307
  • 那天剩彬,我揣著相機(jī)與錄音,去河邊找鬼矿卑。 笑死喉恋,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的母廷。 我是一名探鬼主播轻黑,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼琴昆!你這毒婦竟也來(lái)了氓鄙?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤业舍,失蹤者是張志新(化名)和其女友劉穎抖拦,沒(méi)想到半個(gè)月后升酣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡态罪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年噩茄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片复颈。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绩聘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耗啦,到底是詐尸還是另有隱情凿菩,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布帜讲,位于F島的核電站衅谷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏舒帮。R本人自食惡果不足惜会喝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望玩郊。 院中可真熱鬧肢执,春花似錦、人聲如沸译红。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)侦厚。三九已至耻陕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刨沦,已是汗流浹背诗宣。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留想诅,地道東北人召庞。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像来破,于是被迫代替她去往敵國(guó)和親篮灼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容