-
1 # 機器之心Pro
-
2 # 資料分析精選
如何高效地使用Matplotlib?
①英文還可以的話,閱讀官方文件,有著最詳細的各種圖表製作及引數設定資訊。
②遇到要製作具體圖表無從下手的話,中文百度上有很多人寫的教程。關鍵是要能使用恰當的關鍵詞才能搜尋到關聯度最高的答案。比如你想製作直方圖,直接搜直方圖是不行的,返回的資訊太雜太亂。而用“matplotlib,直方圖”作為關鍵詞基本上前幾條就是相關文章或教程。
④遇到出錯或圖表未按照預想的結果顯示,看出錯資訊的最後一行,並對照核對哪個引數設定不對。
⑤遇到自己不能解決的問題,可以去stack overflow提問或搜尋答案。
⑥多練習,慢慢一些常用命令就會爛熟於心了。
-
3 # 讀芯術
全文共2153字,預計學習時長4分鐘或更長
用Matplotlib模擬雨
動畫是呈現各種現象的有趣方式。在描述像過去幾年的股票價格、過去十年的氣候變化、季節性和趨勢等時間序列資料時,與靜態圖相比,動畫更能說明問題。因為,從動畫中,我們可以看到特定引數是如何隨時間而變化的。
上圖是模擬雨的影象。此圖由Matplotlib繪相簿繪製而成,該繪相簿常常被認為是python視覺化資料包的原始資料組。Matplotlib透過50個分散點的比例和不透明度來模擬雨滴落在平面上的情景。如今,Plotly、Bokeh、Altair等一系列視覺化工具均為Python中強大的視覺化工具。這些庫可實現最先進的動畫和互動動作。但是,本文重點在於研究資料庫的一個方面——動畫。同時,我們也將關注實現動畫的方法。
概述Matplotlib是一個 Python 的 2D繪相簿,也是Python中最受歡迎的繪圖資料庫。大多數人在踏上資料視覺化之旅時,都是首選Matplotlib。這是因為它可簡單地生成繪圖,直方圖、功率譜、條形圖、錯誤圖、散點圖等。不僅如此,它還無縫連線了Pandas、Seaborn等資料庫,甚至創建出更為複雜的視覺化資料。
Matplotlib有幾大優點:
· 其構造和MATLAB(矩陣實驗室)類似,所以兩者間易於切換
· 包含許多後端渲染
· 可以快速生成繪圖
· 已存在數十年以上,因此,有大量的使用者基礎
但是,Matplotlib除了這些優點之外,也有許多不足之處:
· Matplotlib常常不可避免地存在冗繁的API(應用程式程式設計介面)
· 有時預設樣式設計不如人意
· 對web和互動式圖表的支援較低
· 處理大型及複雜的資料時速度常常較慢
對於進修者來說,Datacamp中有關於Matplotlib的必備知識可以幫助提高基礎知識。
動畫Matplotlib的動畫基類負責處理動畫部分。其可提供構建動畫功能的框架。有兩個主要介面來實現此功能:
FuncAnimation:透過反覆觸發func.功能建立動畫。
ArtistAnimation:利用已定義的Artist物件建立動畫。
但是,在上述兩種介面中,FuncAnimation更為方便。我們專注於FuncAnimation工具的研究。
要求
· 安裝numpy和matplotlib模組。
· 安裝符合要求的 ffmpeg 或imagemagick方可將動畫以mp4或gif的形式儲存。
一切準備就緒,就可以開始在JupyterNotebooks中製作第一個基本動畫了。本文的訪問密碼可在GithubRepository中獲取。
基本動畫:移動的正弦波
在電腦中,利用FuncAnimation建立正弦波的基本動畫。動畫原始碼可在Matplotlib動畫教程中獲取。先來看看輸出程式碼,然後將其破譯,並瞭解其中奧妙。
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
plt.style.use("seaborn-pastel")
fig = plt.figure()
ax = plt.axes(xlim=(0, 4), ylim=(-2, 2))
line, = ax.plot([], [], lw=3)
def init():
line.set_data([], [])
return line,
def animate(i):
x = np.linspace(0, 4, 1000)
y = np.sin(2 * np.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
anim = FuncAnimation(fig, animate, init_func=init,
frames=200, interval=20, blit=True)
anim.save("sine_wave.gif", writer="imagemagick")
· 在第7行到第9行,簡單地建立一個圖形視窗,圖中只有一個軸。然後,建立無內容的行物件,其本質上是在動畫中可修改的物件。稍後用資料來填充行物件。
· 在第11行到13行,建立init函式,觸發動畫發生。此函式初始化資料,並限定軸範圍。
· 最後,在第14行到第18行,定義動畫函式,該函式以幀數(i)作為引數,並建立一個正弦波(或任意其他的動畫),而其移動取決於i的值。此函式返回一個已修改的plot物件的元組,告知動畫框架plot中哪些部分需要動畫化。
· 在第20 行,建立實際的動畫物件。Blit引數確保只重新繪製已更改的部分。
· 這是在Matplolib中建立動畫的基本知識。只需對程式碼稍作調整,就可以創建出一些有趣的視覺化。接下來看看其中一些視覺化的例子吧。
一個不斷擴大的線圈同樣,在GreeksforGreeks中,有一個建立圖形的好例子。我們一起在animation模組的幫助下創造一個緩慢展開的活動線圈。該程式碼和正弦波圖極為相似,只有一些小調整。
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
plt.style.use("dark_background")
fig = plt.figure()
ax = plt.axes(xlim=(-50, 50), ylim=(-50, 50))
line, = ax.plot([], [], lw=2)
# initialization function
def init():
# creating an empty plot/frame
line.set_data([], [])
return line,
# lists to store x and y axis points
xdata, ydata = [], []
# animation function
def animate(i):
# t is a parameter
t = 0.1*i
# x, y values to be plotted
x = t*np.sin(t)
y = t*np.cos(t)
# appending new points to x, y axes points list
xdata.append(x)
ydata.append(y)
line.set_data(xdata, ydata)
return line,
# setting a title for the plot
plt.title("Creating a growing coil with matplotlib!")
# hiding the axis details
plt.axis("off")
# call the animator
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=500, interval=20, blit=True)
# save the animation as mp4 video file
anim.save("coil.gif",writer="imagemagick")
實時更新圖繪製股票資料、感測器資料等其他與時間相關的動態資料時,實時更新圖就會派上用場。我們繪製一個基圖,在更多的資料被輸入系統後,基圖就會自動更新。現在,來繪製某假定公司某月內的股價圖。
#importing libraries
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
#creating a subplot
ax1 = fig.add_subplot(1,1,1)
def animate(i):
data = open("stock.txt","r").read()
lines = data.split("\n")
xs = []
ys = []
for line in lines:
x, y = line.split(",") # Delimiter is comma
xs.append(float(x))
ys.append(float(y))
ax1.clear()
ax1.plot(xs, ys)
plt.xlabel("Date")
plt.ylabel("Price")
plt.title("Live graph with matplotlib")
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
現在,開啟終端並執行python檔案,可以得到如下所示可自動更新的圖表:
其更新的時間間隔是1000毫秒或一秒。
3D圖中的動畫建立3D圖形十分常見,但是如果可以將這些圖形視角動畫化呢?其方法是,在改變相機檢視後,利用生成後的所有影象來建立動畫。而在PythonGraph Gallery(Python圖形庫)中有個專門的部分可以完成這類工作。
首先建立一個名為volcano的資料夾,放在與記事本相同的目錄中。然後,將所有會用於動畫化的圖形儲存在該資料夾中。
# library
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
# Get the data (csv file is hosted on the web)
url = "https://python-graph-gallery.com/wp-content/uploads/volcano.csv"
data = pd.read_csv(url)
# Transform it to a long format
df=data.unstack().reset_index()
df.columns=["X","Y","Z"]
# And transform the old column name in something numeric
df["X"]=pd.Categorical(df["X"])
df["X"]=df["X"].cat.codes
# We are going to do 20 plots, for 20 different angles
for angle in range(70,210,2):
# Make the plot
fig = plt.figure()
ax = fig.gca(projection="3d")
ax.plot_trisurf(df["Y"], df["X"], df["Z"], cmap=plt.cm.viridis, linewidth=0.2)
ax.view_init(30,angle)
filename="Volcano/Volcano_step"+str(angle)+".png"
plt.savefig(filename, dpi=96)
plt.gca()
這樣就可以在Volcano資料夾中建立多個PNG檔案。接著,利用ImageMagick(一個建立、編輯、合成圖片的軟體)將這些PNG檔案轉化成動畫。開啟終端並導向Volcano資料夾,輸入以下指令:
convert -delay 10 Volcano*
.pnganimated_volcano.gif
利用Celluloid模組動畫化Celluloid是python中的一個模組,其在matplotlib中可簡化建立動畫的程序。這個庫建立一個matplotlib圖並從中建立相機。然後,重新啟用該圖,並在建立每幀動畫後,用上述相機拍快照。最後,利用所有捕捉到的幀建立動畫。
安裝
pip install celluloid
下面是利用Celluloid模組的例子:
極小值
from matplotlib import pyplot as plt
from celluloid import Camera
fig = plt.figure()
camera = Camera(fig)
for i in range(10):
plt.plot([i] * 10)
camera.snap()
animation = camera.animate()
animation.save("celluloid_minimal.gif", writer = "imagemagick")
子圖
import numpy as np
from matplotlib import pyplot as plt
from celluloid import Camera
fig, axes = plt.subplots(2)
camera = Camera(fig)
t = np.linspace(0, 2 * np.pi, 128, endpoint=False)
for i in t:
axes[0].plot(t, np.sin(t + i), color="blue")
axes[1].plot(t, np.sin(t - i), color="blue")
camera.snap()
animation = camera.animate()
animation.save("celluloid_subplots.gif", writer = "imagemagick")
圖例
import matplotlib
from matplotlib import pyplot as plt
from celluloid import Camera
fig = plt.figure()
camera = Camera(fig)
for i in range(20):
t = plt.plot(range(i, i + 5))
plt.legend(t, [f"line {i}"])
camera.snap()
animation = camera.animate()
animation.save("celluloid_legends.gif", writer = "imagemagick")
我們一起分享AI學習與發展的乾貨
回覆列表
引言
對新手來說 Python 視覺化實在有些令人挫敗。有很多不同的選項,如何選擇正確的選項是一個挑戰。例如,兩年前這篇文章《Overview of Python Visualization Tools》(http://pbpython.com/visualization-tools-1.html)仍然吸引了大量讀者。在那篇文章中,我否定了 Matplotlib。但是,在使用過 pandas、scikit-learn、seaborn 和其他 Python 資料科學棧之後,我覺得之前否認 Matplotlib 的行為有點不成熟。坦白講,當時我不是很瞭解 Matplotlib,也不懂如何在我的工作流中高效使用 Matplotlib。
現在我學習了一些工具,瞭解瞭如何用 Matplotlib 使用這些工具,Matplotlib 逐漸變成了必需工具。本文將展示如何使用 Matplotlib。我堅定地認為 Matplotlib 是 Python 資料科學棧必不可少的一部分,希望這篇文章可以幫助大家瞭解如何使用 Matplotlib 進行 Python 視覺化。
為什麼大家都在否定 Matplotlib?
我認為,Matplotlib 對於新手來說比較難存在幾個原因。首先,Matplotlib 有兩個介面。第一個介面基於 MATLAB,使用基於狀態的介面。第二個介面是面向物件的介面。本文就不展開介紹 Matplotlib 有兩個介面的原因,但瞭解這兩種方法在使用 Matplotlib 繪圖時會很重要。兩個介面會引起混淆的原因是堆疊溢位和透過谷歌搜尋獲取的大量資訊,新使用者將發現問題的多個解決方案,但是這些問題看起來類似卻不完全相同。從我的個人經驗來講,從以前的程式碼中,我可以看出有一些 Matplotlib 程式碼的混雜,我對此感覺很疑惑(儘管寫它們的人是我……):-)
關鍵點
Matplotlib 新手應該學習和使用面向物件的介面。
使用 Matplotlib 的另一個歷史性挑戰是一些預設的樣式缺乏吸引力。在 R 使用 ggplot 就可以生成相當不錯的圖的世界中,Matplotlib 相對來說有點醜。好訊息是 Matplotlib 2.0 中的樣式好看了很多,你可以用最小的努力生成視覺化。
第三個挑戰是你不確定什麼時候該使用 Matplotlib,什麼時候該使用基於 Matplotlib 構建的工具,如 pandas 或 seaborn。大部分時候做一件事都有多種選擇,但是對於新手來說選擇正確的道路有些困難。這種混淆加上兩種不同 API 的混淆簡直就是挫敗本敗了……
為什麼使用 Matplotlib?
儘管 Matplotlib 有這麼多問題,我還是喜歡用它,因為它很強大。這個庫允許你建立幾乎所有視覺化。此外,圍繞 Matplotlib 有一個豐富的 Python 工具生態環境,很多更高階的視覺化工具使用 Matplotlib 作為基礎庫。如果你想在 Python 資料科學棧中進行任何操作,你需要對如何使用 Matplotlib 有一些基礎瞭解。這就是本文其餘部分的重點——提供一種高效使用 Matplotlib 的基礎方法。
前提
推薦以下步驟學習如何使用 Matplotlib:
1. 學習 Matplotlib 的基本術語,具體來說就是什麼是 Figure 和 Axes。
2. 一直使用面向物件的介面,養成習慣。
3. 用基礎的 pandas 繪圖開始視覺化。
4. 使用 seaborn 進行稍微複雜的資料視覺化。
5. 使用 Matplotlib 自定義 pandas 或 seaborn 視覺化。
下圖非常重要,有助於理解圖的不同術語。
大部分術語很直接易懂,需要牢記的是 Figure 是可能包含一或多個 axes 的最終影象。Axes 代表單個圖。一旦你理解這些是什麼以及如何透過面向物件的 API 評估它們,其餘步驟就很簡單了。
瞭解這個知識還有一個好處,就是當你在網路上看東西的時候有一個出發點。如果你花時間瞭解了這個點,那麼其他的 Matplotlib API 才有意義。此外,很多高階 Python 包,如 seaborn 和 ggplot 依賴於 Matplotlib 構建,因此理解了基礎,學習更強大的框架才更加容易。
最後,我不是說你應該逃避其他優秀選項,如 ggplot(又名 ggpy)、bokeh、plotly 或 altair。我只是認為你需要對 matplotlib + pandas + seaborn 有一個基礎的瞭解。瞭解基礎視覺化棧之後,你就可以探索其他優秀工具,根據需求做出合適的選擇。
開始
下面主要介紹如何在 pandas 中建立基礎的視覺化以及使用 Matplotlib 定製最常用的項。瞭解基礎流程有助於更直觀地進行自定義。
我主要關注最常見的繪圖任務,如標註軸、調整圖形界限(limit)、更新圖示題、儲存影象和調整圖例。
開始,我打算設定輸入,讀取一些資料:
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
df = pd.read_excel("https://github.com/chris1610/pbpython/blob/master/data/sample-salesv3.xlsx?raw=true")
df.head()
資料包括 2014 年的銷售交易額。為簡短起見,我將總結這些資料,列出前十名客戶的採購次數和交易額。繪圖時我將對各列進行重新命名。
top_10 = (df.groupby("name")["ext price", "quantity"].agg({"ext price": "sum", "quantity": "count"})
.sort_values(by="ext price", ascending=False))[:10].reset_index()
top_10.rename(columns={"name": "Name", "ext price": "Sales", "quantity": "Purchases"}, inplace=True)
下圖是資料。
現在資料在簡單的表格形式呈現,我們再來看一下如何將資料繪製成條形圖。如前所述,Matplotlib 具備多種不同風格,可用於渲染圖表。你可以使用 plt.style.available 檢視你的系統可用的風格。
plt.style.available
["seaborn-dark",
"seaborn-dark-palette",
"fivethirtyeight",
"seaborn-whitegrid",
"seaborn-darkgrid",
"seaborn",
"bmh",
"classic",
"seaborn-colorblind",
"seaborn-muted",
"seaborn-white",
"seaborn-talk",
"grayscale",
"dark_background",
"seaborn-deep",
"seaborn-bright",
"ggplot",
"seaborn-paper",
"seaborn-notebook",
"seaborn-poster",
"seaborn-ticks",
"seaborn-pastel"]
使用如下簡單風格:
plt.style.use("ggplot")
我鼓勵大家使用不同的風格,找到自己喜歡的。
現在我們有了好看的風格,第一步就是使用標準 pandas 繪圖函式繪製資料:
top_10.plot(kind="barh", y="Sales", x="Name")
推薦使用 pandas 繪圖的原因在於它是一種快速便捷地建立視覺化原型的方式。
自定義圖表
如果你對該圖表的重要部分都很滿意,那麼下一步就是對它執行自定義。一些自定義(如新增標題和標籤)可以使用 pandas plot 函式輕鬆搞定。但是,你可能會發現自己需要在某個時刻跳出來。這就是我推薦你養成以下習慣的原因:
fig, ax = plt.subplots()
top_10.plot(kind="barh", y="Sales", x="Name", ax=ax)
生成的圖表和原始圖表基本一樣,不過我們向 plt.subplots() 添加了一個額外的呼叫,並將 ax 傳輸至繪圖函式。為什麼要這麼做呢?還記得我說在 Matplotlib 中獲取軸和影象非常關鍵嗎?這裡所做的就是為了達到該目的。透過 ax 或 fig 物件可以執行任何自定義。
我們利用 pandas 實現快速繪圖,現在利用 Matplotlib 獲取所有功能。透過使用命名慣例,調整別人的解決方案適應自己的需求變得更加直接簡單了。
假設我們想調整 x 極限,改變一些軸標籤。現在我們在 ax 變數中有多個軸,可以進行一些操作:
fig, ax = plt.subplots()
top_10.plot(kind="barh", y="Sales", x="Name", ax=ax)
ax.set_xlim([-10000, 140000])
ax.set_xlabel("Total Revenue")
ax.set_ylabel("Customer");
這是另一種改變標題和標籤的簡單方式:
fig, ax = plt.subplots()
top_10.plot(kind="barh", y="Sales", x="Name", ax=ax)
ax.set_xlim([-10000, 140000])
ax.set(title="2014 Revenue", xlabel="Total Revenue", ylabel="Customer")
為了進一步展示該方法,我們還可以調整影象大小。使用 plt.subplots() 函式可以定義 figsize,以英寸為單位。我們還可以使用 ax.legend().set_visible(False) 移除圖例。
fig, ax = plt.subplots(figsize=(5, 6))
top_10.plot(kind="barh", y="Sales", x="Name", ax=ax)
ax.set_xlim([-10000, 140000])
ax.set(title="2014 Revenue", xlabel="Total Revenue")
ax.legend().set_visible(False)
要想修改這個影象,你可能需要執行很多操作。圖中最礙眼的可能是總收益額的格式。Matplotlib 可以使用 FuncFormatter 解決這一問題。該函式用途多樣,允許使用者定義的函式應用到值,並返回格式美觀的字串。
以下是貨幣格式化函式,用於處理數十萬美元區間的數值:
def currency(x, pos):
"The two args are the value and tick position"
if x >= 1000000:
return "${:1.1f}M".format(x*1e-6)
return "${:1.0f}K".format(x*1e-3)
現在我們有了格式化程式函式,就需要定義它,並將其應用到 x 軸。完整程式碼如下:
fig, ax = plt.subplots()
top_10.plot(kind="barh", y="Sales", x="Name", ax=ax)
ax.set_xlim([-10000, 140000])
ax.set(title="2014 Revenue", xlabel="Total Revenue", ylabel="Customer")
formatter = FuncFormatter(currency)
ax.xaxis.set_major_formatter(formatter)
ax.legend().set_visible(False)
這張圖美觀多了,非常好地展示了自定義問題解決方案的靈活性。最後要說的自定義特徵是向圖表添加註釋。你可以使用 ax.axvline() 畫垂直線,使用 ax.text() 新增自定義文字。就以上示例,我們可以畫一條表示平均值的線,包括代表 3 個新客戶的標籤。以下是完整程式碼:
# Create the figure and the axes
fig, ax = plt.subplots()
# Plot the data and get the averaged
top_10.plot(kind="barh", y="Sales", x="Name", ax=ax)
avg = top_10["Sales"].mean()
# Set limits and labels
ax.set_xlim([-10000, 140000])
ax.set(title="2014 Revenue", xlabel="Total Revenue", ylabel="Customer")
# Add a line for the average
ax.axvline(x=avg, color="b", label="Average", linestyle="--", linewidth=1)
# Annotate the new customers
for cust in [3, 5, 8]:
ax.text(115000, cust, "New Customer")
# Format the currency
formatter = FuncFormatter(currency)
ax.xaxis.set_major_formatter(formatter)
# Hide the legend
ax.legend().set_visible(False)
這可能不是最壯觀的圖,但它確實展示了使用該方法的力量。
圖表
目前,我們所做的所有改變都是針對單個圖表。我們還能夠在影象上新增多個表,使用不同的選項儲存整個影象。
如果我們確定要在同一個影象上放置兩個表,那麼我們應該對如何做有一個基礎瞭解。首先,建立影象,然後建立軸,再將它們繪製成圖表。使用 plt.subplots() 可以完成該操作:
fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(7, 4))
在這個例子中,我使用 nrows 和 ncols 指定大小,這對新使用者來說比較清晰易懂。
在示例程式碼中,你會經常看到變數如 1、2。我認為使用命名引數便於稍後檢視程式碼時理解程式碼。
我還使用 sharey=True 以使 y 軸共享相同的標籤。
該示例很靈活,因為不同的軸可以解壓成 ax0 和 ax1。現在我們有了這些軸,就可以像上述示例中那樣繪圖,然後把一個圖放在 ax0 上,另一個圖放在 ax1。
# Get the figure and the axes
fig, (ax0, ax1) = plt.subplots(nrows=1,ncols=2, sharey=True, figsize=(7, 4))
top_10.plot(kind="barh", y="Sales", x="Name", ax=ax0)
ax0.set_xlim([-10000, 140000])
ax0.set(title="Revenue", xlabel="Total Revenue", ylabel="Customers")
# Plot the average as a vertical line
avg = top_10["Sales"].mean()
ax0.axvline(x=avg, color="b", label="Average", linestyle="--", linewidth=1)
# Repeat for the unit plot
top_10.plot(kind="barh", y="Purchases", x="Name", ax=ax1)
avg = top_10["Purchases"].mean()
ax1.set(title="Units", xlabel="Total Units", ylabel="")
ax1.axvline(x=avg, color="b", label="Average", linestyle="--", linewidth=1)
# Title the figure
fig.suptitle("2014 Sales Analysis", fontsize=14, fontweight="bold");
# Hide the legends
ax1.legend().set_visible(False)
ax0.legend().set_visible(False)
現在,我已經在 jupyter notebook 中用 %matplotlib inline 展示了很多影象。但是,在很多情況下你需要以特定格式儲存影象,將其和其他呈現方式整合在一起。
Matplotlib 支援多種不同檔案儲存格式。你可以使用 fig.canvas.get_supported_filetypes() 檢視系統支援的檔案格式:
fig.canvas.get_supported_filetypes()
{"eps": "Encapsulated Postscript",
"jpeg": "Joint Photographic Experts Group",
"jpg": "Joint Photographic Experts Group",
"pdf": "Portable Document Format",
"pgf": "PGF code for LaTeX",
"png": "Portable Network Graphics",
"ps": "Postscript",
"raw": "Raw RGBA bitmap",
"rgba": "Raw RGBA bitmap",
"svg": "Scalable Vector Graphics",
"svgz": "Scalable Vector Graphics",
"tif": "Tagged Image File Format",
"tiff": "Tagged Image File Format"}
我們有 fig 物件,因此我們可以將影象儲存成多種格式:
fig.savefig("sales.png", transparent=False, dpi=80, bbox_inches="tight")
該版本將圖表儲存為不透明背景的 png 檔案。我還指定 dpi 和 bbox_inches="tight" 以最小化多餘空白。
結論
希望該方法可以幫助大家理解如何更有效地使用 Matplotlib 進行日常資料分析。