Python——比 Seaborn 更好的相關性熱力圖:Biokit Corrplot

多玩我的世界盒子發表於2024-06-30

目錄
  • 前言:我們需要更好的相關性熱力圖
  • 對比 Python Seaborn 與 R corrplot
    • 傳統的 Seaborn 相關性熱力圖
    • R 語言中的相關性熱力圖
  • 關於 Biokit
    • 簡介
    • 庫的安裝
    • 相關性熱圖的繪製
      • 基本使用方法
      • 詳述一些繪圖引數的問題及細節
        • 關於 order_method 引數
        • 關於 order_metric 引數
        • 關於 cmap 引數
  • 改進 BioKit 中 Corrplot 對 colormap 的支援
    • 事情的起因
    • 拉取 Biokit 原始碼並在本地構建
  • 總結和補充

前言:我們需要更好的相關性熱力圖

在 Python 中我們日常分析資料的過程當中經常需要對資料進行相關性分析,相關性熱力圖(Correlation Heatmap)是我們經常使用的一種工具。透過相關性熱力圖,我們可以透過為相關性不同的資料使用不同深淺的不同顏色進行標記,從而直觀地觀察兩兩資料序列之間的相關性情況——這將有助於我們進一步的資料分析和處理,比如資料的迴歸分析等。

這其中最常見的工具就是由 Seaborn 工具包提供的 sns.heatmap(),處理方法的原理相當於先取得變數序列的相關性矩陣,然後直接對相關性矩陣繪製矩陣熱圖。

然而最近在學習了 R 語言之後,使用 corrplot 包可以繪製出更加華麗、全面、直觀的相關性圖,相比較之下就覺得 Seaborn 提供的熱圖並不令人滿意。因此本文介紹一種新的更好的相關圖的繪製方法,來自 Biokit 工具包中的 biokit.viz.Corrplot() 類,並介紹其使用。

廢話不多說,先上圖展示:

image

  • (咱就說我們平時寫論文或者分析報告的時候,把這個圖往上面一放是不是高階大氣上檔次?)

對比 Python Seaborn 與 R corrplot

傳統的 Seaborn 相關性熱力圖

下面展示了一個簡單的使用示例:

# 匯入需要用到的庫
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 讀取資料,這裡以著名的波士頓資料集舉例
data = pd.read_csv('./data/boston.csv')

# 繪製熱圖
fig, ax = plt.subplots(figsize=(10, 8))
sns.heatmap(
    data.corr(), annot=True, cmap='coolwarm', ax=ax)

fig.savefig('../image/py_heatmap.png')

上述的程式碼繪製效果如下圖所示:

image

從上圖中可以看出傳統的 Seaborn 的熱圖存在一些問題:

  1. 各個欄位按順序輸出,不同相關性的單元格混排,難以一眼看出變數之間的顯著相關關係
  2. 即使選擇了對比度更高的,Colormap 仍然難以一眼找到具有較高相關性的變數
  3. 三角圖的繪製需要設定和使用 mask 引數,程式碼書寫複雜度瞬間翻倍

R 語言中的相關性熱力圖

相比較之下,如下是透過 R 語言繪製的相關性熱圖的示例:

### 匯入工具包 ###

library('corrplot')

### 讀取資料 ###
dat <- read.csv('./data/boston.csv')

### 計算取得相關性及相關性顯著性 ###
corr <- cor(dat)
corrl <- cor.mtest(dat)

### 繪圖 ###
corrplot.mixed(
  corr, order = 'AOE',
  lower = 'number', upper = 'ellipse',
  p.mat = corrl$p, insig = 'pch', pch = 4,
  tl.col = 'black', tl.pos = 'lt',
  number.cex = 0.7,  # 調整數字大小
)

關於 R 語言中 Corrplot 的使用方法,可以參考文章 An Introduction to corrplot Package,從原文中可以看見 R 的 Corrplot 包提供了大量自由度極高的引數選項對繪製的相關性熱圖進行調整。

繪製效果如下圖所示:

image

從上圖可以看出,與 Seaborn 相比,R 語言的相關性熱力圖具有以下幾個顯著的優勢:

  1. 允許使用橢圓形、不同大小的圓或者方片乃至小餅圖結合顏色標記來標記每個變數之間的相關性,更加直觀、清晰。
  2. 允許數值和形狀分開分別繪製在圖的左下角和右上角,豐富圖的內容。
  3. 對於未透過顯著性檢驗的相關性矩陣元素,使用散點標記劃去(圖中的叉)。

