Matplotlib

tags: Python Matplotlib

2022/01/11 by JohnAxer

基本觀念

  • 畫圖當然要先有畫布,這個畫布就是「圖」,一張圖可以再分為幾個子圖,每個子圖都可以有一到多個軸空間,每個軸空間可以有1到多個軸。
  • 一個圖至少有一個子圖,每個子圖至少有一個軸空間,軸空間必包含軸。
    • 圖 figure
    • 子圖 subplot
    • 軸空間 axes
    • 軸 axis

兩種寫法

  • 預計的執行結果

    • Image Not Showing Possible Reasons
      • The image was uploaded to a note which you don't have access to
      • The note which the image was originally uploaded to has been deleted
      Learn More →
  • 物件導向的寫法 OO-style

    • 注意到第16行,如果一個圖要包含多個子圖,可以這樣宣告。
      fig, ax = plot.subplots(2, 1)
    • 這樣就有兩個子圖,呈兩列(row)一欄(column)的排列。此時 ax 是一個 ndarray,裡面有兩個子圖 (AxesSubplot)。這個可以用
      print(ax) 和 print(type(ax))
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個參數,表示目前在第幾個子圖。這個函數一樣會傳回一個子圖物件。
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()

有哪些圖形呢?

資料點和線的樣式

解決中文顯示問題

  • 一般來說,預設中文顯示會出現豆腐(就是亂碼),解決的方式也有兩種

  • 第一式:程式法

    ​​​​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 下,可以直接用微軟正黑體,這樣可以不用下載字型。
    ​​​​import matplotlib ​​​​ ​​​​matplotlib.rcParams['font.sans-serif'] = 'microsoft jhenghei' ​​​​matplotlib.rcParams['font.family'] = 'sans-serif'
  • 第二式:系統設定法

搭配 Pandas

  • 使用 iris.csv

  • 資料集中有五項數據

    • Sepal length: 花萼長度 (cm)
    • Sepal width: 花萼寬度 (cm)
    • Petal length: 花瓣長度 (cm)
    • Petal width: 花瓣寬度 (cm)
    • Variety: 鳶尾花的種類
  • 資料集的前10筆

  • 示範程式

    ​​​​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()
  • 執行結果

用 colormap 的方式產生圖形

  • 這種方式,所有資料列屬於同一資料類,所以,圖示(legend)無法自動產生,得用另一種方式。
  • legend 產生方式(未完待續)。
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()
  • 執行結果