圓形布局對(duì)于表示復(fù)雜的信息非常有用。首先,它可以用更長(zhǎng)的坐標(biāo)軸或大量的類別表示不同的信息;其次,它可以直觀地展示數(shù)據(jù),將多個(gè)圓環(huán)聚焦于同一對(duì)象;第三,它可以清楚地展示元素之間的關(guān)系。圓形布局提供了一種在圓圈上排列信息的有效方法,而且很漂亮。
Circos
是一個(gè)廣泛用于在Perl
中實(shí)現(xiàn)的圓形布局的工具。它大大增強(qiáng)了科學(xué)結(jié)果的可視化(特別是在基因組學(xué)領(lǐng)域)。因此,具有圓形布局的圖通常被命名為“circos圖”。
circlize
包旨在在R中實(shí)現(xiàn)Circos
。在R中實(shí)現(xiàn)的一個(gè)重要優(yōu)勢(shì)是,R是一個(gè)理想的環(huán)境,可在數(shù)據(jù)分析和數(shù)據(jù)可視化之間提供無縫連接。circlize
不是為Circos
生成配置文件的前端包裝器,而是使用R優(yōu)雅的統(tǒng)計(jì)和圖形引擎,完全以R風(fēng)格編碼。我們的目標(biāo)是保持Circos
的靈活性和可配置性,同時(shí)使軟件包更易于使用并增強(qiáng)它以支持更多類型的圖形。
本文目錄:
設(shè)計(jì)原理
快速上手
添加圖形元素的方式
圓形熱圖
設(shè)計(jì)原理 圓形布局由扇區(qū) (sectors)和圓環(huán) (tracks)組成。對(duì)于不同類別的數(shù)據(jù),它們被分配到不同的扇區(qū),對(duì)于同一類別的多個(gè)測(cè)量,它們表示為從外到內(nèi)的堆疊圓環(huán) 。扇區(qū)和圓環(huán)的交集部分稱為單元格 (cell)(或網(wǎng)格(grid)、面板(panel)),它是圓形布局中的基本單元。它是用于繪制數(shù)據(jù)點(diǎn)的假想繪圖區(qū)域。
由于大多數(shù)圖形都是由簡(jiǎn)單的圖形組成的,如點(diǎn)、線、多邊形等,Circlize
實(shí)現(xiàn)了在圓形繪圖區(qū)域添加低級(jí)圖形的功能,因此可以通過不同組合的低級(jí)圖形功能輕松生成更復(fù)雜的圖形。這一原則確保了高級(jí)圖形的類型不受軟件本身的限制,并且可以使用其他高級(jí)圖形R包在其上構(gòu)建特定的高級(jí)圖形。
目前,有以下可用于添加低級(jí)圖形的函數(shù)。用法與沒有circos.
前綴的函數(shù)非常相似,除了一些專門為圓形可視化設(shè)計(jì)的增強(qiáng)功能。
circos.points()
:在單元格中添加點(diǎn)。circos.segments()
:在單元格中添加線段。circos.polygon()
:在單元格中添加多邊形。circos.axis()
和circos.yaxis()
:在單元格中添加坐標(biāo)軸。以下函數(shù)在圓圈中的兩個(gè)圓環(huán)之間繪制鏈接:
以下函數(shù)繪制高級(jí)圖形:
以下功能排列圓形布局。
circos.initialize()
:在圓圈上分配扇區(qū)。circos.track()
:為單個(gè)圓環(huán)中的單元格創(chuàng)建繪圖區(qū)域。circos.update()
:更新現(xiàn)有的單元格。circos.info()
:打印當(dāng)前圓形圖的一般參數(shù)。circos.clear()
:重置圖形參數(shù)和內(nèi)部變量。快速上手 在我們深入探討細(xì)節(jié)之前,我首先演示一個(gè)簡(jiǎn)單的例子,使用circlize
包中的基本功能來幫助你了解該包的工作原理。
首先是生成演示數(shù)據(jù),一個(gè)字符型向量表示類別,另外兩個(gè)變量是數(shù)值型:
set.seed(999 ) n <- 1000 df <- data.frame(sectors = sample(letters[1 :8 ], n, replace = TRUE ), x = rnorm(n), y = runif(n) ) head(df)
sectors x y 1 c -0.33837650 0.2277292 2 d 0.20128773 0.8345422 3 e 0.60271173 0.6507941 4 g -0.22501815 0.8613092 5 a -0.01011359 0.3624672 6 f -0.19359724 0.5251656
使用circlize
的第一步是初始化圓形布局。函數(shù)會(huì)自動(dòng)根據(jù)X軸的范圍和類別變量將圓形劃分為不同的扇區(qū)(相當(dāng)于自動(dòng)分配x軸范圍):
library (circlize) circos.par("track.height" = 0.1 ) # 設(shè)置圓環(huán)的高度為0.1,也就是圓形半徑的10% circos.initialize(df$sectors, x = df$x)
運(yùn)行以上代碼后不會(huì)顯示任何圖形,因?yàn)榇藭r(shí)只是進(jìn)行了初始化,并沒有開始畫圖~
接下來如果要畫圖的話首先要使用circos.track
添加圓環(huán),然后使用各種低級(jí)繪圖函數(shù)添加各種圖形元素,比如點(diǎn)、線、文字等:
# 初始化布局 circos.par("track.height" = 0.1 ) # 設(shè)置圓環(huán)的高度為0.1,也就是圓形半徑的10% circos.initialize(df$sectors, x = df$x)# 添加第一個(gè)圓環(huán) circos.track(df$sectors, y = df$y, # 這里相當(dāng)于自動(dòng)分配y軸范圍 panel.fun = function (x, y) { # 定義添加點(diǎn)和文字的細(xì)節(jié),你可以去掉這個(gè)函數(shù)試試看 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 )# 添加點(diǎn) circos.trackPoints(df$sectors, df$x, df$y, col = col, pch = 16 , cex = 0.5 )# 在指定panel添加文字 circos.text(-1 , 0.5 , "text" , sector.index = "a" , track.index = 1 )
上面這個(gè)原圓環(huán)圖有8個(gè)單元格(或者叫8個(gè)panel)。
為了更直觀的展示這個(gè)繪圖過程,你可以先運(yùn)行前3行看看結(jié)果,然后再運(yùn)行第4和第5行,再運(yùn)行第6行。其實(shí)這個(gè)過程就是R的繪圖過程,關(guān)于這方面的細(xì)節(jié),大家感興趣的可以參考書籍:《R繪圖系統(tǒng)》。
panel.fun
是一個(gè)用于控制單元格細(xì)節(jié)的函數(shù),后面會(huì)詳細(xì)介紹。CELL_META$xxx
是一個(gè)快捷函數(shù),可以幫你提取橫縱坐標(biāo)等信息。
接下來你可以繼續(xù)添加更多的圓環(huán)
# 初始化布局 circos.par("track.height" = 0.1 ) # 設(shè)置圓環(huán)的高度為0.1,也就是圓形半徑的10% circos.initialize(df$sectors, x = df$x)# 添加第一個(gè)圓環(huán) circos.track(df$sectors, y = df$y, # 這里相當(dāng)于自動(dòng)分配y軸范圍 panel.fun = function (x, y) { # 定義添加點(diǎn)和文字的細(xì)節(jié),你可以去掉這個(gè)函數(shù)試試看 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 )# 添加點(diǎn) 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 )# 添加第2個(gè)圓環(huán) bgcol = rep(c("#EFEFEF" , "#CCCCCC" ), 4 ) circos.trackHist(df$sectors, df$x, bin.size = 0.2 , bg.col = bgcol, col = NA )
接下來載添加第3個(gè)圓環(huán)。我們隨機(jī)從每個(gè)單元格中挑選10個(gè)點(diǎn),然后用線條連接起來
# 初始化布局 circos.par("track.height" = 0.1 ) # 設(shè)置圓環(huán)的高度為0.1,也就是圓形半徑的10% circos.initialize(df$sectors, x = df$x)# 添加第一個(gè)圓環(huán) circos.track(df$sectors, y = df$y, # 這里相當(dāng)于自動(dòng)分配y軸范圍 panel.fun = function (x, y) { # 定義添加點(diǎn)和文字的細(xì)節(jié),你可以去掉這個(gè)函數(shù)試試看 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 )# 添加點(diǎn) 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 )# 添加第2個(gè)圓環(huán) bgcol = rep(c("#EFEFEF" , "#CCCCCC" ), 4 ) circos.trackHist(df$sectors, df$x, bin.size = 0.2 , bg.col = bgcol, col = NA )# 添加第3個(gè)圓環(huán) 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]) })
如果我們要對(duì)某一個(gè)圓環(huán)的某個(gè)扇區(qū)(也就是某個(gè)panel)進(jìn)行修改,可以使用circos.update
,但是要注意,這個(gè)函數(shù)不能修改位置相關(guān)的東西。
比如我們要修改d
這個(gè)扇區(qū)的第2層圓環(huán),可以在運(yùn)行完以上代碼后運(yùn)行以下代碼:
circos.update(sector.index = "d" , # 選擇扇區(qū) track.index = 2 , # 選擇圓環(huán) bg.col = "#FF8080" , bg.border = "black" ) # 修改顏色 circos.points(x = -2 :2 , y = rep(0.5 , 5 ), col = "white" ) circos.text(CELL_META$xcenter, CELL_META$ycenter, "updated" , col = "white" )
接下來還可以繼續(xù)添加圓環(huán):
circos.track(ylim = c(0 , 1 ), panel.fun = function (x, y) { xlim = CELL_META$xlim ylim = CELL_META$ylim breaks = 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 ) })
當(dāng)你把圓環(huán)都畫完之后,還可以使用線條或者條帶連接不同的扇區(qū):
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 )
當(dāng)你完成整個(gè)圓環(huán)圖的繪制后,一定要記得重置圖形參數(shù):
circos.clear()
添加圖形元素的方式 畫圓環(huán)圖首先要初始化,然后添加圓環(huán),再然后才是添加各種圖形元素,比如點(diǎn)、線、文本、坐標(biāo)軸等。
添加這些圖形元素的方式有3種:
使用circos.points()
、circos.lines()
等低級(jí)繪圖函數(shù)進(jìn)行添加,但是這種方式需要自己寫for
循環(huán),逐個(gè)panel進(jìn)行添加,需要自己根據(jù)類別劃分?jǐn)?shù)據(jù); 使用circos.trackPoints()
、circos.trackLines()
等函數(shù)添加,不需要自己寫循環(huán); 使用panel.fun
添加,這種方式最為強(qiáng)大,但是需要一點(diǎn)理解,作者是比較推薦這種方式的,下面會(huì)詳細(xì)介紹。 panel.fun
需要兩個(gè)參數(shù):x
和y
,分別表示在當(dāng)前panel中的橫坐標(biāo)和縱坐標(biāo)。
以下是一個(gè)簡(jiǎn)單的示例。在扇區(qū)a中,x的值是1:3,在扇區(qū)b中,x的值是4:5:
sectors = c("a" , "a" , "a" , "b" , "b" ) x = 1 :5 y = 5 :1 circos.initialize(sectors = sectors, x=x) circos.track(sectors, x = x, y = y, panel.fun = function (x, y) { circos.points(x, y) circos.text(CELL_META$xcenter, # 橫坐標(biāo) CELL_META$cell.ylim[2 ] + mm_y(5 ), #縱坐標(biāo) CELL_META$sector.index) })
circos.clear()
CELL_META$xxxx
其實(shí)是get.cell.meta.data()
的縮寫,可以幫你提取當(dāng)前panel的信息,比如CELL_META$xcenter
會(huì)提取當(dāng)前panel的橫坐標(biāo)的中心點(diǎn),CELL_META$cell.ylim
是提取當(dāng)前panel的縱坐標(biāo),CELL_META$sector.index
提取當(dāng)前panel的標(biāo)簽。
我們也可以直接使用get.cell.meta.data()
函數(shù),但是不如用CELL_META$
簡(jiǎn)潔。以下第二個(gè)圓環(huán)就是用的get.cell.meta.data()
函數(shù):
sectors = c("a" , "a" , "a" , "b" , "b" ) x = 1 :5 y = 5 :1 # 初始化 circos.initialize(sectors = sectors, x=x)# 添加第一個(gè)圓環(huán),標(biāo)簽在圓環(huán)外面 circos.track(sectors, x = x, y = y, panel.fun = function (x, y) { circos.points(x, y) circos.text(CELL_META$xcenter, # 橫坐標(biāo) CELL_META$cell.ylim[2 ] + mm_y(5 ), #縱坐標(biāo) CELL_META$sector.index) })
# 添加第二個(gè)圓環(huán),把標(biāo)簽放在圓環(huán)里面 circos.track(sectors, y = y, panel.fun = function (x, y) { sector.index = get.cell.meta.data("sector.index" ) xcenter = get.cell.meta.data("xcenter" ) ycenter = get.cell.meta.data("ycenter" ) circos.text(xcenter, ycenter, sector.index) })
circos.clear()
圓形熱圖 圓形熱圖非常常見,以前是通過circos.rect
實(shí)現(xiàn),但是0.4.10版本以后提供了一個(gè)circos.heatmap
函數(shù),可以直接繪制圓形熱圖,非常方便。
數(shù)據(jù)準(zhǔn)備時(shí)也要提供一個(gè)數(shù)值型矩陣(用來畫熱圖),除此之外,還需要一個(gè)分組變量,用來分割熱圖。
rm(list = ls()) set.seed(123 ) mat1 = rbind(cbind(matrix(rnorm(50 *5 , mean = 1 ), nr = 50 ), matrix(rnorm(50 *5 , mean = -1 ), nr = 50 )), cbind(matrix(rnorm(50 *5 , mean = -1 ), nr = 50 ), matrix(rnorm(50 *5 , mean = 1 ), nr = 50 )) ) rownames(mat1) = paste0("R" , 1 :100 ) colnames(mat1) = paste0("C" , 1 :10 ) mat1 = mat1[sample(100 , 100 ), ] # 畫熱圖的矩陣 mat1[1 :4 ,1 :4 ]
C1 C2 C3 C4 R97 -2.1986224 -1.1271486 -1.8308115 -1.1624219 R49 1.7799651 0.7642996 3.1001089 0.3888341 R50 0.9166309 -0.0264209 -0.2870305 -0.1854801 R42 0.7920827 1.5483970 0.7378025 0.6753141
# 分組變量 split = sample(letters[1 :5 ], 100 , replace = TRUE ) spilt = factor(split, levels = letters[1 :5 ]) col_fun1 = colorRamp2(c(-2 , 0 , 2 ), c("blue" , "white" , "red" ))
這個(gè)數(shù)據(jù)如果直接畫熱圖是這樣的:
library (ComplexHeatmap) Heatmap(mat1, row_split = split)
如果要畫圓形熱圖,是這樣的:
circos.heatmap(mat1, split = split, col = col_fun1, track.height = 0.4 , # 圓環(huán)的寬度 show.sector.labels = T , #顯示圓環(huán)的標(biāo)簽 rownames.side = "outside" , # 行名顯示在外側(cè) dend.side = "inside" # 聚類樹顯示在內(nèi)側(cè) )
circos.clear()
各種文本都可以進(jìn)行各種自定義設(shè)置,比如顏色、大小、字體等。
聚類樹也可以進(jìn)行修改,比如重新排序或者使用不同的顏色等。需要借助dend.callback
參數(shù)以及自定義函數(shù)實(shí)現(xiàn)。自定義函數(shù)需要3個(gè)參數(shù):
si
:當(dāng)前扇區(qū)的編號(hào)(或者名字)比如重新設(shè)置聚類樹的順序:
library (dendsort) circos.heatmap(mat1, split = split, col = col_fun1, dend.side = "inside" , dend.callback = function (dend, m, si) { dendsort(dend) } )
circos.clear()
或者給聚類樹添加不同的顏色:
library (dendextend) dend_col = structure(1 :5 , names = letters[1 :5 ]) circos.heatmap(mat1, split = split, col = col_fun1, dend.side = "inside" , dend.track.height = 0.2 , dend.callback = function (dend, m, si) { # when k = 1, it renders one same color for the whole dendrogram color_branches(dend, k = 1 , col = dend_col[si]) } )
circos.clear()
增加多個(gè)圓環(huán)也是一模一樣的方法。
# 第2個(gè)熱圖的數(shù)據(jù) mat2 = mat1[sample(100 , 100 ), ] # randomly permute mat1 by rows # 第2個(gè)熱圖的顏色 col_fun2 = colorRamp2(c(-2 , 0 , 2 ), c("green" , "white" , "red" ))# 繪制兩個(gè)圓形熱圖 circos.heatmap(mat1, split = split, col = col_fun1, dend.side = "outside" ) circos.heatmap(mat2, col = col_fun2)
circos.clear()
更加復(fù)雜的例子,大家可以至官網(wǎng)學(xué)習(xí):https://jokergoo./circlize_book/book/