(資料科學學習手札87)利用adjustText解決matplotlib文字標籤遮擋問題

費弗裡發表於2020-06-13

本文示例程式碼、資料已上傳至我的Github倉庫https://github.com/CNFeffery/DataScienceStudyNotes

1 簡介

  在進行資料視覺化時我們常常需要在視覺化作品上進行一些文字標註,譬如對散點圖我們可以將每個散點對應的屬性資訊標註在每個散點旁邊,但隨著散點量的增多,或影像上的某個區域聚集了較多的散點時,疊加上的文字標註會擠在一起相互疊置,出現如圖1所示的情況:

(資料科學學習手札87)利用adjustText解決matplotlib文字標籤遮擋問題
圖1

  出現這種情況非常影響資料視覺化作品的呈現效果,而我們下面要介紹的adjustText是一個輔助matplotlib所繪製的影像自動調整文字位置以緩解遮擋現象的庫,其靈感來源於R中非常著名的輔助ggplot2解決文字遮擋問題的ggrepel

(資料科學學習手札87)利用adjustText解決matplotlib文字標籤遮擋問題
圖2

  它通過演算法迭代,在一輪輪的迭代過程中逐漸消除文字遮擋現象:

(資料科學學習手札87)利用adjustText解決matplotlib文字標籤遮擋問題
圖3

  下面我們就來學習如何使用adjustText解決matplotlib影像文字遮擋問題。

2 使用adjustText解決文字遮擋問題

2.1 從一個簡單的例子出發

  使用pip install adjustTextconda install -c conda-forge adjusttext 來安裝adjustText。安裝成功之後,首先生成隨機示例資料以方便之後的演示:

import matplotlib.pyplot as plt
from adjustText import adjust_text
import numpy as np

#解決中文顯示問題
plt.rcParams['font.sans-serif'] = ['SimHei']

seed = np.random.RandomState(42) # 固定隨機數水平
x, y = seed.uniform(0, 1, [2, 100]) # 產生固定的均勻分佈隨機數
texts = [f'文字{i}' for i in range(x.__len__())]

  接著我們先不使用adjustText調整影像,直接繪製出原始的散點+文字標籤

fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(x, y, c='SeaGreen', s=10) # 繪製散點

# 繪製所有點對應的文字標籤
for x_, y_, text in zip(x, y, texts):
    plt.text(x_, y_, text, fontsize=12)

# 美觀起見隱藏頂部與右側邊框線
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

fig.savefig('圖4.png', dpi=300, bbox_inches='tight', pad_inches=0) # 儲存影像
(資料科學學習手札87)利用adjustText解決matplotlib文字標籤遮擋問題
圖4

  可以看到,在通常的情況下,散點聚集的區域內文字標籤非常容易重疊在一起,接下來我們使用adjustText的基礎功能來消除文字重疊現象:

(資料科學學習手札87)利用adjustText解決matplotlib文字標籤遮擋問題
圖5

  這時可以看到與圖4相比,圖5中的所有文字都沒有出現彼此重疊現象,adjustText幫助我們自動微調了文字的擺放位置,並且距離原始散點偏移較大的文字還貼心的加上了連線線,至此,我們就初探了adjustText的強大功能,接下來我們來學習adjustText的更多功能。

2.2 adjust_text的用法

  adjustText中的核心功能都通過呼叫函式adjust_text來實現,其核心引數如下:

texts:List型,每個元素都是表示單個文字標籤對應的matplotlib.text.Text物件

ax:繪製文字標籤的目標axe物件,預設為最近一次的axe物件

lim:int型,控制迭代調整文字標籤位置的次數,預設為500次

precision:float型,用於決定迭代停止的精度,預設為0.01,即所有標籤相互遮擋部分的長和寬佔所有標籤自身長寬之和的比例,addjust_text會在精度達到precision和迭代次數超過lim這兩個條件中至少有一個滿足時停止迭代

only_move:字典型,用於指定文字標籤與不同物件發生遮擋時的位移策略,鍵有'points''text''objects',對應的值可選'xy''x''y',分別代表豎直和水平方向均調整、只調整水平方向以及只調整豎直方向

arrowprops:字典型,用於設定偏移後的文字標籤與原始位置之間的連線樣式,下文會作具體演示

save_steps:bool型,用於決定是否儲存記錄迭代過程中各輪的幀影像,預設為False

save_prefix:str型,當save_steps設定為True時,用於指定中間幀儲存的路徑,預設為'',即當前工作路徑

  下面我們來演示一下這些引數的使用效果,首先我們來看看only_move引數的效果,在圖6的基礎上,我們設定only_move={'text': 'x'},即當文字出現遮擋時,只在水平方向上進行偏移,這裡將save_steps設定為True以直觀地檢視偏移過程:

fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(x, y, c='SeaGreen', s=10) # 繪製散點

# 使用adjustText修正文字重疊現象
new_texts = [plt.text(x_, y_, text, fontsize=12) for x_, y_, text in zip(x, y, texts)]
adjust_text(new_texts, 
            only_move={'text': 'x'},
            arrowprops=dict(arrowstyle='-', color='grey'),
            save_steps=True)

# 美觀起見隱藏頂部與右側邊框線
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
(資料科學學習手札87)利用adjustText解決matplotlib文字標籤遮擋問題
圖6
  可以看到在整個迭代微調的過程中,每個標籤只在水平方向發生位移,你可以根據自己作圖的實際需要靈活調整這裡的平移策略。接下來我們來看看`arrowprops`對視覺化結果的影響,在之前的例子裡我們設定了`arrowprops={arrowstyle='-', color='grey'}`,其中`arrowstyle`用於設定連線的線型,`color`不用多說,接下來我們新增引數`lw`用於控制線的寬度,並對線型與顏色進行修改:
fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(x, y, c='SeaGreen', s=10) # 繪製散點

# 使用adjustText修正文字重疊現象
new_texts = [plt.text(x_, y_, text, fontsize=12) for x_, y_, text in zip(x, y, texts)]
adjust_text(new_texts, 
            arrowprops=dict(arrowstyle='->', 
                            color='red',
                            lw=1))

# 美觀起見隱藏頂部與右側邊框線
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)

fig.savefig('圖7.png', dpi=300, bbox_inches='tight', pad_inches=0) # 儲存影像

  這時連線隨著我們自定義的設定改變到相應的樣式:

(資料科學學習手札87)利用adjustText解決matplotlib文字標籤遮擋問題
圖7

  有關adjustText的更多引數設定資訊和示例可以去官方文件(https://adjusttext.readthedocs.io/en/latest/ )檢視。

  以上就是本文的全部內容,如有疑問歡迎在評論區與我們討論。

相關文章