據(jù)整理到高級定制)
1. 先搞清楚這張圓形熱圖到底要解決什么展示問題看到“復(fù)現(xiàn)文獻中的圓形熱圖”這個需求很多人的第一反應(yīng)是去找代碼。但更關(guān)鍵的一步是先理解文獻作者為什么選擇圓形熱圖而不是常規(guī)的矩形熱圖。這決定了你復(fù)現(xiàn)的起點和終點。圓形熱圖在學(xué)術(shù)圖表里通常被稱為“Circular Heatmap”或“Circular Visualization”它不只是把矩形熱圖彎成一個圈那么簡單。它核心解決的是兩類展示問題周期性或循環(huán)關(guān)系數(shù)據(jù)的可視化比如一天24小時的基因表達變化、一年12個月的物種豐度、細(xì)胞周期相關(guān)基因的表達模式。用圓形布局能直觀地體現(xiàn)“周期結(jié)束即是開始”的循環(huán)特性。高維度、多層次關(guān)聯(lián)數(shù)據(jù)的整合展示當(dāng)你有多個數(shù)據(jù)矩陣并且它們之間存在層級或網(wǎng)絡(luò)關(guān)系時比如基因-通路-表型圓形布局可以更緊湊地展示這些關(guān)系并通過扇區(qū)、軌道等元素進行分層。所以在動手敲代碼之前我建議你先問自己兩個問題第一我手頭的數(shù)據(jù)是否具有上述的周期性或多層次關(guān)聯(lián)特性第二文獻中的那張圖除了“圓”這個形狀它的顏色、扇區(qū)劃分、內(nèi)外軌道、連線分別代表了什么數(shù)據(jù)維度把這兩個問題搞清楚復(fù)現(xiàn)就成功了一半。對于R語言用戶ComplexHeatmap包是繪制這類高級熱圖的絕對主力它功能強大但學(xué)習(xí)曲線也相對陡峭。它的核心優(yōu)勢在于將熱圖拆解成一個個可以自由組合的“模塊”比如熱圖塊、行/列注釋、圖形元素再通過%v%或運算符進行拼接最終實現(xiàn)幾乎任何你能想到的布局。復(fù)現(xiàn)圓形熱圖本質(zhì)上就是學(xué)習(xí)如何用ComplexHeatmap的這些模塊去“搭建”一個圓形布局。2. 復(fù)現(xiàn)前的核心準(zhǔn)備數(shù)據(jù)、包與環(huán)境復(fù)現(xiàn)不是照貓畫虎你得先把自己的“原料”準(zhǔn)備好。這里分三步走數(shù)據(jù)整理、包安裝、理解核心函數(shù)。2.1 數(shù)據(jù)整理從文獻到你的數(shù)據(jù)框文獻里的圖很美但你的數(shù)據(jù)必須整理成R能識別的格式。ComplexHeatmap最常用的輸入是一個數(shù)值矩陣matrix行和列通常是你要比較的實體比如基因和樣本。假設(shè)你要復(fù)現(xiàn)一張展示10個基因在5個時間點表達量的圓形熱圖你的數(shù)據(jù)就應(yīng)該是一個10行基因、5列時間點的矩陣。數(shù)據(jù)可以從你的實驗中獲得或者從文獻的補充材料里提取有時作者會提供原始數(shù)據(jù)。用read.table()或read.csv()讀入后用as.matrix()轉(zhuǎn)換。# 示例創(chuàng)建一個模擬的表達矩陣 set.seed(123) expr_matrix - matrix(rnorm(50, mean0, sd1), nrow10, ncol5) rownames(expr_matrix) - paste0(Gene, 1:10) colnames(expr_matrix) - paste0(Time, 1:5) print(expr_matrix)關(guān)鍵點矩陣的行名和列名很重要它們會默認(rèn)顯示在熱圖上。確保你的數(shù)據(jù)沒有缺失值NA或者你已想好如何處理NA比如用均值填充。2.2 包安裝與加載別在依賴上卡住ComplexHeatmap依賴于circlize包來實現(xiàn)圓形布局。所以你需要安裝這兩個包。我強烈建議使用RStudio并通過CRAN鏡像安裝這是最穩(wěn)的方式。# 安裝核心包 install.packages(ComplexHeatmap) install.packages(circlize) # 加載包 library(ComplexHeatmap) library(circlize)如果安裝ComplexHeatmap時遇到問題比如提示依賴包版本不對可以先嘗試更新你的R版本到較新的穩(wěn)定版如4.2.x以上然后單獨安裝報錯的依賴包。網(wǎng)絡(luò)問題可以嘗試更換CRAN鏡像Tools - Global Options - Packages。2.3 理解兩個核心函數(shù)Heatmap與circos.heatmap這是最容易混淆的地方。ComplexHeatmap包的主函數(shù)是Heatmap()它生成的是矩形熱圖。而要實現(xiàn)真正的圓形熱圖你需要使用的是circlize包中的circos.heatmap()函數(shù)它是在環(huán)形布局circos layout上繪制熱圖。簡單來說Heatmap()功能全面用于創(chuàng)建矩形熱圖及其所有高級注釋是學(xué)習(xí)這個包的起點。circos.heatmap()專門用于在圓形軌道上繪制熱圖數(shù)據(jù)是復(fù)現(xiàn)“圓形熱圖”的直接工具。它通常需要配合circlize的circos.initialize()和circos.track()等函數(shù)來搭建整個圓形畫布。很多教程一上來就講Heatmap()的復(fù)雜參數(shù)但對于圓形熱圖復(fù)現(xiàn)你應(yīng)該先聚焦于circos.heatmap()。不過ComplexHeatmap包中對顏色映射、數(shù)據(jù)分割split等概念的理解對使用circos.heatmap()同樣至關(guān)重要。3. 從零搭建一個基礎(chǔ)圓形熱圖我們現(xiàn)在用模擬數(shù)據(jù)走一遍繪制基礎(chǔ)圓形熱圖的完整流程。這個過程我會拆解得很細(xì)包括每個參數(shù)的作用和常見坑點。3.1 第一步初始化圓形畫布與軌道在circlize的邏輯里畫圖前必須先“初始化”扇區(qū)。對于熱圖我們通常把矩陣的每一行或每一列定義為一個扇區(qū)。這里我們以行為扇區(qū)每個基因一個扇區(qū)。# 繼續(xù)使用之前的 expr_matrix # 1. 初始化扇區(qū)這里按行基因初始化 circos.par(gap.degree 5, start.degree 90) # 設(shè)置扇區(qū)間隔和起始角度 circos.initialize(factors rownames(expr_matrix), xlim c(0, ncol(expr_matrix))) # 2. 創(chuàng)建軌道用于繪制熱圖 circos.track(ylim c(0, 1), bg.border NA, track.height 0.3) # 先創(chuàng)建一個空軌道 # 3. 在軌道上添加熱圖 # circos.heatmap 需要矩陣數(shù)據(jù)以及顏色映射函數(shù) col_fun - colorRamp2(c(min(expr_matrix), 0, max(expr_matrix)), c(blue, white, red)) circos.heatmap(expr_matrix, col col_fun) # 4. 添加軌道標(biāo)簽基因名 circos.track(track.index 1, panel.fun function(x, y) { circos.text(CELL_META$xcenter, CELL_META$cell.ylim[2] mm_y(2), CELL_META$sector.index, facing clockwise, niceFacing TRUE, adj c(0, 0.5), cex 0.7) }, bg.border NA) # 5. 結(jié)束繪圖關(guān)閉設(shè)備 circos.clear()代碼拆解與避坑circos.par(gap.degree5)gap.degree控制扇區(qū)之間的間隙角度。數(shù)值越大間隙越寬。如果基因很多比如上百個這個值要設(shè)小比如1或2否則圓環(huán)會斷裂。start.degree90讓圓環(huán)從12點鐘方向開始這是比較符合閱讀習(xí)慣的。circos.initializefactors參數(shù)定義了扇區(qū)這里用了行名。xlim定義了每個扇區(qū)在x軸上的范圍這里對應(yīng)了樣本數(shù)5。colorRamp2這是定義顏色映射的關(guān)鍵函數(shù)。它接受一個數(shù)值向量斷點和一個顏色向量將數(shù)據(jù)線性映射到顏色。這里最容易出問題你的數(shù)據(jù)分布可能不是對稱的如果直接用min和max可能導(dǎo)致中間色白色不對應(yīng)0值。更好的做法是根據(jù)數(shù)據(jù)實際范圍調(diào)整例如c(quantile(expr_matrix, 0.1), 0, quantile(expr_matrix, 0.9))。circos.tracktrack.height控制軌道高度。多個軌道可以疊加用于放置熱圖、柱狀圖、折線圖等。circos.clear()非常重要每次用circlize畫完一個圓必須調(diào)用此函數(shù)重置布局否則下一個圖會疊加在上一個圖上。運行這段代碼你應(yīng)該能得到一個最基礎(chǔ)的、環(huán)狀的、按基因分扇區(qū)的熱圖。但這可能離文獻里的精美圖形還有距離。3.2 第二步添加行列注釋與分割文獻中的熱圖往往色彩豐富旁邊還有各種注釋條。在圓形布局中我們可以通過添加額外的軌道來實現(xiàn)類似效果。假設(shè)我們想給樣本列加上分組注釋比如“處理組”和“對照組”并給基因行加上類型注釋比如“通路A”、“通路B”。# 準(zhǔn)備注釋信息 sample_annotation - data.frame( Group rep(c(Treatment, Control), times c(3, 2)) # 前3個時間點為處理組后2個為對照組 ) rownames(sample_annotation) - colnames(expr_matrix) gene_annotation - data.frame( Type sample(c(Pathway_A, Pathway_B), size10, replaceTRUE) ) rownames(gene_annotation) - rownames(expr_matrix) # 定義注釋顏色 group_colors - c(Treatment darkorange, Control steelblue) type_colors - c(Pathway_A forestgreen, Pathway_B purple) # 重新繪圖 circos.par(gap.degree 5, start.degree 90) circos.initialize(factors rownames(expr_matrix), xlim c(0, ncol(expr_matrix))) # 軌道1基因類型注釋最外圈 circos.track(ylim c(0, 1), track.height 0.05, bg.border NA, panel.fun function(x, y) { sector.index - CELL_META$sector.index type - gene_annotation[sector.index, Type] circos.rect(0, 0, ncol(expr_matrix), 1, col type_colors[type], border type_colors[type]) }) # 軌道2樣本分組注釋以“軌道點”形式在熱圖內(nèi)側(cè) # 我們需要在每個扇區(qū)基因內(nèi)為每個樣本畫一個小矩形 circos.track(ylim c(0, 1), track.height 0.05, bg.border NA, panel.fun function(x, y) { sector.index - CELL_META$sector.index for (j in 1:ncol(expr_matrix)) { group - sample_annotation[colnames(expr_matrix)[j], Group] circos.rect(j-1, 0, j, 1, col group_colors[group], border group_colors[group]) } }) # 軌道3主熱圖 circos.track(ylim c(0, 1), track.height 0.25, bg.border NA) col_fun - colorRamp2(c(-2, 0, 2), c(blue, white, red)) # 固定顏色范圍便于比較 circos.heatmap(expr_matrix, col col_fun) # 軌道4基因名稱標(biāo)簽 circos.track(track.index 4, panel.fun function(x, y) { circos.text(CELL_META$xcenter, CELL_META$cell.ylim[2] mm_y(1), CELL_META$sector.index, facing clockwise, niceFacing TRUE, adj c(0, 0.5), cex 0.6) }, bg.border NA, track.height 0.05) # 添加圖例 legend(x -1.2, y 1.2, legend names(group_colors), fill group_colors, title Sample Group, cex0.8) legend(x 1.0, y 1.2, legend names(type_colors), fill type_colors, title Gene Type, cex0.8) circos.clear()經(jīng)驗之談軌道順序circos.track是依次從外向內(nèi)添加軌道的。所以先畫的注釋會在外圈后畫的熱圖在內(nèi)圈。規(guī)劃好你的視覺層次。注釋繪制邏輯在panel.fun里CELL_META$sector.index獲取當(dāng)前扇區(qū)名即基因名CELL_META$xlim獲取當(dāng)前扇區(qū)x軸范圍。我們需要在這個函數(shù)內(nèi)部根據(jù)這些信息繪制當(dāng)前扇區(qū)內(nèi)的圖形元素。這是circlize編程的核心思維有點繞但理解后非常靈活。顏色映射固定在正式作圖中我建議像上面一樣用固定的數(shù)值范圍如c(-2,0,2)來定義colorRamp2而不是用數(shù)據(jù)的min和max。這能保證多張圖之間顏色意義一致便于比較。4. 向文獻級復(fù)現(xiàn)邁進高級定制與問題排查基礎(chǔ)圖形出來后就需要摳細(xì)節(jié)了。文獻里的圖之所以好看往往在于這些細(xì)節(jié)。4.1 實現(xiàn)行或列的數(shù)據(jù)分割Clustering/Splitting文獻熱圖常按行或列的聚類樹進行分割。在圓形熱圖中雖然不能直接畫樹狀圖但我們可以通過預(yù)先聚類并重新排序數(shù)據(jù)來實現(xiàn)分割效果。# 對行基因進行層次聚類 row_dist - dist(expr_matrix) row_hclust - hclust(row_dist) row_order - row_hclust$order expr_matrix_ordered - expr_matrix[row_order, ] # 根據(jù)聚類結(jié)果手動定義分割例如切成3類 row_clusters - cutree(row_hclust, k 3) # 在初始化時按聚類后的順序和分類定義扇區(qū) # 我們需要創(chuàng)建一個因子其水平順序是聚類后的順序且包含分類信息 row_factors - paste0(Cluster, row_clusters[row_order], _, rownames(expr_matrix_ordered)) circos.par(gap.degree c(rep(2, sum(row_clusters1)-1), 5, # 簇內(nèi)間隙小簇間間隙大 rep(2, sum(row_clusters2)-1), 5, rep(2, sum(row_clusters3)-1))) circos.initialize(factors row_factors, xlim c(0, ncol(expr_matrix_ordered))) # ... 后續(xù)繪制軌道的代碼扇區(qū)將按聚類順序和分組排列 ...關(guān)鍵點circos.par(gap.degree)這里傳入了一個向量用來為每個扇區(qū)間隙設(shè)置不同的角度從而實現(xiàn)“簇內(nèi)緊簇間松”的視覺效果。這是手動模擬聚類分割的常用技巧。4.2 添加連線Links展示關(guān)聯(lián)圓形圖強大的另一個功能是展示扇區(qū)之間的關(guān)聯(lián)比如基因之間的共表達關(guān)系。這需要用到circos.link()函數(shù)。假設(shè)我們想強調(diào)Gene1和Gene5在所有樣本中表達模式高度相關(guān)。# 在畫完所有軌道后添加連線 # 假設(shè) Gene1 和 Gene5 在新的排序矩陣中位于 sector A 和 sector B # 我們需要知道它們所在的扇區(qū)名和x軸位置 sector_gene1 - row_factors[which(rownames(expr_matrix_ordered) Gene1)] sector_gene5 - row_factors[which(rownames(expr_matrix_ordered) Gene5)] # 在扇區(qū)中間位置連線 circos.link(sector.index1 sector_gene1, point1 c(2.5, 2.5), # point1 定義連線在扇區(qū)1的起始和結(jié)束x坐標(biāo) sector.index2 sector_gene5, point2 c(2.5, 2.5), col grey50, lwd 2, lty 2)circos.link的參數(shù)需要精確計算尤其是point1和point2它們指定了連線在各自扇區(qū)x軸上的起始和結(jié)束位置。對于連接整個扇區(qū)通常取其中點。4.3 輸出與美化分辨率、字體與格式用于發(fā)表的圖對分辨率、字體和矢量格式有要求。# 保存為高清PDF矢量圖適合投稿 pdf(My_Circular_Heatmap.pdf, width 10, height 10) # 在這里執(zhí)行你所有的繪圖代碼 dev.off() # 保存為高清PNG位圖用于PPT等 png(My_Circular_Heatmap.png, width 3000, height 3000, res 300) # 在這里執(zhí)行你所有的繪圖代碼 dev.off()美化建議字體在circos.text()等函數(shù)中使用family參數(shù)指定字體如Arial、Helvetica。確保系統(tǒng)中已安裝該字體。顏色主題不要默認(rèn)使用紅綠配色紅綠色盲讀者無法分辨。可以使用viridis、RColorBrewer包的顏色方案。例如col_fun - colorRamp2(breaks, viridis::viridis(3))。圖例使用ComplexHeatmap包的Legend()和draw()函數(shù)可以創(chuàng)建更精美、布局更靈活的圖例而不是用基礎(chǔ)的legend()。4.4 常見問題排查清單繪圖過程中遇到問題按這個順序查圖形不顯示或錯亂檢查是否最后調(diào)用了circos.clear()沒調(diào)用會導(dǎo)致后續(xù)圖形疊加。檢查circos.initialize中的factors是否與數(shù)據(jù)行名/列名完全匹配因子水平順序決定了繪圖順序。檢查xlim的范圍是否覆蓋了你的數(shù)據(jù)索引通常從0到列數(shù)顏色映射不對勁檢查colorRamp2的斷點breaks是否合理用summary(expr_matrix)或hist(expr_matrix)查看數(shù)據(jù)分布。檢查輸入expr_matrix是否是數(shù)值矩陣字符型矩陣會導(dǎo)致錯誤。軌道重疊或順序不對回憶circos.track的調(diào)用順序先調(diào)用的畫在外圈。檢查每個circos.track的track.height參數(shù)它們加起來不應(yīng)超過1或你設(shè)定的總范圍。比例失調(diào)會導(dǎo)致重疊。文字標(biāo)簽重疊或顯示不全扇區(qū)太多基因太多時標(biāo)簽必然擁擠??梢詼p小cex字體大小。只顯示部分重要標(biāo)簽通過條件判斷在panel.fun中選擇性繪制。增大畫布尺寸pdf(width, height)??紤]是否真的需要顯示所有行名有時用圖例代替更清晰。性能問題數(shù)據(jù)量大時繪圖慢圓形熱圖本身計算量較大。如果行數(shù)超過1000請慎重考慮是否必須用圓形。矩形熱圖Heatmap()效率更高??梢韵仍跀?shù)據(jù)子集如前100行上調(diào)試代碼和樣式成功后再用全數(shù)據(jù)繪制最終圖。復(fù)現(xiàn)文獻圖表尤其是ComplexHeatmap制作的復(fù)雜圖形本質(zhì)上是一個“逆向工程”加“創(chuàng)造性實現(xiàn)”的過程。最有效的路徑不是尋找一模一樣的代碼而是理解原圖的數(shù)據(jù)邏輯和視覺元素然后運用circlize和ComplexHeatmap的工具箱像搭積木一樣把它構(gòu)建出來。從最基礎(chǔ)的單軌道熱圖開始逐步添加注釋、調(diào)整顏色、設(shè)置分割、美化標(biāo)簽每一步都確保理解參數(shù)的意義這樣即使下次遇到不同的圖你也能舉一反三快速實現(xiàn)。