顯然我們希望在 Python 中也能像這樣輕鬆第繪製更加美觀和華麗的相關性熱力圖。

關於 Biokit

簡介

BioKit 一套專門用於生物資訊學、資料視覺化(BioKit.viz)、線上生物資料訪問(例如 UniProt,得益於生物服務的 NCBI)的工具。它還包含與資料分析相關的更高階的工具(例如 biokit.stats)。由於R在生物資訊學中很常見,Biokit 還提供了一個方便的模組來在 Python 指令碼或 shell 中執行 R(biokit.rtools 模組)。

文件手冊在 biokit.readthedocs.io,詳情請自行檢視。

庫的安裝

Biokit 的安裝在原生的 Python 和 Anaconda 中略有不同,按照官方給出的安裝方式,在 Python 中:

pip install biokit

如果在使用 Conda,則官方手冊和程式碼倉庫的 README.md 檔案中給出的安裝方式互不相同。其原因……額,咱也不知道咱也不敢問:

conda install bioconda # 根據官方倉庫 README.md
conda install biokit   # 根據手冊

不過我會推薦大家從原始碼構建安裝 Biokit,具體的原因我會在下文詳述。

相關性熱圖的繪製

基本使用方法

Biokit 中的熱圖是以一個類的形式定義的,具體的定義形式:

biokit.viz.Corrplot(data, na=0)

這裡初始化方法傳入兩個引數:na 是一個用於替換資料中的缺失值的預設值,也就是說如果資料裡面有缺失值就會被自動替換為這個值。不言而喻;

這個 data 則很有意思,我翻譯了官網上對於這個變數的介紹:

輸入可以是 DataFrame(Pandas)、List(python)或 numpy 矩陣。但是,請注意,值必須介於 -1 和 1 之間。如果不是, 或者如果矩陣(或列表列表)不是方陣,則相關性為其自動計算的結果。資料或計算的相關性儲存在 df 屬性中。

也就是說,在 Biokit 中,傳進的 data 引數可以是有待計算相關性的原始資料,也可以是相關性矩陣。如果傳入前者,方法會為你計算相關性係數。

完成初始化之後,需要呼叫 .plot() 方法來繪圖。其中,關於繪製圖片的方法 biokit.viz.Corrplot.plot() 定義形式如下:

def plot(
    self, fig=None, grid=True, rotation=30, 
    lower=None, upper=None, shrink=0.9, facecolor="white",
    colorbar=True, label_color="black", fontsize="small",
    edgecolor="black", method="ellipse", order_method="complete",
    order_metric="euclidean", cmap=None, ax=None, 
    binarise_color=False):

詳述一些繪圖引數的問題及細節

對於每個引數的含義的說明,如下表所示:

名稱 預設值 含義
fig None 一個 matplotlib 圖物件,用於繪製熱力圖。預設情況下會自動建立,也可以在使用的時候具體指定。
grid True 新增網格(預設為灰色)。您可以將其設定為False或顏色。
rotation 30 在y軸上旋轉標籤的角度
lower None 設定左下角部分的熱力圖的繪製方法,可選的引數有 'ellipse''square''rectangle' 以及'color''text''circle''number''pie',繪製效果即字面意思所示,本文的讀者可自行嘗試每一種繪製效果。
upper None 設定右上上角部分的熱力圖的繪製方法,可選引數同 lower,此不復贅述。
shrink 0.9 符號使用每個小方格的最大空間(百分比)。如果提供負值,則取絕對值;如果大於 1,符號將與邊框方格重疊。
facecolor "white" 圖整體背景的顏色(預設為白色)。
colorbar True 新增顏色條(預設為True)。
label_color "black" 軸標籤的顏色(預設為黑色)。
fontsize "small" 字型的大小,預設為“small”。可以直接用數字控制大小。
edgecolor "black" 程式碼文件中未曾提及但是確實存在的引數選項,目測其功能是指定繪製矩形、橢圓、圓等圖形時圖形邊緣的顏色。。
method "ellipse" 在沒有指定 lower 或者 upper 引數時生效,
order_method "complete" 使用方法對相關性矩陣的行和列重新排序,使得相似的變數被聚集在一起。這種排序可以幫助你更清晰地觀察和理解資料的相關性模式。
order_metric "euclidean" 用於計算距離的度量標準。下文詳述。
cmap None matplotlib 或 colormap 包中的有效的 Colormap(例如。'jet''copper')。預設為紅色/白色/藍色。
ax None 一個標準的 matplotlib 座標軸物件。不指定則自動建立。
binarise_color False 又是一個程式碼文件中沒說過的引數。目測其功能是將顏色值二值化(binarize)。具體來說,如果 binarise_color 被設定為 True,顏色值將被轉換為二進位制值。換句話說,簡化顏色表示,僅使用兩種顏色來表示正負相關性。

