Matplotlib詳解

      在〈Matplotlib詳解〉中尚無留言

matplotlib 曾在資料分析裏的 Matplotlib 繪制圖表 介紹過。在此要對 matploblib 進行更詳細的說明。

安裝套件

本例需安裝套件

pip install matplotlib

使用 matplotlib 時,需 import 如下套件

 import matplotlib.pyplot as plt

但上面實在太長了,所以可以精簡為如下

 import pylab as plt

plot

plt.plot(x軸集合, y軸集合) 就可以繪制折線圖

若要儲存圖檔,使用 plt.savefig()函數

import pylab as plt
import numpy as np
plt.xlim(0,12)
plt.ylim(1,30)
x=np.linspace(1,12,12)
y=np.random.randint(10,30,12)
plt.plot(x, y)
plt.savefig("ling.jpg")
plt.show()

plt.plot 可以下達第三個參數 “ro”,此時就會變成點散圖。

 “r” : 紅色,”g” : 綠色,”y” :  黃色,”k” : 黑色。
“o” : 圓點,”s” : 四方型,”^” : 三角型。

第四個參數 markersize 為點散圖的大小。

import pylab as plt
import numpy as np
plt.xlim(0,12)
plt.ylim(1,30)
x=np.linspace(1,12,12)
y=np.random.randint(10,30,12)
plt.plot(x, y, 'ro', markersize=20)
plt.savefig("scatter.jpg")
plt.show()

繪制格點

底下的代碼是使用 plt.plot 練習格點繪制的範例

import pylab as plt
plt.xlim(-10,10)
plt.ylim(-10,10)
x=[-10, 10]
y=[0,0]
plt.plot(x, y, color="#ff0000", linewidth=0.5)
plt.plot([0,0],[-10, 10],color="#ff0000", linewidth=0.5)

for i in range(-10,11):
    plt.plot([i, i], [-10,10], color="#0000ff")
for i in range(-10,11):
    plt.plot([-10,10],[i, i], color="#0000ff")

plt.show()

scatter

plt.scatter(x軸集合, y軸集合, s大小, c顏色) 可以繪制點散圖,跟同上述的 plt.plot(“ro”) 差不多的效果,不過改變點散圖的大小為 s 參數,顏色為 c 參數

import pylab as plt
import numpy as np
plt.xlim(0,12)
plt.ylim(1,30)
x=np.linspace(1,12,12)
y=np.random.randint(10,30,12)
plt.scatter(x, y, s=150, c="#ff0000")
plt.show()

bar

繪制長條圖當然也是 matplotlib 的強項,繪圖指令為 plt.bar(x, height, width)

matplotlib 在顯示中文時會出現亂碼,需設定 plt.rcParams[‘font.sans-serif’]=[‘Microsoft JhengHei’]
width 若為1, 則二長條圖會緊緊相鄰,所以需設定在1之下,才會有間隔。
color 則必需傳入List格式。

import pylab as plt
import numpy as np
#解決中文碼無法顯示
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
x=np.linspace(1,12,12)
rates=np.random.randint(1,20,12)
colors=[]
for rate in rates:
    if rate<=5:colors.append('green')
    elif rate<=10:colors.append('yellow')
    else: colors.append('red')
plt.bar(x, rates, width=0.8, color=colors)
plt.title("產品不良率")
plt.xlabel('月份')
plt.ylabel('不良率')
plt.show()

plt.bar()為繪制垂直長條圖,plt.barh()則繪制水平長條圖

pie

plt.pie(數值集合, labe集合, autopct) 為繪制圓餅圖的函數,第一個參數為所有的數值資料,第二個參數為 label,第三個參數為顯示比例的格式。

import pylab as plt
labels=['China','Taiwan','Japan', 'Korea']
data=[2000,500,1300,1350]
plt.title('Covid-19')
plt.pie(data, labels=labels, autopct='%.2f%%')
plt.show()

plt.imshow

matplotlib 亦可以顯示圖片,本例使用 opencv 載入圖片,所以需先安裝 opencv-python 套件

pip install opencv-python

為什麼要使用 matploblib 顯示圖片呢? opencv 本身就可以顯圖了啊!! 這是因為 matplotlib 會依視窗大小自動進行縮放,而 opencv 則無法改變視窗大小。另外請注意,opencv 載入的圖檔為 BGR格式,所以需轉成 RGB格式再由 matplotlib 來顯示圖片。

底下也說明了去除 xy label 的值,有二種方式,分別為 xticks 及 axis(“off”)

import cv2
import pylab as plt
img=cv2.imread('tiger.jpg')
img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
#第一種去除 xy label
#plt.xticks([])
#plt.yticks([])
#第二種去除xy label
plt.axis("off")
plt.imshow(img) plt.show()

子繪圖區域

plt.plot 只能繪制一個圖。若有多個圖,可以使用 plt.subplot() 新增多個子繪圖區。subplot 的參數為 subplot(列, 行, 繪圖區),其中繪圖區由 1 開始。

