# Matplotlib ###### tags: `Python` `Matplotlib` #### 2022/01/11 by JohnAxer ## 基本觀念 - 畫圖當然要先有畫布,這個畫布就是「圖」,一張圖可以再分為幾個子圖,每個子圖都可以有一到多個軸空間,每個軸空間可以有1到多個軸。 - 一個圖至少有一個子圖,每個子圖至少有一個軸空間,軸空間必包含軸。 - 圖 figure - 子圖 subplot - 軸空間 axes - 軸 axis ## 兩種寫法 - 預計的執行結果 - ![](https://hackmd.io/_uploads/S159q1cht.png) - 物件導向的寫法 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筆 - ![](https://hackmd.io/_uploads/rJZyIlc3F.png) - 示範程式 ```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() ``` - 執行結果 - ![](https://hackmd.io/_uploads/Byk9Og9nF.png) :::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() ``` - 執行結果 ![](https://hackmd.io/_uploads/ryNys55hK.png)