對於上表中的部分引數,我這裡需要給出一些詳細的說明:

我寫不動了,這段內容我讓 Chat 姐幫我寫了。請讀者們體諒一下。

關於 order_method 引數

order_method 引數決定了用於層次聚類的連結方法。這些方法定義瞭如何計算聚類之間的距離。常見的連結方法包括:

  • 'single': 最近鄰法。兩個聚類之間的距離由最近的一對點決定。
  • 'complete': 最遠鄰法。兩個聚類之間的距離由最遠的一對點決定。
  • 'average': 平均法。兩個聚類之間的距離由所有點對之間的平均距離決定。
  • 'centroid': 質心法。兩個聚類之間的距離由它們質心之間的距離決定。
  • 'median': 中位數法。計算兩個聚類的中位數質心之間的距離。
  • 'ward': Ward法。最小化每個聚類的平方和誤差。

這些方法是透過 scipy.cluster.hierarchy.linkage 函式實現的。選擇不同的方法會影響聚類結果,從而影響相關性矩陣的排序。

關於 order_metric 引數

order_metric 引數決定了用於計算距離的度量標準。常見的度量標準包括:

  • 'euclidean': 歐幾里得距離。
  • 'cityblock': 曼哈頓距離(城市街區距離)。
  • 'cosine': 餘弦距離。
  • 'hamming': 漢明距離。
  • 'jaccard': 傑卡德距離。

這些度量標準是透過 scipy.spatial.distancescipy.cluster.hierarchy 提供的。選擇不同的距離度量會影響聚類計算的結果。

在使用中,order_methodorder_metric 會對相關性矩陣的行和列重新排序,使得相似的變數被聚集在一起。這種排序可以幫助你更清晰地觀察和理解資料的相關性模式。為了方便讀者理解,我們這裡給出一段示例程式碼——例如,如果你使用 'complete' 方法和 'euclidean' 距離,對應的程式碼如下:

Y = self.linkage(
    self.df, 
    method=order_method, 
    metric=order_metric)
ind1 = hierarchy.fcluster(
    Y, 0.7 * max(Y[:, 2]), "distance")
Z = hierarchy.dendrogram(Y, no_plot=True)
idx1 = Z["leaves"]
cor2 = self.df.iloc[idx1, idx1]

這段程式碼首先計算相關性矩陣的層次聚類,然後根據聚類結果對矩陣的行和列進行重新排序。self.linkage 函式呼叫了 scipy.cluster.hierarchy.linkagehierarchy.fclusterhierarchy.dendrogram 分別用於獲取聚類結果和繪製樹狀圖。

關於 cmap 引數

Biokit 原始碼中應用 camp 引數的方法是:

self.cm = cmap_builder(cmap)

所以你可以使用如下的指定 colormap 的方法:

  • 指定 colormap 的名稱:cmap='viridis'
  • 指定三種具體顏色的過渡:cmap=['#2F7FC1', '#FFFFFF', '#D8383A']

⚠ ⚠ ⚠ 不要使用 如下的方法指定 colormap,否則將會報錯:

# 不要這樣做!
import matplotlib.pyplot as plt
from

cmap = plt.cm.get_cmap('inferno') # 獲取cmap物件
data = ...

... # 中間程式碼略

corrp = Corrplot(data)
corrp.plot(
  cmap=cmap
)

改進 BioKit 中 Corrplot 對 colormap 的支援

事情的起因

回到本文最開頭的那張圖片,繪製這張圖片我使用瞭如下的程式碼:

import pandas as pd
import matplotlib.pyplot as plt

from biokit.viz import Corrplot

data = pd.read_csv('./data/boston.csv')

fig, ax = plt.subplots(figsize=(10, 8))
datacorr = Corrplot(data)
datacorr.plot(
    ax=ax,
    lower='ellipse', upper='text', 
    cmap='YlGnBu_r', edgecolor='grey', 
    fontsize=12, order_method='centroid')

如果讀者們執行我給出的這段程式碼,就會發現圖片繪製出來的效果和本文中所展示的有所不同——為什麼右上半個三角中的數字只有紅藍兩種顏色,而本文中展示的繪製效果則是疊加了 viridis Colormap 的效果呢?

