Matplotlib
資料視覺化¶到今天, matplotlib
幾乎是標準 Python 畫圖套件了! 在有 matplotlib
之前, Python 要畫圖不那麼方便, 和 Python 很多套件一樣, 有許多方案, 但各家有不同的優缺點, 也沒有一套是大家都在用的。
而 matplotlib
仿 Matlab 式的畫圖方式, 讓很多人很快入手、並且功能相當完整。原作者是 John D. Hunter, 和很多 Python 的套件作者一樣, 他有博士學位。非常令人遺憾的是他在 2012 年因大腸直腸癌治療併發症過逝, 過逝時才 44 歲!
他在過逝前不久, 還被邀請到 SciPy 研討會的 Keynote, 回去之後就被檢查出有大腸直腸癌。因此這部影片大概就是他最後一次的 Keynote, 時間是 2012 年 7 月 18 日 (他在同年 8 月 28 日過逝)。
from IPython.display import YouTubeVideo
YouTubeVideo('e3lTby5RI54')
我們之前使用過, 這裡只是複習。
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
先來設個 x 的範圍
x = np.linspace(0, 10, 200)
y = np.sin(5*x) / (1+x**2)
plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x1146965f8>]
參數 | 說明 |
---|---|
alpha |
透明度 |
color (c ) |
顏色 |
linestyle (ls ) |
線條風格 |
linewidth (lw ) |
線寬 |
plt.plot(x,y, c='r', lw=3)
[<matplotlib.lines.Line2D at 0x1146c7b00>]
c = 'r'
可以用 blue (b), green (g), red (r), cyan (c), magenta (m), yellow (y), black (k), white (w)
用一個 0 到 1 的數字表灰階, 越大越白。
c = '0.6'
網頁常用的標準 16 進位 RGB 表示法。
c = '#00a676'
我們怎知哪裡可選顏色呢? 可以用之前彥良介紹的 Coolors.co 等。
用 0-1 的數字表 RGB 也可以。
c=(0.7, 0.4, 1)
plt.plot(x,y, c = '#00a676', lw=3)
[<matplotlib.lines.Line2D at 0x1148d7860>]
所謂 'marker' 就是原來的點, 我們特別標示出來, 為了不要讓圖形太複雜, 我們新換個點比較少的例子。
x = range(20)
y = np.random.randn(20)
plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x11496c0b8>]
plt.plot(x, y, marker='o')
[<matplotlib.lines.Line2D at 0x114a36a90>]
參數 | 說明 |
---|---|
marker |
marker 的風格 |
markeredgecolor (mec ) |
邊線顏色 |
markeredgewidth (mew ) |
邊線寬度 |
markerfacecolor (mfc ) |
marker 的顏色 |
markerfacecoloralt (mfcalt ) |
marker 替換色 |
markersize (ms ) |
marker 大小 |
markevery |
隔多少畫一個 marker |
plt.plot(x, y, c='#6b8fb4', lw=5, marker='o', mfc='#fffa7c', mec="#084c61", mew=3, ms=20)
[<matplotlib.lines.Line2D at 0x114b493c8>]
比較奇特的參數是 markevery
, 這是說我們每多少筆資料要畫一個 marker。為了示範, 我們拿之前的例子來看看。
x = np.linspace(0, 10, 200)
y = np.sin(5*x) / (1+x**2)
plt.plot(x, y, lw=3, marker='o', ms=10)
[<matplotlib.lines.Line2D at 0x114c55f60>]
後面的完完全全連在一起了, 我們現在隔 10 個畫一個 marker 試試。
plt.plot(x, y, lw=3, marker='o', ms=10, markevery=10)
[<matplotlib.lines.Line2D at 0x114d63518>]
plt.bar(range(1,6), np.random.randint(1,30,5))
<Container object of 5 artists>
移一點點。
plt.bar(np.arange(0.6, 5), np.random.randint(1,30,5))
<Container object of 5 artists>
x = np.arange(1,6)
plt.bar(x - 0.4, [3, 10, 8, 12, 6], width=0.4, ec='none', fc='#e63946')
plt.bar(x, [6, 3, 12, 5, 8], width=0.4, ec='none', fc='#7fb069')
<Container object of 5 artists>
A = np.random.randint(2,15,5)
B = np.random.randint(2,15,5)
C = np.random.randint(2,15,5)
plt.bar(x, A, fc='#e63946', ec='none')
plt.bar(x, B, fc='#7fb069', ec='none', bottom = A)
plt.bar(x, C, fc='#e55934', ec='none', bottom = A+B)
<Container object of 5 artists>
x = np.arange(0.6, 6)
plt.barh(x, np.random.randint(1,15,6), fc='#e55934', ec='none')
<Container object of 6 artists>
x = np.arange(0.6,6)
A = np.random.randint(1,15,6)
B = np.random.randint(1,15,6)
plt.barh(x, A, fc='#e63946', ec='none')
plt.barh(x, -B, fc='#7fb069', ec='none')
<Container object of 6 artists>
subplot
畫多個圖¶我們每次畫圖的時候, matplotlib
就弄 1 個 figure 畫圖區出來, 裡面可以有很多子圖, 在 figure 裡叫 axes。目前我們都只有 1 個 figure 內含 1 張圖, 所以都不用設, 現在我想畫 4 張圖時。我們就要先想好「陣式」。
比如說 2x2 這樣排列的 4 張圖。
x = np.linspace(0, 10, 100)
plt.subplot(221)
plt.plot(x, np.sin(x), c='#e63946', lw=3)
plt.subplot(222)
plt.plot(x, np.sin(3*x), c='#7fb069', lw=3)
plt.subplot(223)
plt.scatter(x, np.random.randn(100), c='#daa73e', s=50, alpha=0.5)
plt.subplot(224)
plt.bar(range(10), np.random.randint(1,30,10), fc='#e55934')
<Container object of 10 artists>
x = np.linspace(-2*np.pi, 2*np.pi, 200)
y = np.sin(x)
plt.title("My lovely sin function")
plt.xlabel('x-axes')
plt.ylabel('values')
plt.plot(x, y, lw=3)
[<matplotlib.lines.Line2D at 0x11540b5f8>]
plt.xlim(-6,6)
plt.ylim(-1.2,1.2)
plt.plot(x,y,lw=3)
[<matplotlib.lines.Line2D at 0x1155330b8>]
xv = np.linspace(0, 2*np.pi, 100)
yv = np.sin(xv)
plt.plot(xv,yv,lw=3)
plt.xticks([0, np.pi/2, np.pi, 3*np.pi/2, 2*np.pi],
['$0$', '$\pi/2$', '$-\pi$', '$-3\pi/2$', '$-2\pi$']);
plt.xlim(-6,6)
plt.ylim(-1.2,1.2)
plt.plot(x, y, lw=3, label='$\sin$')
plt.plot(x, np.cos(x), lw=3, label='$\cos$')
plt.legend()
<matplotlib.legend.Legend at 0x115bf8a90>
plt.xlim(-6,6)
plt.ylim(-1.2,1.2)
plt.plot(x, y, lw=3, label='$\sin$')
plt.plot(x, np.cos(x), lw=3, label='$\cos$')
plt.legend(loc=4)
<matplotlib.legend.Legend at 0x115d55c18>
我們有時要設 axes 的背景啦等等的資訊。這時就要取得現在工作中的 axes。這一般有兩種方式, 第一種是設 subplot
時可以取得:
fig, ax = plt.subplot()
另一種是用 gca
函數:
ax = plt.gca()
ax = plt.gca()
ax.set_facecolor('#69b8bb')
ax.set_xlim(-6,6)
ax.set_ylim(-1.2,1.2)
plt.plot(x,y,lw=5,c='white')
[<matplotlib.lines.Line2D at 0x115cc77f0>]
ax = plt.gca()
ax.set_facecolor('#69b8bb')
ax.set_xlim(-6,6)
ax.set_ylim(-1.2,1.2)
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_position(('data',0))
ax.spines['left'].set_position(('data',0))
plt.plot(x,y,lw=5,c='white')
[<matplotlib.lines.Line2D at 0x115ee0e80>]
這裡用兩種方式去解決中文顯示問題。
你需要有字型完整路徑, 這很容易成功, 只是每次都要指定使用的字型。
import matplotlib.font_manager as fm
myfont = fm.FontProperties(fname="/Users/mac/Library/Fonts/NotoSansHant-Medium.otf")
x = np.linspace(-2*np.pi, 2*np.pi, 200)
y = np.sin(x)
plt.plot(x, y, lw=5)
plt.title("我可愛的 sin 函數", fontproperties=myfont, size=20)
<matplotlib.text.Text at 0x11605bf60>
再來我們可以用 matplotlib
的參數設定, rcParams
, 把字型完完全全用某個中文字型。
plt.rcParams['font.sans-serif'] = ['SimHei'] # 選個普通的黑體字
plt.rcParams['axes.unicode_minus']=False # 負號不出問題
plt.plot(x, y, lw=5)
plt.title("我可愛的 sin 函數", size=15) # 不用再設字型!
<matplotlib.text.Text at 0x115afa048>
其實參數式圖形基本上也是一堆 x, y 的座標, 只是 x(t), y(t) 都是 t 的函數, 我們可以想成 t 是時間。
比如說如果你還記得極座標這件事, 假設我們的
$$ -2\pi \leq t \leq 2\pi$$要畫個半徑是 r 的圓, x(t), y(t) 就是這樣:
我們來試試事情是不是如我們想的這樣...
r = 3
t = np.linspace(-2*np.pi, 2*np.pi, 200)
x = r*np.cos(t)
y = r*np.sin(t)
plt.plot(x, y, lw=3)
[<matplotlib.lines.Line2D at 0x11611e9b0>]
真的成功了... 等等, matplotlib
怎麼畫成橢圓呢? 我們來調整一下。
ax = plt.gca()
ax.set_aspect('equal')
plt.plot(x, y, lw=3)
[<matplotlib.lines.Line2D at 0x116128550>]
再來我們可以想一下, 如果 r 不是故定的呢? 也就是它會變長變短, 是不是會畫出很多特別的圖呢? 其實很多特別的曲線不過就是找個試合的 r(t), 我們來隨意試試看。
r = np.sin(5*t)
x = r*np.cos(t)
y = r*np.sin(t)
ax = plt.gca()
ax.set_aspect('equal')
plt.plot(x,y,lw=3)
[<matplotlib.lines.Line2D at 0x115dc1080>]
是不是很有意思呢? 你也可以自己試試不同的變化。
xkcd
¶with plt.xkcd():
x = np.linspace(-5, 5, 200)
y = np.sin(2*x) + 0.2*x
plt.plot(x,y)
其實我們如果大膽用
plt.xkcd()
之後畫圖完全就是 xkcd
風了。那為什為我們不這樣做呢? 因為你也許用到某個階段就想換回來, 不像上面的只影響一次。如果你真的直接用 plt.xkcd()
又想再換回原來風格, 可以再下
plt.rcdefaults()
嚴格說來, 剛剛我有點在騙你。原因是 Jupyter Notebook 有點調整 matplotlib
預設, 讓圖的大小比較適中。你要真的回到原先設定, 需要先存你的狀態, 用完 xkcd
風再回覆。所以在設 xkcd
時是這樣:
save_state = plt.rcParams.copy()
plt.xkcd()
然後就畫圖, 你會發現怎麼畫都是可愛 xkcd
風。再要回來時, 請把剛剛存的狀態讀回來,
plt.rcParams.update(save_state)
再畫圖就回覆原狀了!!
save_state = plt.rcParams.copy()
plt.xkcd()
<matplotlib.rc_context at 0x115d6e908>
plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x1162e0be0>]
plt.rcParams.update(save_state)
plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x1163ed8d0>]
對了, 為什麼叫 xkcd
呢? 其實是 Randall Munroe 畫的網路漫畫, 選個沒有什麼意思、沒有母音的字組出來的名字。Munroe 是唸物理出身, 曾在 NASA 工作過, 後來專心畫 xkcd, 還有出一本科普的書《如果這樣, 會怎樣?》非常奇特的科學問答書。
他畫的 xkcd 有一篇很常被 Python 社群引用:
原作品連結: http://xkcd.com/353/
seaborn
大救星¶雖然 matplotlib
幾乎什麼都可以調, 但有些簡單的調整卻要花費很多時間。於是有了許多可以協助 matplotlib
變美、變方便的套件出現。最近有個很有名的叫 seaborn
, 儼然有另一個標準套件的態式。
最重要的是 seaborn
和我們未來要介紹的 pandas
配合得非常好, 雖然熊貓和海生的聽來就沒什麼關係...
我們先來個標準 (又來標準縮寫) 讀入 seaborn
的方法。
import seaborn as sns
然後像平常畫個圖。
x = np.linspace(-10,10,200)
y = np.sinc(x)
plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x11a9ffa58>]