import pylab as plt
import numpy as np
for i in range(3):
    ax=plt.subplot(1, 3, i+1)
    x=np.linspace(1,12,12)
    y=np.random.randint(1, 30, 12)
    ax.bar(x, y)
plt.savefig("subplot.jpg")
plt.show()

底下是使用子繪圖區域進行圖片顯示,子繪圖區去除label 的方法,是 set_xticklabels([])

import cv2
import pylab as plt
img=cv2.imread('tiger.jpg')
ax=plt.subplot(1,2,1)
ax.imshow(img)
#第一種去除xy值的方法
ax.set_xticklabels([])
ax.set_yticklabels([])

img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
ax=plt.subplot(1,2,2)
#第二種去除xy值的方法
ax.axis("off")
ax.imshow(img)
plt.show()

plt.subplots

subplot 是使用 1, 2, 3 來指定繪圖區域。而 subplots 則是一次建立多個繪圖區域,然後依陣列來指定要繪圖的區域。

如下代碼中,plt.subplots 指定列,行及圖形大小,傳回 fig 整張圖物件及 ax 子繪圖區域。

如果列為 1,則 ax 為一維陣列,需使用 ax[0],ax[1] 來指定繪圖區域。

如果列為 2 或以上,則 ax 為二維陣列,需使用 ax[0][0],ax[1][1] 來指定繪圖區域。

import cv2
import pylab as plt
img=cv2.imread('tiger.jpg')
fig, ax=plt.subplots(2,2, figsize=(12, 6))
ax[0][0].axis("off")
ax[0][0].imshow(img)

img=cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
ax[1][1].axis("off")
ax[1][1].imshow(img)
plt.show()

簡易動畫

在主程式裏,使用 animation.FuncAnimation 指定要更新的圖表(fig)及要調用的函數(run),第三個 frames參數為要傳給 run 函數 data 參數,可以為list格式。參數名稱不一定要用 “data”,此名稱可以自已訂。

當 list 傳遞完畢,會重頭再重傳,一直輪迴下去。如果 frames = 10,就等於 frames =range(10)。

interval 為每次停留多久的時間,單位為 ms。

import pylab as plt
from matplotlib import animation
import random
d=[(random.randint(1,10), random.randint(1,10)) for i in range(10)]
fig, ax=plt.subplots()
def run(data):
    ax.clear()
    ax.set_xlim(1,10)
    ax.set_ylim(1, 10)
    ax.scatter(data[0],data[1])
ani=animation.FuncAnimation(fig, run,frames=d, interval=500)
#ani.save('animation.gif', fps=10)
plt.show()

雨滴降落

底下代碼,不需傳值給 run, 但 run(data)  的 “data”參數,還是一定要寫frames = 50,也一定要寫,表示要儲存 50 幅圖片,其值不可以為 0。

import pylab as plt
import numpy as np
from matplotlib import animation
fig, ax=plt.subplots()
x = np.random.randint(1, 50, 50)
y = np.random.randint(40, 50, 50)
def run(data):
    for i in range(len(y)):
        if y[i]>0:
            y[i]-=1
    ax.clear()
    ax.set_xlim(0, 50)
    ax.set_ylim(0, 50)
    ax.scatter(x, y, s=80, c=x+y)
ani=animation.FuncAnimation(fig, runnable, interval=100)
#ani.save('animation_2.gif', frame=50, fps=10)
plt.show()

使用Thread

上述的作法,會一直循環下去而不會停止,所以若要在某個條件下停止動畫,就需使用 Threading。在每次繪制動畫時,都要使用 plt.draw() 進行更新。

請注意底下的代碼中,要使用 time.sleep(0.03),速度不可以太快,否則會因 Tkinter 來不及更新會造成例外錯誤。

import pylab as plt
import numpy as np
import threading
import time
fig, ax=plt.subplots()
x = np.random.randint(1, 50, 50)
y = np.random.randint(40, 50, 50)
def runnable():
    while y.sum()!=0:
        for i in range(len(y)):
            if y[i]>0:
                y[i]-=1
        ax.clear()
        ax.set_xlim(0, 50)
        ax.set_ylim(0, 50)
        ax.scatter(x, y, s=80, c=x+y)
        plt.draw()
        time.sleep(0.03)
t=threading.Thread(target=runnable)
t.start()
plt.show()

plot.pause

animation.FuncAnimation 有 bug, 自訂 threading 也有問題,所以最佳解為

import pylab as plt
import numpy as np
from celluloid import Camera

fig=plt.figure(figsize=(9,6))
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False
ax=plt.axes()
rng=np.random.RandomState(1)
xl=-15;xr=15;yb=-8;yt=12;n=20
x = np.linspace(-10, 10, n)
y = 0.5 * x + 3 - rng.randint(-5, 5, n)

learning_rate = 1e-3  # 學習率
a = 0
b = 0
c = 0
d = 0
e = 0
ax.set_xlim(xl, xr)
ax.set_ylim(yb, yt)

