Matplotlib 是一個常用的資料視覺化工具。但是在使用之前,若是沒有先做設定的話,Matplotlib 無法正確地顯示中文。本文章將一步一步地講解如何解決 Matplotlib 的顯示問題。
由於我只有 Mac OS 系統,所以接下來只會講解如何在 Mac OS 上做設定。
首先,我們來執行下面這一段 Matplotlib 的程式碼。
import matplotlib.pyplot as plt import pandas as pd df = pd.DataFrame([ [2015, 23_462_914], [2016, 23_515_945], [2017, 23_555_522], [2018, 23_580_080], [2019, 23_596_027], [2020, 23_582_179], ], columns=['年份', '人口']) plt.plot('年份', '人口', data=df) plt.legend() plt.xlabel('年份') plt.ylabel('人口') plt.title('台灣歷年人口')
如果你執行的結果也是像上圖一樣,中文字都變成了框框,那表示你必須要繼續往下閱讀。。。。。
首先我們要先準備好中文字型檔。請到 Google Noto Fonts 網頁下,下載 Noto Sans CJK TC 和 Noto Serif CJK TC 這兩個中文字型。下載下來後會是兩個 ZIP 檔,請先將它們解壓縮好放著。可以看到,NotoSansCJKtc 包含 Sans 和 SansMono 字型,而 NotoSerifCJKtc 包含 Serif 字型。
~ % ls NotoSansCJKtc-hinted NotoSansCJKtc-hinted.zip NotoSerifCJKtc-hinted NotoSerifCJKtc-hinted.zip ~ % ls NotoSansCJKtc-hinted LICENSE_OFL.txt NotoSansCJKtc-Black.otf NotoSansCJKtc-Bold.otf NotoSansCJKtc-DemiLight.otf NotoSansCJKtc-Light.otf NotoSansCJKtc-Medium.otf NotoSansCJKtc-Regular.otf NotoSansCJKtc-Thin.otf NotoSansMonoCJKtc-Bold.otf NotoSansMonoCJKtc-Regular.otf README ~ % ls NotoSerifCJKtc-hinted NotoSerifCJKtc-hinted LICENSE_OFL.txt NotoSerifCJKtc-Black.otf NotoSerifCJKtc-Bold.otf NotoSerifCJKtc-ExtraLight.otf NotoSerifCJKtc-Light.otf NotoSerifCJKtc-Medium.otf NotoSerifCJKtc-Regular.otf NotoSerifCJKtc-SemiBold.otf README
接下來,切換到 Matplotlib 套件的路徑。以下是在 Miniconda 下,Matplotlib 的字型檔的路徑。這路徑下存放著 Matplotlib 內建的字型檔。很可惜,其中沒有中文。
~ % cd ~/.conda/envs/example/lib/python3.8/site-packages/matplotlib/mpl-data/fonts/ttf
複製所有剛剛從 Google Noto Fonts 下載的字型檔到 mpl-data/fonts/ttf/ 下。Matplotlib 也有支援 OTF 字型檔案格式。
ttf % cp ~/NotoSansCJKtc-hinted/*.otf . ttf % cp ~/NotoSerifCJKtc-hinted/*.otf .
然後,切換到 .matplotlib,並刪除它下面的 fontlist-v330.json。.matplotlib 是 Matplotlib 的 cache 資料夾。
ttf % cd ~/.matplotlib .matplotlib % ls fontlist-v330.json .matplotlib % rm fontlist-v330.json .matplotlib % ls
刪除後,你要先停掉 Jupyter,再重啟並且執行我們一開始的那個程式碼。那個程式碼裡面會呼叫 Matplotlib,所以 Matplotlib 會重新產生 fontlist-v330.json。
.matplotlib % ls fontlist-v330.json .matplotlib % vi fontlist-v330.json
打開 fontlist-v330.json,我們可以看到 Matplotlib 已經載入我們剛剛放入的字型檔。以下只有列出 NotoSansCJKtc、NotoSerifCJKtc 和 NotoSansMonoCJKtc 的 Regular 的字型檔。其中,要留意它們的 name 屬性。
... { "fname": "fonts/ttf/NotoSansCJKtc-Regular.otf", "name": "Noto Sans CJK TC", "style": "normal", "variant": "normal", "weight": 400, "stretch": "normal", "size": "scalable", "__class__": "FontEntry" }, ... { "fname": "fonts/ttf/NotoSerifCJKtc-Regular.otf", "name": "Noto Serif CJK TC", "style": "normal", "variant": "normal", "weight": 400, "stretch": "normal", "size": "scalable", "__class__": "FontEntry" }, ... { "fname": "fonts/ttf/NotoSansMonoCJKtc-Regular.otf", "name": "Noto Sans Mono CJK TC", "style": "normal", "variant": "normal", "weight": 400, "stretch": "normal", "size": "scalable", "__class__": "FontEntry" }, ...
接下來切回 Matplotlib 套件的字型檔目錄,並且編輯 matplotlibrc。
.matplotlib % cd ~/.conda/envs/example/lib/python3.8/site-packages/matplotlib/mpl-data mpl-data % ls fonts images matplotlibrc stylelib mpl-data % vi matplotlibrc
在 matplotlibrc 中,找到以下這幾行。移除前面的 # 號。剛剛在 fontlist-v330.json 中,我們找到的那三個 Regular 設定與設定裡面的 name 屬性值。如 NotoSansCJKtc-Regular.otf 是 Sans-Serif。所以,將它的 name 屬性值 Noto Sans CJK TC 插入到 font.sans-serif 列表中的第一個。以此類推,將三個都填入。
font.family: sans-serif #font.style: normal #font.variant: normal #font.weight: normal #font.stretch: normal #font.size: 10.0 font.serif: Noto Serif CJK TC, DejaVu Serif, ... font.sans-serif: Noto Sans CJK TC, DejaVu Sans, ... #font.cursive: Apple Chancery, Textile, ... #font.fantasy: Comic Neue, Comic Sans MS, ... font.monospace: Noto Sans Mono CJK TC, DejaVu Sans Mono, ...
基本上,這樣就設定完成了。停掉 Jupyter,再重新執行我們最一開始程式,來看看是否可以正確地顯示中文。