matplotlib 顯示中文
codemee
Posted on February 1, 2023
matplotlib 預設的字型並不是中文字型, 所以顯示中文會變成方框, 像是這個例子:
>>> 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:
>>> 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)
實際顯示畫面如下:
設定使用中文字體
不過其實系統本身已經有中文字型, 可以透過 fontManager 物件來檢視, 它會列出系統字型資資料夾下的字型:
>>> 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() 函式, 就可以改用任何一種字型, 例如:
>>> import matplotlib
>>> matplotlib.rc('font', family='Microsoft JhengHei')
這樣就可以改用微軟正黑體, 重新產生圓餅圖顯示:
>>> 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()
就可以看到正確顯示中文的圖形了:
修改 rcParams 物件
剛剛的 rc 函式修改的其實是 matplotlib 模組的 rcParams 字典物件, 你也可以直接修改設定, 例如:
>>> 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()
就會將字型改為明體:
使用 rc 函式的好處是同時可以更改多項設定, 例如若要將字體改回之前的黑體, 同時又想要將字體變大, 就可以這樣做:
>>> 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()
要注意的是使用 rcParams 字典物件時, 索引鍵是功能分類.細項名稱
這樣的格式, 但叫用 rc 函式時, 則是 rc('功能分類', 細項名稱=)
的參數格式。
附帶一題, matplotlib 模組的 rcParams 和 matplotlib.pyplot 模組的 rcParams 引用的是同一個物件, 由以下程式可以確認:
>>> matplotlib.rcParams is matplotlib.pyplot.rcParams
True
這樣做只是方便大家使用而已。
matplotlibrc 設定檔
如果你希望儲存上述設定, 不需要每次都重新在程式中設定, 也可以將設定寫入 matplotlibrc 檔, 目前使用的設定檔路徑可以由以下程式取得:
>>> matplotlib.matplotlib_fname()
'D:\\code\\pytest\\lib\\site-packages\\matplotlib\\mpl-data\\matplotlibrc'
這是在我的虛擬環境的 matplotlib 安裝資料夾下, 預設內容所有設定都被註解, 並有加上許多解說, 你可以複製到當前目錄下, 然後修改其中的設定, 例如:
存檔後, 在重新回到 Python 下匯入 matplotlib 模組, 就可以看到預設的字型已經更改了:
>>> import matplotlib
>>> matplotlib.rcParams['font.family']
['Microsoft JhengHei']
在 Windows 下你也可以將這個設定檔放到使用者家目錄的 .matplotlib 資料夾下, 這樣不論你在哪個目錄下, 都會採用設定檔中的設定。
如果是 *nix 系統, 就要複製到家目錄下的 .config/matplotlib 下。
用更具彈性的方式
matplotlib 提供有比較彈性的機制, 可以讓你快速變換字型, 並且在找不到指定的字型可用時, 依序採用替補的字型。這項功能藉助 serif、sans-serif、cursive、fantasy、monospace 這 5 種通用字型, 這每一種通用字型都可以指定實際要採用的字型清單, 實際使用時只要設定要用哪一種通用字型即可。舉例來說:
>>> 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() 函式, 像是這樣:
>>> 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 結束後會自動變回使用明體, 因此圓餅圖上的文字就變成明體了。
加入額外的中文字型
如果想要使用系統沒有的字型, 但不想將字型安裝到系統上;或是要使用有安裝、但是沒有放在系統字型資料夾下的字型, 可以透過 fontManager 的 addFont() 方法新增, 例如把上述的圓餅圖改成韓文:
>>> plt.pie(
... [800, 300, 400],
... labels=['교통', '오락', '기르다'])
就會因為系統沒有韓文字型又顯示成方塊了:
利用 addfont() 方法, 就可以動態加入額外的韓文字型, 我將這個字型下載後儲存在 d:\temp 資料夾下:
>>> 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
...
再設定使用這個韓文字型:
>>> 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()
就可以正常顯示了:
結語
搞定中文, 你用 matplotlib 繪製的圖表就更親近使用者, 也更易閱讀, 希望這篇文章的內容可以幫到大家。
Posted on February 1, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.