epoch = 400
# camera = Camera(fig)
for i in range(epoch):
    ax.clear()
    ax.plot([xl, xr], [0, 0], c='k', linewidth=0.5)
    ax.plot([0, 0], [yb, yt], c='k', linewidth=0.5)
    ax.plot(x, y, 'go')
    f = np.poly1d(np.polyfit(x, y, 4))
    ax.plot(x, f(x), c='b', linewidth=5)
    # 繪製自已算出來的迴歸線
    y_pred = a * x ** 4 + b * x ** 3 + c * x ** 2 + d * x + e
    da = ((y_pred - y) * x ** 4).sum()
    db = ((y_pred - y) * x ** 3).sum()
    dc = ((y_pred - y) * x ** 2).sum()
    dd = ((y_pred - y) * x ** 1).sum()
    de = ((y_pred - y) * x ** 0).sum()

    a = a - learning_rate * da / 1e6
    b = b - learning_rate * db / 1e4
    c = c - learning_rate * dc / 1e3
    d = d - learning_rate * dd / 1e1
    e = e - learning_rate * de / 1e0
    ax.plot(x, a * x ** 4 + b * x ** 3 + c * x ** 2 + d * x + e, c='r', linewidth=1)

    txt = f'逼近法迴歸函數 y={a:.7f}x^4+{b:.7f}x^3+{c:.7f}x^2+{d:.2f}x+{e:.2f}'
    ax.text(-10, -7, txt, color='red')

    print(i)
    plt.pause(0.001)
#     camera.snap()
#
# animation = camera.animate()
# animation.save('reg_ani.gif', writer='PillowWriter', fps=30)

todo

matplotlib 3D 長條圖

使用ax1.bar3d可繪製3D長條圖. 以下可畫出10條長條圖,  x, y, z為每個長條圖的座標. z 從平面開始, 所以z的10個值都是0, dx, dy, dz為每個長條圖的長, 寬, 高. 長寬各為1, 高則由1增加到10

import matplotlib.pyplot as plt
import numpy as np

# 底下雖未用到, 但一定要import, 否則無法使用 3d projection
from mpl_toolkits.mplot3d import Axes3D

from matplotlib import style
style.use('ggplot')

#fig = plt.figure()
#ax1 = fig.add_subplot(111, projection='3d')
ax1 = plt.subplot(111, projection='3d')
x3 = [1,2,3,4,5,6,7,8,9,10]
y3 = [5,6,7,8,2,5,6,3,7,2]
z3 = np.zeros(10)

dx = np.ones(10)
dy = np.ones(10)
dz = [1,2,3,4,5,6,7,8,9,10]

ax1.bar3d(x3, y3, z3, dx, dy, dz, shade=True)
ax1.set_xlabel('x axis')
ax1.set_ylabel('y axis')
ax1.set_zlabel('z axis')
plt.show()

matplotlib_3dbar

密度圖

底下使用 scipy  的高斯模糊化,

np.mgrid的用法,第一個產出值是將 x 軸 (列) 平均分配,然後每一行都跟第一行一樣。第二個產出值是將 y 軸 (欄) 平均分配,然後每一列都跟第一列一樣

np.vstack的用法 : 將二個陣列疊加起來

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import kde
# 建立資料, 200個點
#multivariate_normal(平均數, 協方差矩陣, size)
data = np.random.multivariate_normal([0, 0], [[1, 0.5], [0.5, 3]], 200)
x, y = data.T
# 建立畫布, 6個子圖
fig, axes = plt.subplots(ncols=3, nrows=2, figsize=(10, 10))
# 第一個子圖, 散點圖
axes[0][0].set_title('Scatterplot')
axes[0][0].plot(x, y, 'ko')
# 第二個子圖, 六邊形
nbins = 20
axes[0][1].set_title('Hexbin')
axes[0][1].hexbin(x, y, gridsize=nbins, cmap=plt.cm.BuGn_r)
# 2D 直方圖
axes[0][2].set_title('2D Histogram')
axes[0][2].hist2d(x, y, bins=nbins, cmap=plt.cm.BuGn_r)
# 高斯kde
k = kde.gaussian_kde(data.T)
xi, yi = np.mgrid[x.min():x.max():nbins * 1j, y.min():y.max():nbins * 1j]
zi = k(np.vstack([xi.flatten(), yi.flatten()]))
print(zi)
# 密度圖
axes[1][0].set_title('Calculate Gaussian KDE')
axes[1][0].pcolormesh(xi, yi, zi.reshape(xi.shape), shading='auto', cmap=plt.cm.BuGn_r)
# 新增陰影
axes[1][1].set_title('2D Density with shading')
axes[1][1].pcolormesh(xi, yi, zi.reshape(xi.shape), shading='gouraud', cmap=plt.cm.BuGn_r)
# 新增輪廓
axes[1][2].set_title('Contour')
axes[1][2].pcolormesh(xi, yi, zi.reshape(xi.shape), shading='gouraud', cmap=plt.cm.BuGn_r)
axes[1][2].contour(xi, yi, zi.reshape(xi.shape))
plt.savefig("density.jpg")
plt.show()

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *