matplotlib 顯示中文
Tags: python, matplotlib
Published: 2023-02-01 02:25:38

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)

## 結語

搞定中文, 你用 matplotlib 繪製的圖表就更親近使用者, 也更易閱讀, 希望這篇文章的內容可以幫到大家。