實際上,原生的 Biokit 中 Corrplot 在使用 method='number' 的時候確實不支援任意 Colormap,只支援紅藍雙色。參考 Biokit 在 Github 上的原始碼倉庫 biokit/biokit 中的 Jupyter Notebook biokit/viz/corrplot 中的說明:

c.plot(method='text', fontsize=8, colorbar=False)
# only red to blue colormap is implemented so far

image

但是仔細檢視原始碼之後,我發現想要修復這個問題使 .plot(method='text') 支援 Colormap 其實很簡單。參考 stackoverflow 上的提問 Getting individual colors from a color map in matplotlib,我發現 matplotlib 中有個 Normalize 類能夠將 colormap 物件對映到任何數值範圍內:

matplotlib.colors.Normalize(vmin=vmin, vmax=vmax)

我們知道相關性是一個從 -1 到 1 之間的數值,因此只需要將使用者指定的 Colormap 對映到這個區間範圍,再根據每個相關性矩陣元素的值從標準化之後的 Colormap 中選取顏色就好了。具體的實現方法:

Biokit 中原始的這段程式碼為:

...
                elif method in ['number', 'text']:
                    from easydev import precision
                    if d<0:
                        edgecolor = 'red'
                    elif d>=0:
                        edgecolor = 'blue'
                    ax.text(x,y, precision(d, 2), color=edgecolor,
                            fontsize=self.fontsize, horizontalalignment='center',
                            weight='bold', alpha=max(0.5, d_abs))
                            # withdash=False)
...

我給修改成了:

...
                elif method in ['number', 'text']:
                    from easydev import precision
                    # if d<0:
                    #     edgecolor = 'blue'
                    # elif d>=0:
                    #     edgecolor = 'red'
                    # Instantiate a 
                    # `matplotlib.colors.Normalize()` object
                    # as `color_norm`
                    color_norm = Normalize(
                        # the range of correlation coefficient
                        vmin=-1.0, 
                        vmax=1.0)
                    # select colors for each `d` 
                    # with normalized colormap
                    edgecolor = self.cm(
                        color_norm(d))
                    # plot the label with the color
                    ax.text(
                        x,
                        y, 
                        precision(d, 2), 
                        color=edgecolor,
                        fontsize=self.fontsize, 
                        horizontalalignment='center',
                        weight='bold', 
                        # alpha=max(0.5, d_abs) 
                        # withdash=False)
                        ) # alpha is no longer needed
...

我當時覺得這個改動還蠻好的,所以大概是在這篇部落格寫完之前的上個星期,我向原倉庫提交了 Feature: A Method to Apply colormap to corrplot while Using method='number' #78,結果原作者一直不回覆我。後來我乾脆自己動手,又提交了 Pull Request for Issue #78: Apply Colormap to Corrplot #79後來我看了一下 Biokit 倉庫現在的情況——好傢伙,上次更新都已經是 3 年前了,不知道為什麼這個倉庫自那之後便一直沒有再更新過。

所以讀者們如果想要使用像本文開頭所展示的那樣帶顏色的 text Corrplot,可以從 Github 上拉取 我 fork 並修改過原始碼的 repo,然後在本地透過原始碼構建安裝。

拉取 Biokit 原始碼並在本地構建

如果您的計算機環境上已經配置過 Git 工具則可以使用下面的命令拉取專案到本地:

git clone https://github.com/GitHubonline1396529/biokit.git

進入本地專案資料夾並構建安裝原始碼:

cd ./biokit
pip install .

注意!不要再使用 python -u ./setup.py 了! 這種安裝方式已經嚴重過時,被棄用了。(居然到現在為止 CSDN 上還有那麼多博文在建議這樣構建原始碼,每天都有一大堆新手跑過來問我。我還能說什麼?)

總結和補充

Biokit 是一個功能強大的庫,儘管本文僅介紹其熱力圖的繪製,但是實際上這個工具包的功能非常的多,有待讀者們發現。但是中文網際網路上對於 Biokit 的介紹很少,我直接透過 Bing 搜尋找到的也只有騰訊雲社群上的 BioKit!讓你用Python也可以輕鬆繪製矩陣熱力圖... 這一篇。

不知道原專案後面會不會更新,但是這麼好的專案沉了也是蠻可惜的。但是憑我自學 Python 兩年半的功夫肯定是更新不動這麼大的一個專案的。

我也相當於是在這裡問問有沒有人願意提供幫助吧。

相關文章