# Matplotlib
###### tags: `Python` `Matplotlib`
#### 2022/01/11 by JohnAxer
## 基本觀念
- 畫圖當然要先有畫布,這個畫布就是「圖」,一張圖可以再分為幾個子圖,每個子圖都可以有一到多個軸空間,每個軸空間可以有1到多個軸。
- 一個圖至少有一個子圖,每個子圖至少有一個軸空間,軸空間必包含軸。
- 圖 figure
- 子圖 subplot
- 軸空間 axes
- 軸 axis
## 兩種寫法
- 預計的執行結果
- 
- 物件導向的寫法 OO-style
- 注意到第16行,如果一個圖要包含多個子圖,可以這樣宣告。
``fig, ax = plot.subplots(2, 1)``
- 這樣就有兩個子圖,呈兩列(row)一欄(column)的排列。此時 ax 是一個 ndarray,裡面有兩個子圖 (AxesSubplot)。這個可以用
``print(ax) 和 print(type(ax))``
```python=
import matplotlib
import matplotlib.pyplot as plot
import numpy
#正確顯示中文字
matplotlib.rcParams['font.sans-serif'] = 'microsoft jhenghei'
matplotlib.rcParams['font.family'] = 'sans-serif'
#資料
x = numpy.linspace(0, 2*numpy.pi, 24)
ysin = numpy.sin(x)
ycos = numpy.cos(x)
#物件導向的方式
#OO-style
fig, ax = plot.subplots()
ax.plot(x, ysin, label="sin")
ax.plot(x, ycos, label="cos")
ax.set_title("sin 和 cos")
ax.set_xlabel("弧度")
ax.set_ylabel("值")
ax.legend()
#設定繪圖視窗標題
fig.canvas.set_window_title('By JohnAxer')
plot.show()
```
- 函數的寫法 pyplot-style
- 在 pyplot-style 的寫法下,我們其實可以直接用 plot 直接繪圖,因為 matplotlib 會自動幫我們建立一個圖(包含1個子圖)和軸空間。
- 第16行,如果我們想要有2個子圖,呈兩列(row)一欄(column)的排列,這時可以用
``plot.subplot(2, 1, 1)``
其中第3個參數,表示目前在第幾個子圖。這個函數一樣會傳回一個子圖物件。
```python=
import matplotlib
import matplotlib.pyplot as plot
import numpy
#正確顯示中文字
matplotlib.rcParams['font.sans-serif'] = 'microsoft jhenghei'
matplotlib.rcParams['font.family'] = 'sans-serif'
#資料
x = numpy.linspace(0, 2*numpy.pi, 24)
ysin = numpy.sin(x)
ycos = numpy.cos(x)
#函數方式
#pyplot-style
plot.subplot()
plot.plot(x, ysin, label="sin")
plot.plot(x, ycos, label="cos")
plot.xlabel("弧度")
plot.ylabel("值")
plot.title("sin 和 cos")
plot.legend()
#取得目前的 figure
fig = plot.gcf()
fig.canvas.set_window_title('By JohnAxer')
plot.show()
```
## 有哪些圖形呢?
- 折線圖 line
- 長條圖 bar
- 橫向長條圖 barh
- 直方圖 hist
- 盒狀圖 box
- 圓餅圖 pie
- 散布圖 scatter
- 密度圖 density
- 六邊箱圖 hexbin
- http://tuzhidian.com/chart?id=5c6662a7372bb033b9c2fa04
- 區域圖 area (折線圖+向下填滿)
- 核密度估計圖 kde
- Kernel Density Estimation
- https://datama.com.tw/20191112b1/
## 資料點和線的樣式
- 資料點的形狀,請參考以下的連結
- https://matplotlib.org/stable/api/markers_api.html
- 格式化字串
- fmt = '[marker][line][color]'
- https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html#matplotlib.pyplot.plot
- 顏色
- colormap 是一系列的漸層色,利用浮點數值(float)對應。
- 有那些命名的 colormap,請參考以下網址:
- https://matplotlib.org/stable/tutorials/colors/colormaps.html
## 解決中文顯示問題
- 一般來說,預設中文顯示會出現**豆腐**(就是亂碼),解決的方式也有兩種
- 第一式:程式法
- 下載想要的字型,下載後上傳到執行資料夾。
- https://www.google.com/get/noto/help/cjk
- https://briian.com/25754
```python=
import pandas
import matplotlib
import matplotlib.pyplot as plot
from matplotlib import font_manager
#字型要放在程式執行的資料夾
font_manager.fontManager.addfont('NotoSansTC-Regular.otf')
myFont = font_manager.FontProperties(fname="NotoSansTC-Regular.otf")
matplotlib.rcParams['font.sans-serif'] = myFont.get_name()
matplotlib.rcParams['font.family'] = myFont.get_family()
#看有哪些字體
font_set = {f.name for f in font_manager.fontManager.ttflist}
for f in font_set:
if "Noto" in f:
print(f)
```
- 如果在 windows 下,可以直接用微軟正黑體,這樣可以不用下載字型。
```python=
import matplotlib
matplotlib.rcParams['font.sans-serif'] = 'microsoft jhenghei'
matplotlib.rcParams['font.family'] = 'sans-serif'
```
- 第二式:系統設定法
- 透過設定 matplotlib 的設定檔,也可以正確顯示中文。
- 詳細的設定方法,請參考下面這篇文章的第2點。
- https://medium.com/marketingdatascience/解決python-3-matplotlib與seaborn視覺化套件中文顯示問題-f7b3773a889b
## 搭配 Pandas
- 使用 iris.csv
- https://gist.github.com/netj/8836201#file-iris-csv
- 資料集中有五項數據
- Sepal length: 花萼長度 (cm)
- Sepal width: 花萼寬度 (cm)
- Petal length: 花瓣長度 (cm)
- Petal width: 花瓣寬度 (cm)
- Variety: 鳶尾花的種類
- 資料集的前10筆
- 
- 示範程式
```python=
import matplotlib
import matplotlib.pyplot as plot
import pandas
import numpy
#正確顯示中文字
matplotlib.rcParams['font.sans-serif'] = 'microsoft jhenghei'
matplotlib.rcParams['font.family'] = 'sans-serif'
#讀 csv 進入 DataFrame
d = pandas.read_csv('iris.csv', encoding='utf-8')
#取得欄位 variety 的唯一值後轉成串列
#['Setosa', 'Versicolor', 'Virginica']
s = d['variety'].unique().tolist()
#依據欄位 variety 分組
df = d.groupby('variety')
# ax 傳回來的是一個 ndarray 一維陣列
fig, ax = plot.subplots(2, 1)
#第一個圖形
for key, value in enumerate(s):
x = df.get_group(value)['petal.length']
y = df.get_group(value)['petal.width']
ax[0].scatter(x, y, label=value)
ax[0].set_xlabel('petal.length')
ax[0].set_ylabel('petal.width')
ax[0].set_title("鳶尾花 花瓣長度與寬度")
ax[0].legend()
#第二個圖形
for key, value in enumerate(s):
x = df.get_group(value)['sepal.length']
y = df.get_group(value)['sepal.width']
ax[1].scatter(x, y, label=value)
ax[1].set_xlabel('sepal.length')
ax[1].set_ylabel('sepal.width')
ax[1].set_title("鳶尾花 花萼長度與寬度")
ax[1].legend()
#讓兩個圖形間距拉大
fig.tight_layout()
plot.show()
```
- 執行結果
- 
:::success
用 colormap 的方式產生圖形
:::
- 這種方式,所有資料列屬於同一資料類,所以,圖示(legend)無法自動產生,得用另一種方式。
- legend 產生方式(未完待續)。
```python=
import pandas
import matplotlib.pyplot as plot
d = pandas.read_csv('iris.csv', encoding='utf-8')
#計算後新增欄位
d['sepal.avg'] = d['sepal.length'] * d['sepal.width']
#篩選
condition = d['sepal.length'] > 5.5
print(d[ condition ].head(10))
#建立色彩對應,其中
# s = ['Setosa', 'Versicolor', 'Virginica']
# color = {'Setosa': 0, 'Versicolor': 1, 'Virginica': 2}
s = d['variety'].unique().tolist()
color = { s[i]: i for i in range(len(s)) }
#另一種寫法
#color = { value: key for key, value in enumerate(s) }
#在 DataFrame 新增一個欄位 color,其值依據欄位 variety 的值,帶入字典 color 得到。
d["color"] = d["variety"].apply(lambda key: color[key])
#繪圖
ax = d.plot(kind="scatter", x='sepal.length', y="sepal.width", s=30, c="color", colormap="jet", marker="*")
ax.legend()
#另一種畫法
d.plot.scatter(x='petal.length', y="petal.width", s=10, c='color', colormap="jet")
plot.show()
```
- 執行結果
