###### tags: `Python` `matplotlib` # matplotlib 顯示中文 matplotlib 預設的字型並不是中文字型, 所以顯示中文會變成方框, 像是這個例子: ```python >>> import matplotlib.pyplot as plt >>> plt.pie( ... [800, 300, 400], ... labels=['交通', '娛樂', '教育']) ([<matplotlib.patches.Wedge object at 0x000001CB83DFC250>, <matplotlib.patches.Wedge object at 0x000001CB83DFC700>, <matplotlib.patches.Wedge object at 0x000001CB83DFCB80>], [Text(-0.11498140519131439, 1.093974074857458, '交通'), Text(-0.7360435164738056, -0.8174594435547703, '娛樂'), Text(0.7360438608860855, -0.817459133444544, '教育')]) ``` 顯示時會出現訊息告知目前的字型沒有 CJK 文字, 其中 4EA4 等等就是個別中文字的 unicode: ```python >>> plt.show() D:\Program Files\Python310\lib\tkinter\__init__.py:839: UserWarning: Glyph 20132 (\N{CJK UNIFIED IDEOGRAPH-4EA4}) missing from current font.func(*args) D:\Program Files\Python310\lib\tkinter\__init__.py:839: UserWarning: Glyph 36890 (\N{CJK UNIFIED IDEOGRAPH-901A}) missing from current font.func(*args) D:\Program Files\Python310\lib\tkinter\__init__.py:839: UserWarning: Glyph 23067 (\N{CJK UNIFIED IDEOGRAPH-5A1B}) missing from current font.func(*args) D:\Program Files\Python310\lib\tkinter\__init__.py:839: UserWarning: Glyph 27138 (\N{CJK UNIFIED IDEOGRAPH-6A02}) missing from current font.func(*args) D:\Program Files\Python310\lib\tkinter\__init__.py:839: UserWarning: Glyph 25945 (\N{CJK UNIFIED IDEOGRAPH-6559}) missing from current font.func(*args) D:\Program Files\Python310\lib\tkinter\__init__.py:839: UserWarning: Glyph 32946 (\N{CJK UNIFIED IDEOGRAPH-80B2}) missing from current font.func(*args) ``` 實際顯示畫面如下: ![](https://i.imgur.com/15MFWk2.png) ## 設定使用中文字體 不過其實系統本身已經有中文字型, 可以透過 [fontManager](https://matplotlib.org/stable/api/font_manager_api.html#matplotlib.font_manager.fontManager) 物件來檢視, 它會列出系統字型資資料夾下的字型: ```python >>> from matplotlib.font_manager import fontManager >>> for i in sorted(fontManager.get_font_names()): ... print(i) Agency FB Algerian ... Microsoft JhengHei Microsoft New Tai Lue Microsoft PhagsPa Microsoft Sans Serif Microsoft Tai Le Microsoft YaHei Microsoft Yi Baiti MingLiU MingLiU-ExtB ... ``` 只要使用 matplotlib 模組的 [rc()](https://matplotlib.org/stable/api/matplotlib_configuration_api.html#matplotlib.rc) 函式, 就可以改用任何一種字型, 例如: ```python >>> import matplotlib >>> matplotlib.rc('font', family='Microsoft JhengHei') ``` 這樣就可以改用微軟正黑體, 重新產生圓餅圖顯示: ```python >>> plt.pie( ... [800, 300, 400], ... labels=['交通', '娛樂', '教育']) ([<matplotlib.patches.Wedge object at 0x000001CB87E615D0>, <matplotlib.patches.Wedge object at 0x000001CB833A72E0>, <matplotlib.patches.Wedge object at 0x000001CB87E63310>], [Text(-0.11498140519131439, 1.093974074857458, '交通'), Text(-0.7360435164738056, -0.8174594435547703, '娛樂'), Text(0.7360438608860855, -0.817459133444544, '教育')]) >>> plt.show() ``` 就可以看到正確顯示中文的圖形了: ![](https://i.imgur.com/VfetvIq.png) ## 修改 rcParams 物件 剛剛的 rc 函式修改的其實是 matplotlib 模組的 [rcParams](https://matplotlib.org/stable/gallery/text_labels_and_annotations/font_family_rc.html) 字典物件, 你也可以直接修改設定, 例如: ```python >>> matplotlib.rcParams['font.family'] = 'MingLiU' >>> plt.pie( ... [800, 300, 400], ... labels=['交通', '娛樂', '教育']) ([<matplotlib.patches.Wedge object at 0x0000021F3E56BC40>, <matplotlib.patches.Wedge object at 0x0000021F3EE44910>, <matplotlib.patches.Wedge object at 0x0000021F3E569BD0>], [Text(-0.11498140519131439, 1.093974074857458, '交通'), Text(-0.7360435164738056, -0.8174594435547703, '娛樂'), Text(0.7360438608860855, -0.817459133444544, '教育')]) >>> plt.show() ``` 就會將字型改為明體: ![](https://i.imgur.com/GRuDazv.png) 使用 rc 函式的好處是同時可以更改多項設定, 例如若要將字體改回之前的黑體, 同時又想要將字體變大, 就可以這樣做: ```python >>> matplotlib.rc('font', ... family='Microsoft JhengHei', ... size=32 ... ) >>> plt.pie( ... [800, 300, 400], ... labels=['交通', '娛樂', '教育']) ([<matplotlib.patches.Wedge object at 0x0000021F3E51D000>, <matplotlib.patches.Wedge object at 0x0000021F3E51EE60>, <matplotlib.patches.Wedge object at 0x0000021F3E51EAD0>], [Text(-0.11498140519131439, 1.093974074857458, '交通'), Text(-0.7360435164738056, -0.8174594435547703, '娛樂'), Text(0.7360438608860855, -0.817459133444544, '教育')]) >>> plt.show() ``` ![](https://i.imgur.com/OA5xfKG.png) 要注意的是使用 rcParams 字典物件時, 索引鍵是`功能分類.細項名稱` 這樣的格式, 但叫用 rc 函式時, 則是 `rc('功能分類', 細項名稱=)` 的參數格式。 附帶一題, matplotlib 模組的 rcParams 和 matplotlib.pyplot 模組的 rcParams 引用的是同一個物件, 由以下程式可以確認: ```python >>> matplotlib.rcParams is matplotlib.pyplot.rcParams True ``` 這樣做只是方便大家使用而已。 ## matplotlibrc 設定檔 如果你希望儲存上述設定, 不需要每次都重新在程式中設定, 也可以將設定寫入 [matplotlibrc 檔](https://matplotlib.org/stable/tutorials/introductory/customizing.html#customizing-with-matplotlibrc-files), 目前使用的設定檔路徑可以由以下程式取得: ```python >>> matplotlib.matplotlib_fname() 'D:\\code\\pytest\\lib\\site-packages\\matplotlib\\mpl-data\\matplotlibrc' ``` 這是在我的虛擬環境的 matplotlib 安裝資料夾下, 預設內容所有設定都被註解, 並有加上許多解說, 你可以複製到當前目錄下, 然後修改其中的設定, 例如: ![](https://i.imgur.com/0KrWTpO.png) 存檔後, 在重新回到 Python 下匯入 matplotlib 模組, 就可以看到預設的字型已經更改了: ```python >>> import matplotlib >>> matplotlib.rcParams['font.family'] ['Microsoft JhengHei'] ``` 在 Windows 下你也可以將這個設定檔放到使用者家目錄的 .matplotlib 資料夾下, 這樣不論你在哪個目錄下, 都會採用設定檔中的設定。 如果是 \*nix 系統, 就要複製到家目錄下的 .config/matplotlib 下。 ## 用更具彈性的方式 matplotlib 提供有比較彈性的機制, 可以讓你快速變換字型, 並且在找不到指定的字型可用時, 依序採用替補的字型。這項功能藉助 serif、sans-serif、cursive、fantasy、monospace 這 5 種通用字型, 這每一種通用字型都可以指定實際要採用的字型清單, 實際使用時只要設定要用哪一種通用字型即可。舉例來說: ```python >>> matplotlib.rc('font', ... family='serif', ... serif=['ABC', 'MingLiU'], ... size=32 ... ) ``` 這裡我們設定 serif 通用字型實際上要用的字型是 ABC 字型, 如果執行時候找不到 ABC 行, 就改用清單中的下一個, 也就是 MingLiu 字型。另外, 我們也設定 font.family 是 serif 字型, 如此執行前面一樣的圓餅圖, 就會因為找不到 ABC 字型而依照上述設定套用明體字型了。 這個機制等於可以讓你設定 5 組字型清單, 並可隨時替換, 也可以透過字型清單因應不同平台套用不同的系統預設字型, 像是在 Mac 上預設並不會有微軟的字型, 就可以在字型清單中加入 Mac 對應的字型, 就不會因為找不到字型而顯示錯誤的結果了。 ## 暫時替換字型 如果想要在同一張圖上使用不同字體, 可以善用 matplotlib 或是 matplotlib.pyplot 模組都有的 [rc_context() 函式](https://matplotlib.org/stable/api/matplotlib_configuration_api.html#matplotlib.rc_context), 像是這樣: ```python >>> plt.rc('font', family='MingLiu') >>> with plt.rc_context(rc= {'font.family': 'Microsoft JhengHei'}): ... plt.title("圓餅圖") >>> plt.pie( ... [800, 300, 400], ... labels=['交通', '娛樂', '教育']) ([<matplotlib.patches.Wedge object at 0x0000025D64A0AA10>, <matplotlib.patches.Wedge object at 0x0000025D64A217E0>, <matplotlib.patches.Wedge object at 0x0000025D64A234F0>], [Text(-0.11498140519131439, 1.093974074857458, '交通'), Text(-0.7360435164738056, -0.8174594435547703, '娛樂'), Text(0.7360438608860855, -0.817459133444544, '教育')]) >>> plt.show() ``` 你可以看到一開始先設定使用明體, 然後透過 rc_context() 與 with 合用, 暫時改用黑體標示標題文字, with 結束後會自動變回使用明體, 因此圓餅圖上的文字就變成明體了。 ![](https://i.imgur.com/EoUFeZQ.png) ## 加入額外的中文字型 如果想要使用系統沒有的字型, 但不想將字型安裝到系統上;或是要使用有安裝、但是沒有放在系統字型資料夾下的字型, 可以透過 fontManager 的 [addFont()](https//matplotlib.org/stable/api/font_manager_api.html#matplotlib.font_manager.FontManager.addfont) 方法新增, 例如把上述的圓餅圖改成韓文: ```python >>> plt.pie( ... [800, 300, 400], ... labels=['교통', '오락', '기르다']) ``` 就會因為系統沒有韓文字型又顯示成方塊了: ![](https://i.imgur.com/3hDCQbT.png) 利用 addfont() 方法, 就可以動態加入額外的[韓文字型](https://fonts.google.com/noto/specimen/Noto+Serif+KR?noto.continent=Asia&noto.region=KR), 我將這個字型下載後儲存在 d:\\temp 資料夾下: ```python >>> fontManager.addfont('D:\\temp\\NotoSerifKR-Regular.otf') >>> for i in sorted(fontManager.get_font_names()): ... print(i) Agency FB ... Noto Serif CJK TC Noto Serif KR Noto Serif TC ... ``` 再設定使用這個韓文字型: ```python >>> matplotlib.rc('font', family='Noto Serif KR') >>> plt.pie( ... [800, 300, 400], ... labels=['교통', '오락', '기르다']) ([<matplotlib.patches.Wedge object at 0x000001CB88634340>, <matplotlib.patches.Wedge object at 0x000001CB88636050>, <matplotlib.patches.Wedge object at 0x000001CB88635750>], [Text(-0.11498140519131439, 1.093974074857458, '교통'), Text(-0.7360435164738056, -0.8174594435547703, '오락'), Text(0.7360438608860855, -0.817459133444544, '기르다')]) >>> plt.show() ``` 就可以正常顯示了: ![](https://i.imgur.com/HUkbWim.png) ## Colab 中顯示中文 如果是在 Colab 中,則可以上傳字型檔,或者是透過 Google 雲端硬碟存放字型,這樣就不需要每次重新開啟 Colab 連線都要在上傳字型檔。以下就以我將[思源黑體 NotoSansTC-Regular.ttf](https://fonts.google.com/selection?preview.text=%E8%88%87WebSiteBuilderTaiwan%E5%9C%98%E9%9A%8A%E4%B8%80%E8%B5%B7%E6%9E%B6%E8%A8%AD%E7%B6%B2%E7%AB%99%EF%BC%81) 檔放置在 Google 雲端硬碟 public/fonts 資料夾下為例,首先把 Google 雲端硬碟怪接到虛擬主機上: ```python from google.colab import drive drive.mount('/content/drive') ``` 然後到左邊的檔案窗格複製字型的路徑: ![image](https://hackmd.io/_uploads/BkS7Qde2yl.png) 就可以透過複製的路徑加入額外字型檔: ```python from matplotlib.font_manager import fontManager fontManager.addfont( '/content/drive/MyDrive/public/fonts/NotoSansTC-Regular.ttf' ) ``` 即可指定使用該字型: ```python import matplotlib matplotlib.rc('font', family='Noto Sans TC') ``` 現在繪製圖案就可以正確顯示中文: ![image](https://hackmd.io/_uploads/BJBC7_enyg.png) ## 結語 搞定中文, 你用 matplotlib 繪製的圖表就更親近使用者, 也更易閱讀, 希望這篇文章的內容可以幫到大家。