# 【Python 筆記】Matplotlib Pyplot 套件應用(中)
[TOC]
感謝您點進本篇文章,我是 LukeTseng,該系列主要以筆記加上口語白話形式學習 Python,若文章某處有誤敬請告知,非常感謝。
## 解決中文顯示問題
要直接使用系統內建的中文字體,加入以下這一行即可:
```python
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei'] # Windows 微軟正黑體
```
使用標楷體可將 `Microsoft JhengHei` 填入 `DFKai-sb`。
在 colab 上執行可能會出錯,而在 Jupyter Notebook 就沒什麼問題,因為 Jupyter Notebook 是在電腦本機上執行的,可以隨時抓到字體庫。
透過以下範例可試試看顯示結果是否正確:
```python=
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
x = [0, 1, 2, 3, 4, 5]
y = [3, 4, 5, 6, 7, 8]
plt.plot(x, y)
plt.title("我的圖表")
plt.xlabel("月份(月)")
plt.ylabel("營收(十萬)")
plt.grid(True, linestyle="-", alpha=0.5)
plt.xticks(x)
plt.yticks(range(1, 11))
plt.show()
```
Output:

### 解決中文負號問題
中文字體的負號可能會產生錯誤,如下圖(X 軸要顯示 -3、-2、-1 就出現方框):

在程式中加上這行就不會產生這樣的錯誤了:`plt.rcParams['axes.unicode_minus'] = False`
完整範例程式碼:
```python=
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False
x = [-3, -2, -1, 0, 1, 2]
y = [3, 4, 5, 6, 7, 8]
plt.plot(x, y)
plt.title("我的圖表")
plt.xlabel("月份(月)")
plt.ylabel("營收(十萬)")
plt.grid(True, linestyle="-", alpha=0.5)
plt.xticks(x)
plt.yticks(range(1, 11))
plt.show()
```
Output:

## 設定圖表區
圖表區主要由 `plt.figure()` 這個函式所建立,以下是他的語法:
```python
matplotlib.pyplot.figure(num=None, figsize=None, dpi=None,
facecolor=None, edgecolor=None,
frameon=True, clear=False, **kwargs)
```
* `num`:圖表區編號或名稱。可以給數字或字串,指定後如果已存在同編號的 figure 就會啟用它,不指定則自動產生新圖表區。
* `figsize`:圖表區尺寸,tuple 格式 (寬, 高),單位為英吋。如 `figsize=(8,4)` 創建一張 8 吋寬、4 吋高的圖表區。
* `dpi`:解析度,每英吋幾個像素。預設常見為 `100` 或 `80`,dpi 越高圖越清晰但檔案也越大。
* `facecolor`:圖表區的背景色,預設為白色。可用色名、16進位色碼等,例如 `facecolor='yellow'`。
* `edgecolor`:圖表區的邊框顏色。預設也是白色,可以設成你想要的顏色。
* `frameon`:是否顯示圖表區的邊框和背景顏色。布林值,預設為 `True`(顯示);設成 `False` 可關掉背景框。
* `clear`:若指定圖表區編號已存在,`clear=True` 會先清除其內容再使用(較少用,預設是 `False`)。
* `linewidth`:邊框線條粗細,預設為 0(幾乎看不到)。設大一點可以明顯看到外框。
範例:
```python=
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['DFKai-sb']
plt.rcParams['axes.unicode_minus'] = False
x = np.linspace(-np.pi, np.pi, 256)
y1 = np.sin(x)
y2 = np.cos(x)
# 創建第一個圖形
fig1 = plt.figure(num='first')
fig1.suptitle('第一個圖形')
plt.plot(x, y1)
# 創建第二個圖形
fig2 = plt.figure(num='second')
fig2.suptitle('第二個圖形')
plt.plot(x, y2)
# 切回第一個圖形繼續繪製
plt.figure(num='first') # 或用 plt.figure(num=1)
plt.plot(x, y2)
plt.show()
```
Output:

當中 `numpy.linspace()` 的語法如下:
```python
numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
```
* `start`:序列起始值(包含)。
* `stop`:序列結束值(預設包含,除非 `endpoint` 設為 `False`)。
* `num`:產生的數字個數,預設為 `50`。
* `endpoint`:布林值,預設為 `True`,表示包含 `stop` 值;若 `False` 則不包含。
* `retstep`:布林值,若為 `True` 會回傳(array, steps)。
* `dtype`:指定輸出陣列的資料型態。
numpy 的 `linspace()` 函式用來在指定區間 `[開始值, 結束值]` 內,生成指定數量的等差數列,最後會回傳一個 NumPy 陣列。
而當中的 `xx.suptitle()` 可為這些圖表設定標題。
### figsize
`plt.figure()` 的參數 `figsize` 可以調整圖的大小:
```python=
import matplotlib.pyplot as plt
# 創建較小的圖形(2英吋寬 x 1英吋高)
fig1 = plt.figure(figsize=(2, 1))
plt.plot([1, 2, 3, 4])
# 創建較大的圖形(8英吋寬 x 6英吋高)
fig2 = plt.figure(figsize=(8, 6))
plt.plot([1, 2, 3, 4])
plt.show()
```
Output:

### facecolor
`plt.figure()` 的 `facecolor` 參數可設定圖形的背景顏色,預設為白色。
```python=
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
# 設定黃色背景
fig = plt.figure(facecolor='yellow')
plt.plot(x)
plt.show()
```
Output:

### edgecolor
`plt.figure()` 的 `edgecolor` 參數可設定圖形邊框的顏色。
```python=
import matplotlib.pyplot as plt
# 設定紅色邊框、黃色背景、邊框寬度 5
fig = plt.figure(edgecolor='#f00', facecolor='#ff0', linewidth=5, figsize=(8, 3))
plt.plot([1, 2, 3, 4, 5])
plt.show()
```
Output:

### fig.savefig() 保存圖表
如 `fig.savefig('my_figure.png')` 可將先前用 `plt.figure()` 生成的 `figure` 物件像是 `fig`,將其圖表儲存。
### 小結
若有多個圖表的時候建議用 `num` 參數明確指定某個圖表,之後好方便去找、管理。
## 圖表區中加入多個圖表
首先介紹 `plt.subplot()`,這是拿來用在同一張圖表區(figure:或可以說是一個畫布)上創建多個子圖(subplot)的函數。
語法格式:
```python
plt.subplot(nrows, ncols, index)
# 或簡寫成三位數
plt.subplot(nci) # n=nrows, c=ncols, i=index
```
* `nrows`:子圖的列數(垂直方向有幾排)。
* `ncols`:子圖的行數(水平方向有幾個)。
* `index`:當前要操作的子圖位置編號,從 1 開始計數。
需特別注意同一張圖表區裡所有 `subplot()` 的 `nrows` 和 `ncols` 必須相同。
### 子圖編號規則
子圖的編號是由左到右、由上到下依序編號。以 2x2(2 列 2 行)的配置為例:
```
位置1 (1,1) 位置2 (1,2)
位置3 (2,1) 位置4 (2,2)
```
以下範例示範如何創建一個 1x2(1 列 2 行)子圖:
```python=
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['DFKai-sb']
plt.rcParams['axes.unicode_minus'] = False
# 創建圖表區
fig = plt.figure()
# 左邊的子圖
plt.subplot(1, 2, 1) # 1列2行,第1個位置
plt.plot([0, 6], [0, 100])
plt.title("plot 1")
# 右邊的子圖
plt.subplot(1, 2, 2) # 1列2行,第2個位置
x = [1, 2, 3, 4]
y = [1, 4, 9, 16]
plt.plot(x, y)
plt.title("plot 2")
plt.suptitle("我的圖表") # 整體標題
plt.show()
```
Output:

以下是 2x2(2 行 2 列)子圖的範例:
```python=
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['DFKai-sb']
plt.rcParams['axes.unicode_minus'] = False
fig = plt.figure(figsize=(10, 8))
# 第1個子圖(左上)
plt.subplot(2, 2, 1)
plt.plot([1, 2, 3, 4, 5])
plt.title("子圖 1")
# 第2個子圖(右上)
plt.subplot(2, 2, 2)
plt.plot([5, 4, 3, 2, 1])
plt.title("子圖 2")
# 第3個子圖(左下)
plt.subplot(2, 2, 3)
plt.plot([1, 3, 5, 7, 9])
plt.title("子圖 3")
# 第4個子圖(右下)
plt.subplot(2, 2, 4)
plt.plot([2, 4, 6, 8, 10])
plt.title("子圖 4")
plt.suptitle("2x2 子圖範例")
plt.tight_layout() # 自動調整間距
plt.show()
```
Output:

以下則是 2x1 的子圖,但使用簡寫方式撰寫:
```python=
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [5, 4, 3, 2, 1]
fig = plt.figure()
# 使用三位數簡寫
plt.subplot(221) # 等於 subplot(2, 2, 1)
plt.plot(x)
plt.subplot(224) # 等於 subplot(2, 2, 4)
plt.plot(y)
plt.show()
```
Output:

### 自動調整子圖間距
使用函式 `plt.tight_layout()`,可以自動調整子圖之間的間距,避免標題跟標籤重疊到,會很難看。
### plt.axes()
繼 `plt.subplot()` 後要介紹的是 `plt.axes()`,`plt.axes()` 用來在當前的圖表區中新增並創建一個 Axes(座標系統),並把它設為當前操作的座標系統。可以把 Axes 想像成是在圖表區上實際可以繪圖的區域,包含 x 軸和 y 軸以及所有的圖形元素。
[Figure 和 Axes - matplotlib 教學 ( Python ) | STEAM 教育學習網](https://steam.oxxostudio.tw/category/python/example/matplotlib-figure-axes.html) 有詳細圖文說明 Figure 跟 Axes 的關係,供參。
語法:
```python
plt.axes(arg=None, **kwargs)
```
arg 參數有三種不同的用法:
1. None(預設):創建一個填滿整個 `Figure` 的標準 `Axes`,相當於 `subplot(111)`。
2. 4-tuple 列表:`[left, bottom, width, height]`。
- 使用標準化坐標(範圍從 `0` 到 `1`)。
- `left`:Axes 左邊界距離 Figure 左邊的比例。
- `bottom`:Axes 下邊界距離 Figure 底部的比例。
- `width`:Axes 的寬度比例。
- `height`:Axes 的高度比例。
3. Axes 物件:直接傳入一個已存在的 `Axes` 物件。
範例 1:創建預設的全尺寸 Axes。
```python=
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['DFKai-sb']
plt.rcParams['axes.unicode_minus'] = False
# 創建填滿整個 Figure 的 Axes
plt.figure()
plt.axes() # 等同於 plt.axes(None)
plt.plot([1, 2, 3, 4], [1, 4, 9, 16])
plt.title("我的圖表")
plt.show()
```
Output:

範例 2:使用 4-tuple 自訂 Axes 位置和大小。
```python=
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['DFKai-sb']
plt.rcParams['axes.unicode_minus'] = False
fig = plt.figure(figsize=(8, 6))
# 創建一個位於左下角的小 Axes
# [left=0.1, bottom=0.1, width=0.3, height=0.3]
ax1 = plt.axes([0.1, 0.1, 0.3, 0.3])
ax1.plot([1, 2, 3], [1, 4, 9])
ax1.set_title("小圖(左下)")
# 創建一個較大的 Axes 位於右上
# [left=0.5, bottom=0.5, width=0.4, height=0.4]
ax2 = plt.axes([0.5, 0.5, 0.4, 0.4])
ax2.plot([1, 2, 3], [9, 4, 1], 'r')
ax2.set_title("中圖(右上)")
plt.show()
```
Output:

範例 3:創建子圖中的圖(圖中圖)。
```python=
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['DFKai-sb']
plt.rcParams['axes.unicode_minus'] = False
x = np.linspace(0, 10, 100)
y = np.sin(x)
fig = plt.figure(figsize=(10, 6))
# 主圖(填滿整個 Figure)
ax_main = plt.axes()
ax_main.plot(x, y, 'b-', linewidth=2)
ax_main.set_title("主圖:sin(x)")
ax_main.set_xlabel("x")
ax_main.set_ylabel("sin(x)")
# 嵌入的小圖(右上角)
ax_inset = plt.axes([0.6, 0.59, 0.25, 0.25])
ax_inset.plot(x, y**2, 'r-')
ax_inset.set_title("小圖:sin²(x)", fontsize=10)
plt.show()
```
Output:

範例 4:創建多個不規則排列的 Axes。
```python=
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['DFKai-sb']
plt.rcParams['axes.unicode_minus'] = False
fig = plt.figure(figsize=(10, 8))
# 左側大圖
ax1 = plt.axes([0.1, 0.1, 0.4, 0.8])
ax1.plot([1, 2, 3, 4], [1, 4, 2, 3])
ax1.set_title("左側主圖")
# 右上小圖
ax2 = plt.axes([0.6, 0.55, 0.3, 0.35])
ax2.plot([1, 2, 3], [3, 1, 2], 'g')
ax2.set_title("右上圖")
# 右下小圖
ax3 = plt.axes([0.6, 0.1, 0.3, 0.35])
ax3.plot([1, 2, 3], [2, 3, 1], 'r')
ax3.set_title("右下圖")
plt.show()
```
Output:

### plt.axes() vs plt.subplot()
`plt.axes()`:
- 可以自由指定位置和大小(使用標準化坐標)。
- 適合創建不規則排列或重疊的圖。
- Axes 可以位於任意位置,不限於網格內。
`plt.subplot()`:
- 將 Figure 劃分為規則的網格(m×n)。
- 適合創建整齊排列的多子圖。
- 所有子圖大小統一、不重疊。
## 總結
### 中文字體設定
使用系統字體讓 Matplotlib 正確顯示中文:
```python
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei'] # Windows 微軟正黑體
```
標楷體(DFKai-sb)。
中文負號錯誤修正程式碼:
```python
plt.rcParams['axes.unicode_minus'] = False
```
### 圖表區設定
語法:
```python
plt.figure(num=None, figsize=None, dpi=None,
facecolor=None, edgecolor=None,
frameon=True, clear=False, **kwargs)
```
| 參數 | 說明 |
| ----------- | --------------------- |
| `num` | 圖表區編號或名稱(便於管理多圖) |
| `figsize` | 圖尺寸(單位:英吋),如 `(8, 4)` |
| `dpi` | 解析度,數值越高圖越清晰 |
| `facecolor` | 圖表區背景色 |
| `edgecolor` | 邊框顏色 |
| `frameon` | 是否顯示背景邊框 |
| `clear` | 若圖表區已存在是否清除內容 |
| `linewidth` | 邊框粗細 |
### 圖表區內建立多子圖
```python
plt.subplot(nrows, ncols, index)
```
或簡寫為三位數:`plt.subplot(221)`。
子圖排列規則:
由上到下、由左到右編號。
例如 2×2 共有四張子圖:
```
(1,1) (1,2)
(2,1) (2,2)
```
### 建立自由座標區
- 用來在圖表區上手動指定繪圖區位置與大小。
- 可創建重疊、不規則或嵌入式(圖中圖)的小圖。
```python
plt.axes([left, bottom, width, height])
```
各參數範圍為 `0–1`,代表相對於整張圖表區的比例。
### plt.axes() vs plt.subplot()
| | `plt.subplot()` | `plt.axes()` |
| ---- | --------------- | ------------ |
| 配置方式 | 規則網格(m×n) | 自由指定位置大小 |
| 適用情境 | 整齊排列的多子圖 | 不規則或重疊圖形 |
| 彈性 | 固定尺寸、等間距 | 高度客製化 |
| 編號方式 | 由上到下、左到右 | 用座標指定 |
## 參考資料
[Figure 參數設定 - matplotlib 教學 ( Python ) | STEAM 教育學習網](https://steam.oxxostudio.tw/category/python/example/matplotlib-figure.html)
[matplotlib.pyplot.figure() in Python - GeeksforGeeks](https://www.geeksforgeeks.org/python/matplotlib-pyplot-figure-in-python/)\
[建立多個子圖表 ( subplot、subplots ) - matplotlib 教學 ( Python ) | STEAM 教育學習網](https://steam.oxxostudio.tw/category/python/example/matplotlib-subplot.html)
[Matplotlib Subplot | W3Schools](https://www.w3schools.com/python/matplotlib_subplot.asp)
[Matplotlib.pyplot.axes() in Python - GeeksforGeeks](https://www.geeksforgeeks.org/python/matplotlib-pyplot-axes-in-python/)
[Figure 和 Axes - matplotlib 教學 ( Python ) | STEAM 教育學習網](https://steam.oxxostudio.tw/category/python/example/matplotlib-figure-axes.html)