# Matplotlib 繪圖技巧:四張小圖並列 > 作者:王一哲 > 日期:2019/7/22 <br /> ## 前言 以下是將在四張小圖畫在同一張圖中的作法,將圖排列為 $2 \times 2$ 的樣子,圖中的函數分別為 $$\mathrm{sigmoid:}~~~~~S(x) = \frac{1}{1+e^{-x}}$$ $$\mathrm{tanh:}~~~~~\tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}$$ $$\mathrm{ReLU:}~~~~~f(x) = \begin{cases} 0 & \mathrm{if~~~} x < 0 \\ x & \mathrm{otherwise} \end{cases}$$ $$\mathrm{Leaky~ReLU:}f(x) = \begin{cases} \lambda x & \mathrm{if~~~} x < 0 \\ x & \mathrm{otherwise} \end{cases}$$ <img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://imgur.com/UA0GDnf.png"><br /> ## 方法1:使用 subplot 切換繪圖位置 ```python= import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np plt.rc('text', usetex=True) plt.rc('font', **{'family' : 'sans-serif'}) plt.rc('legend', fontsize=18) params= {'text.latex.preamble' : [r'\usepackage{amsmath}']} mpl.rcParams.update(params) xmin, xmax, num = -6, 6, 200 x = np.linspace(xmin, xmax, num) sigmoid = 1 / (1 + np.exp(-x)) tanh = np.tanh(x) relu = x.copy() relu[np.where(relu < 0)] = 0 leaky = x.copy() leaky[np.where(leaky < 0)] = 0.2*x[np.where(x < 0)] plt.figure(figsize=(12, 10), dpi=72) plt.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=0.4, hspace=0.3) plt.subplot(221) plt.plot(x, sigmoid, color='blue', linestyle='-', linewidth=3, label=r'$\displaystyle S(x) = \frac{1}{1+e^{-x}}$') plt.xlabel('x', fontsize=16) plt.xlim(xmin, xmax) plt.tick_params(axis='both', labelsize=14) plt.title('Sigmoid', fontsize=20) plt.grid(True) plt.legend(handlelength=0, handletextpad=0, loc='upper left') plt.subplot(222) plt.plot(x, tanh, color = 'blue', linestyle = '-', linewidth = 3, label=r'$\displaystyle \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}$') plt.xlabel('x', fontsize = 16) plt.xlim(xmin, xmax) plt.tick_params(axis='both', labelsize=14) plt.title('tanh', fontsize=20) plt.grid(True) plt.legend(handlelength=0, handletextpad=0, loc='upper left') plt.subplot(223) plt.plot(x, relu, color='blue', linestyle='-', linewidth=3, label=r'$\displaystyle f(x) = \begin{cases} 0 & \mathrm{if~~~} x < 0 \\ x & \mathrm{otherwise} \end{cases}$') plt.xlabel('x', fontsize=16) plt.xlim(xmin, xmax) plt.tick_params(axis='both', labelsize=14) plt.title('ReLU', fontsize=20) plt.grid(True) plt.legend(handlelength=0, handletextpad=0, loc='upper left') plt.subplot(224) plt.plot(x, relu, color='blue', linestyle='-', linewidth=3, label=r'$\displaystyle f(x) = \begin{cases} 0.2x & \mathrm{if~~~} x < 0 \\ x & \mathrm{otherwise} \end{cases}$') plt.xlabel('x', fontsize=16) plt.xlim(xmin, xmax) plt.tick_params(axis='both', labelsize=14) plt.title('Leaky ReLU', fontsize=20) plt.grid(True) plt.legend(handlelength=0, handletextpad=0, loc='upper left') plt.savefig('ActivationFunctionMethod_1.svg') plt.savefig('ActivationFunctionMethod_1.png') plt.show() ``` <br /> 程式碼大約可以分為以下5個部分 1. 第1~3行:引入需要的函式庫 2. 第5~9行:設定繪圖參數 3. 第11~18行:產生繪圖資料 4. 第23~61行:繪圖 5. 第63~64行:儲存圖片並顯繪圖成果 除了第4個部分以外,其它部分請參考另外兩篇文章〈[Matplotlib 繪圖技巧:三張小圖並列](https://keejko.blogspot.com/2019/02/201924-matplotlib-matplotlib-httpshackmd.html)〉、〈[於 Matplotlib 繪製的圖表中插入 LATEX 數學式](https://keejko.blogspot.com/2019/07/matplotlib-latex.html)〉。第4個部分的重點如下: 1. 第20行,產生繪圖物件,寬度為12英吋 (inch),高度為10英吋,畫質為72 dpi (dots per inch)。 ```python plt.figure(figsize=(12, 10), dpi=72) ``` 2. 第21行,調整小圖的位置,避免圖片、標籤重疊。 ```python plt.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=0.4, hspace=0.3) ``` 3. 第23行,共有 $2 \times 2 = 4$ 張小圖,小圖的編號順序為左上、右上、左下、右下,因此現在是切換到左上角的小圖。 ```python plt.subplot(221) ``` 4. 第24、25行,繪製 sigmoid 函數,線條為藍色實線,寬度為3,標籤採用 $\LaTeX$ 語法顯示數學式子。 ```python plt.plot(x, sigmoid, color='blue', linestyle='-', linewidth=3, label=r'$\displaystyle S(x) = \frac{1}{1+e^{-x}}$') ``` 5. 第26行,設定橫軸標籤為x,字體大小為16。 ```python plt.xlabel('x', fontsize=16) ``` 6. 第27行,設定橫軸的繪圖範圍。 ```python plt.xlim(xmin, xmax) ``` 7. 第28行,同時設定橫軸、縱軸刻度格式。 ```python plt.tick_params(axis='both', labelsize=14) ``` 8. 第29行,設定圖表標題為Sigmoid,字體大小為20。 ```python plt.title('Sigmoid', fontsize=20) ``` 9. 第30行,繪製格線,舊版的語法選項為 **'on'**、**'off'**,現在改為 **True**、**False**。 ```python plt.grid(True) ``` 10. 第31行,加上圖例,由於我只想要顯示文字,所以將 **handlelength** 及 **handletextpad** 設定為 **0**;由於圖的左上角比較有空間,設定圖例顯示於圖的左上角。 ```python plt.legend(handlelength=0, handletextpad=0, loc='upper left') ``` 另外3張小圖的程式碼與第23~31行幾乎一樣,可以自行比較看看需要修改什麼地方。 <br /> ## 方法2:使用 plt.subplots ```python= import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np plt.rc('text', usetex=True) plt.rc('font', **{'family' : 'sans-serif'}) plt.rc('legend', fontsize=18) params= {'text.latex.preamble' : [r'\usepackage{amsmath}']} mpl.rcParams.update(params) xmin, xmax, num = -6, 6, 200 x = np.linspace(xmin, xmax, num) sigmoid = 1 / (1 + np.exp(-x)) tanh = np.tanh(x) relu = x.copy() relu[np.where(relu < 0)] = 0 leaky = x.copy() leaky[np.where(leaky < 0)] = 0.2*x[np.where(x < 0)] fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10), dpi=72) fig.subplots_adjust(left=0.1, bottom=0.1, right=0.9, top=0.9, wspace=0.4, hspace=0.3) ax1.plot(x, sigmoid, color='blue', linestyle='-', linewidth=3, label=r'$\displaystyle S(x) = \frac{1}{1+e^{-x}}$') ax1.set_xlabel('x', fontsize=16) ax1.set_xlim(xmin, xmax) ax1.tick_params(axis='both', labelsize=14) ax1.set_title('Sigmoid', fontsize=20) ax1.grid(True) ax1.legend(handlelength=0, handletextpad=0, loc='upper left') ax2.plot(x, tanh, color='blue', linestyle='-', linewidth=3, label=r'$\displaystyle \tanh(x) = \frac{e^x - e^{-x}}{e^x + e^{-x}}$') ax2.set_xlabel('x', fontsize=16) ax2.tick_params(axis='both', labelsize=14) ax2.set_title('tanh', fontsize=20) ax2.grid(True) ax2.legend(handlelength=0, handletextpad=0, loc='upper left') ax3.plot(x, relu, color='blue', linestyle='-', linewidth=3, label=r'$\displaystyle f(x) = \begin{cases} 0 & \mathrm{if~~~} x < 0 \\ x & \mathrm{otherwise} \end{cases}$') ax3.set_xlabel('x', fontsize=16) ax3.tick_params(axis='both', labelsize=14) ax3.set_title('ReLU', fontsize=20) ax3.grid(True) ax3.legend(handlelength=0, handletextpad=0, loc='upper left') ax4.plot(x, leaky, color='blue', linestyle='-', linewidth=3, label=r'$\displaystyle f(x) = \begin{cases} 0.2x & \mathrm{if~~~} x < 0 \\ x & \mathrm{otherwise} \end{cases}$') ax4.set_xlabel('x', fontsize=16) ax4.tick_params(axis='both', labelsize=14) ax4.set_title('Leaky ReLU', fontsize=20) ax4.grid(True) ax4.legend(handlelength=0, handletextpad=0, loc='upper left') fig.savefig('ActivationFunctionMethod_2.svg') fig.savefig('ActivationFunctionMethod_2.png') fig.show() ``` <br /> 方法2與方法1很像,最大的差異在於第20行,整個繪圖物件名稱為 fig,四張小圖分別為 ax1、ax2、ax3、ax4,一定要寫成 **((ax1, ax2), (ax3, ax4))**,否則小圖會無法對應到名稱。如果想讓小圖使用同樣的橫軸要加上 **sharex=True**,預設值為 False。 ```python fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(12, 10), dpi=72) ``` 由於四張小圖都有名稱,想要於指定小圖上繪圖時程式碼為 ```python [小圖名稱].plot ``` 不需要用 subplot 切換,個人偏好使用方法2。 <br /> ## 參考資料 1. https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.subplots.html 2. https://stackoverflow.com/questions/25123127/how-do-you-just-show-the-text-label-in-plot-legend-e-g-remove-a-labels-line --- ###### tags:`Python`