### matplotlib 中的字體管理
使用 matplotlib 可視化數(shù)據(jù)時(shí),經(jīng)常要面對(duì)字體設(shè)置的問(wèn)題。在要用到中文時(shí),如果設(shè)置不當(dāng),會(huì)出現(xiàn)亂碼。網(wǎng)上已經(jīng)有很多文章提供了一些解決方法,但多數(shù)是片段式的“授人以魚(yú)”,本文將系統(tǒng)地討論 matplotlib 中的字體管理,希望能“授人以漁”。
環(huán)境:Windows 10 + anaconda (python 3.8) + matplotlib 3.5
如果你的環(huán)境不同,相關(guān)程序安裝位置不同,一些細(xì)節(jié),如路徑、數(shù)量等可能會(huì)有一些差異,但“對(duì)應(yīng)”到你的環(huán)境即可。
matplotlib 中使用的字體來(lái)源于三個(gè)途徑:
1. matplotlib 自帶的字體;
2. windows 系統(tǒng)字體;
3. 用戶(hù)提供的第三方字體。
**注意**
1. 實(shí)際中不建議調(diào)用用戶(hù)字體目錄中的第三方字體文件;
2. 有很多特殊的字體不兼容,會(huì)報(bào)錯(cuò)
#### windows 10 系統(tǒng)字體
Windows 系統(tǒng)字體文件保存在 `C:\Windows\Fonts` 文件夾中,在資源管理器中打開(kāi)這個(gè)文件夾,一般是以大圖標(biāo)的形式顯示,看起來(lái)是這樣的:
字體系列,相當(dāng)于一個(gè)子文件夾,可以進(jìn)一步打開(kāi),如下圖
在 windows 中,字體是以“字體文件”的形式保存的,但在應(yīng)用軟件中為了調(diào)用的直觀性和方便性,則是通過(guò)字體的“名稱(chēng)”屬性來(lái)調(diào)用的。
#### 字體文件的類(lèi)型和字體類(lèi)型
字體文件格式和字體類(lèi)型繁多,常見(jiàn)的有:
字體文件:`.TTF, .FON, .TTC, .AFM`等后綴(格式)。
字體類(lèi)型:`OpenType, TrueType, 光柵` 等類(lèi)型。
**光柵字體(.FON)**:是針對(duì)特定的顯示分辨率以不同大小存儲(chǔ)的位圖,用于Windows系統(tǒng)中屏幕上的菜單、按鈕等處文字的顯示。它并不是以矢量描述的,放大以后會(huì)出現(xiàn)鋸齒,只適合屏幕描述。不過(guò)它的顯示速度非??欤宰鳛橄到y(tǒng)字體而在Windows中使用。
**矢量字體(.FON)**
雖然擴(kuò)展名和光柵字體一樣,但是這種字體卻是由基于矢量的數(shù)學(xué)模型定義的,是Windows系統(tǒng)字體的一類(lèi),一些windows應(yīng)用程序會(huì)在較大尺寸的屏幕顯示中自動(dòng)使用矢量字體來(lái)代替光柵字體的顯示。
.fon 字體文件其實(shí)是標(biāo)準(zhǔn)的windows可執(zhí)行文件(.exe)格式,分NE(New Executable)和PE(Portable Executable)兩種類(lèi)型,字體作為資源存在其中。
**TrueType字體(.TTF)**
TrueType是由美國(guó)蘋(píng)果公司和微軟公司共同開(kāi)發(fā)的一種電腦輪廓字體(曲線(xiàn)描邊字)類(lèi)型標(biāo)準(zhǔn)。這是我們?nèi)粘2僮髦薪佑|得最多的一種類(lèi)型的字體,其最大的特點(diǎn)就是它是由一種數(shù)學(xué)模式來(lái)進(jìn)行定義的基于輪廓技術(shù)的字體,這使得它們比基于矢量的字體更容易處理,保證了屏幕與打印輸出的一致性。同時(shí),這類(lèi)字體和矢量字體一樣可以隨意縮放、旋轉(zhuǎn)而不必?fù)?dān)心會(huì)出現(xiàn)鋸齒。
OpenType標(biāo)準(zhǔn)還定義了 OpenType 文件名稱(chēng)的后綴名。包含 TrueType 字體的OpenType文件后綴名為.ttf,包含PostScript字體的文件后綴名為.OTF。如果是包含一系列TrueType字體的字體包文件,那么后綴名為.TTC。
**.AFM** Adobe Font Metrics
這由Adobe公司開(kāi)發(fā),并包含了有關(guān)Type 1 PostScript 字體的度量特性的信息。AFM結(jié)構(gòu)需要一個(gè)定義了每一個(gè)字體符號(hào)的樣式的控制模版。它主要被用于UNIX。
#### matplotlib 字體管理
matplotlib 的matplotlib.font_manager 模塊,用于跨平臺(tái)查找、管理和使用字體。
這個(gè)模塊提供了一個(gè)可以跨后端(backend)和平臺(tái)共享的 FontManager 實(shí)例。其中的 findfont 函數(shù)在返回本地或系統(tǒng)字體路徑中與指定的 FontPropeties 實(shí)例匹配的最佳 TrueType (TTF) 字體文件。FontManager還處理 PostScript 后端使用的 Adobe Font Metrics (AFM) 字體文件。
也就是說(shuō) matplotlib 主要處理和使用 TTF, AFM 兩種字體。
字體文件可以保存在 matplotlib 的 fonts 文件夾下,也可以保存在系統(tǒng)字體路徑中。
> matplotlib 的字體文件夾路徑如下:
> C:\Users\用戶(hù)名\anaconda3\Lib\site-packages\matplotlib\mpl-data\fonts
該文件夾下有三個(gè)子文件夾,保存的都是 TTF, AFM 格式的字體文件。其中的 ttf 文件夾中約有 40 個(gè) TTF 字體文件。
打開(kāi) matplotlib 的 .fonts/ttf 文件夾,顯示如下
#### matplotlib的 font_manager 模塊
font_manager 模塊主要設(shè)計(jì)了3個(gè)類(lèi):
- .FontEntry:FontEntry 主要供 matplotlib 平臺(tái)管理、存儲(chǔ)可用字體屬性使用。我們可以用它來(lái)統(tǒng)一 matplotlib 繪圖的字體,減少多圖中字體設(shè)置的重復(fù)勞動(dòng)。
- .FontManager:FontManager 類(lèi)提供了字體管理功能。
- .FontProperties, 這個(gè)是一般用戶(hù)最常用的類(lèi),用法與`FontEntry`相似。定義一個(gè) `FontProperties`實(shí)例,可以設(shè)置 text 對(duì)象的全部屬性,并可以重復(fù)使用。無(wú)需在創(chuàng)建 text 時(shí)一個(gè)一個(gè)的設(shè)置。
```python
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.font_manager as fm
x = np.arange(0, 10, 0.2)
y = np.sin(x)
# strech,拉伸,相當(dāng)于word中的字體加寬
font_S = fm.FontProperties(family='Stencil',size=24, stretch=0)
font_M = fm.FontProperties(family='Mistral',size='xx-large',stretch=1000, weight='bold')
fig = plt.figure(constrained_layout=True)
ax = fig.add_subplot(111)
ax.set_title('Axes\'s Title', fontproperties = font_S)
ax.set_xlabel('xaxis label', fontproperties=font_M)
ax.plot(x,y)
plt.show()
```
#### matplotlib 常用中文字體名稱(chēng)對(duì)照表
Windows的字體對(duì)應(yīng)名稱(chēng),根據(jù)需要自行更換!
> 黑體 SimHei
> 微軟雅黑 Microsoft YaHei
> 微軟正黑體 Microsoft JhengHei
> 新宋體 NSimSun
> 新細(xì)明體 PMingLiU
> 細(xì)明體 MingLiU
> 標(biāo)楷體 DFKai-SB
> 仿宋 FangSong
> 楷體 KaiTi
> 仿宋_GB2312 FangSong_GB2312
> 楷體_GB2312 KaiTi_GB2312
#### matplotlib 中文亂碼問(wèn)題的解決
在matplotlib中,在使用中文的時(shí)候經(jīng)常會(huì)出現(xiàn)一些亂碼的問(wèn)題,如中文會(huì)變成小方格子。有以下幾種解決方案:
##### 方法一:修改配置文件matplotlibrc(不推薦這樣做)
```python
#使用下面的命令查看配置文件位置
import matplotlib
matplotlib.matplotlib_fname()
```
結(jié)果:
```
C:\\Users\\你的用戶(hù)名\\anaconda3\\lib\\site-packages\\matplotlib\\mpl-data\\matplotlibrc
```
比如要添加黑體,步驟:
> 1. 一般在C:\Windows\Fonts 里可以找到`黑體常規(guī)`字體文件
>
> 2. 將字體文件拷貝至:C:\\Users\\你的用戶(hù)名\anaconda3\\lib\\site-packages\\matplotlib\\mpl-data\fonts\ttf中
>
> 3. 用寫(xiě)字板或記事本打開(kāi)matplotlibrc文件:
>
> - 刪除 font.family 前面的 # 并將冒號(hào)后面改為“SimHei”;
>
> - 刪除 font.sans-serif 前面的 # 并在冒號(hào)后面添加“SimHei”
>
>
>
> 4. 修改后保存,重新運(yùn)行程序。
##### 方法二:動(dòng)態(tài)設(shè)置參數(shù)(可用)
在python腳本中動(dòng)態(tài)設(shè)置matplotlibrc,這樣就避免了更改配置文件的麻煩,方便靈活,例如:
```在python腳本中動(dòng)態(tài)設(shè)置matplotlibrc,這樣就避免了更改配置文件的麻煩,方便靈活,例如:import matplotlib as mplmpl.rcParams[‘font.sans-serif] = [‘SimHei’]
import matplotlib as mpl
mpl.rcParams[‘font.sans-serif] = [‘SimHei’]
```
由于更改了字體導(dǎo)致顯示不出負(fù)號(hào),將配署文件中axes.unicode minus : True修改為False 就可以了,當(dāng)然這而可以在代碼中完成。
```python
import matplotlib as mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認(rèn)字體
mpl.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負(fù)號(hào)’-'顯示為方塊的問(wèn)題
```
##### 方法三:使用字體管理器(推薦的方法)
優(yōu)點(diǎn):直接在代碼中指定中文字體文件,在每個(gè)出現(xiàn)中文的地方指定 fontproperties為剛才設(shè)置的字體;
缺點(diǎn):每個(gè)出現(xiàn)中文的地方如title都要指定字體,并不是每個(gè)地方如legend都提供指定字體的參數(shù)。
```python
myfont = matplotlib.font_manager.FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
```
直接設(shè)置為中文時(shí)
```python
ax.set_title(u'matplotlib中文顯示測(cè)試', fontproperties = myfont)
```
還可以直接:
```python
ax.set_title(u'matplotlib中文顯示測(cè)試', fontproperties='SimHei')
```
### 折線(xiàn)圖
折線(xiàn)圖用于分析自變量和因變量之間的趨勢(shì)關(guān)系,最適合用于顯示隨著時(shí)間而變化的連續(xù)數(shù)據(jù),同時(shí)還可以看出數(shù)量的差異,增長(zhǎng)情況。
Matplotlib 中繪制散點(diǎn)圖的函數(shù)為 plot() ,使用語(yǔ)法如下:
> `matplotlib.pyplot.plot`(**args*, *scalex=True*, *scaley=True*, *data=None*, ***kwargs*)
(1)簡(jiǎn)單的折線(xiàn)圖
在matplotlib面向?qū)ο蟮睦L圖庫(kù)中,pyplot是一個(gè)方便的接口。plot()函數(shù):支持創(chuàng)建單條折線(xiàn)的折線(xiàn)圖,也支持創(chuàng)建包含多條折線(xiàn)的復(fù)式折線(xiàn)圖----只要在調(diào)用plot()時(shí)傳入多個(gè)分別代表X軸和Y軸數(shù)據(jù)的list列表即可。
```python
import matplotlib.pyplot as plt
x_data = ['2011','2012','2013','2014','2015','2016','2017']
y_data = [58000,60200,63000,71000,84000,90500,107000]
plt.plot(x_data,y_data)
plt.show()
```
(2)復(fù)式折線(xiàn)圖:
```python
import matplotlib.pyplot as plt
x_data = ['2011','2012','2013','2014','2015','2016','2017']
y_data = [58000,60200,63000,71000,84000,90500,107000]
y_data2 = [52000,54200,51500,58300,56800,59500,62700]
plt.plot(x_data,y_data,color='red',linewidth=2.0,linestyle='--')
plt.plot(x_data,y_data2,color='blue',linewidth=3.0,linestyle='-.')
plt.show()
```
注:
說(shuō)明:
參數(shù)color=’ red ‘,可以換成color=’ #054E9F’,每?jī)蓚€(gè)十六進(jìn)制數(shù)分別代表R、G、B分量,除了使用red、blue、green等還可以參照下圖
參數(shù)linestyle可以選擇使用下面的樣式:
> '-' solid line style 表示實(shí)線(xiàn)
>
> '--' dashed line style 表示虛線(xiàn)
>
> '-.' dash-dot line style 表示短線(xiàn)、點(diǎn)相間的虛線(xiàn)
>
> ':' dotted line style 表示點(diǎn)線(xiàn)
參數(shù) linewidth 可以指定折線(xiàn)的寬度
參數(shù) marker 是指標(biāo)記點(diǎn),有如下的:
(3)管理圖例
對(duì)于復(fù)式折線(xiàn)圖,應(yīng)該為每條折線(xiàn)添加圖例,可以通過(guò)legend()函數(shù)來(lái)實(shí)現(xiàn)。該函數(shù)可傳入兩個(gè)list參數(shù),其中第一個(gè)list參數(shù)(handles參數(shù))用于引用折線(xiàn)圖上的每條折線(xiàn);第二個(gè)list參數(shù)(labels)代表為每條折線(xiàn)所添加的圖例
```python
import pandas as pd
import matplotlib.pyplot as plt
#讀取數(shù)據(jù)
data = pd.read_excel('matplotlib.xlsx')
plt.figure(figsize=(10,5))#設(shè)置畫(huà)布的尺寸
plt.title('Examples of line chart',fontsize=20)#標(biāo)題,并設(shè)定字號(hào)大小
plt.xlabel(u'x-year',fontsize=14)#設(shè)置x軸,并設(shè)定字號(hào)大小
plt.ylabel(u'y-income',fontsize=14)#設(shè)置y軸,并設(shè)定字號(hào)大小
#color:顏色,linewidth:線(xiàn)寬,linestyle:線(xiàn)條類(lèi)型,label:圖例,marker:數(shù)據(jù)點(diǎn)的類(lèi)型
in1, = plt.plot(data['時(shí)間'],data['收入_Jay'],color="deeppink",linewidth=2,linestyle=':', marker='o')
in2, = plt.plot(data['時(shí)間'],data['收入_JJ'],color="darkblue",linewidth=1,linestyle='--', marker='+')
in3, = plt.plot(data['時(shí)間'],data['收入_Jolin'],color="goldenrod",linewidth=1.5,linestyle='-', marker='*')
plt.legend(handles = [in1,in2,in3],labels=['Jay income','JJ income','Jolon income'],loc=2)#圖例展示位置,數(shù)字代表第幾象限
plt.show()#顯示圖像
```
(4)管理多個(gè)子圖
```python
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
import matplotlib.font_manager as fm #字體管理器
my_font = fm.FontProperties(fname="/System/Library/Fonts/PingFang.ttc")
plt.figure()
x_data = np.linspace(-np.pi,np.pi,64,endpoint=True)
gs = gridspec.GridSpec(2,3) #將繪圖區(qū)分成兩行三列
ax1 = plt.subplot(gs[0,:])#指定ax1占用第一行(0)整行
ax2 = plt.subplot(gs[1,0])#指定ax2占用第二行(1)的第一格(第二個(gè)參數(shù)為0)
ax3 = plt.subplot(gs[1,1:3])#指定ax3占用第二行(1)的第二、三格(第二個(gè)參數(shù)為1:3)
#繪制正弦曲線(xiàn)
ax1.plot(x_data,np.sin(x_data))
ax1.spines['right'].set_color('none')
ax1.spines['top'].set_color('none')
ax1.spines['bottom'].set_position(('data',0))
ax1.spines['left'].set_position(('data',0))
ax1.set_title('正弦曲線(xiàn)',fontproperties=my_font)
#繪制余弦曲線(xiàn)
ax2.plot(x_data,np.cos(x_data))
ax2.spines['right'].set_color('none')
ax2.spines['top'].set_color('none')
ax2.spines['bottom'].set_position(('data',0))
ax2.spines['left'].set_position(('data',0))
ax2.set_title('余弦曲線(xiàn)',fontproperties=my_font)
#繪制正切曲線(xiàn)
ax3.plot(x_data,np.tan(x_data))
ax3.spines['right'].set_color('none')
ax3.spines['top'].set_color('none')
ax3.spines['bottom'].set_position(('data',0))
ax3.spines['left'].set_position(('data',0))
ax3.set_title('正切曲線(xiàn)',fontproperties=my_font)
plt.show()
```
結(jié)果: