繪圖和可視化
matplotlib API入門
import matplotlib.pyplot as plt
圖和子圖
# matplotlib的圖像位于Figure對象中。你可以用plt.figure創(chuàng)建一張新圖
# plt.figure有一些選項,特別是figsize,用于當(dāng)圖片保存到磁盤時,確保圖片具有一定的大小和寬高比
fig = plt.figure()
# 不能在空Figure對象上繪圖。必須用add_subplot創(chuàng)建一個或多個subplot才能真正繪圖:
# 下面代碼指定,圖像應(yīng)該是2×2的(即最多4張圖),且當(dāng)前選中的是4張子圖中的第1張(編號從1開始
ax1 = fig.add_subplot(2,2,1)
# 再創(chuàng)建兩張子圖
ax2 = fig.add_subplot(2,2,2)
ax3 = fig.add_subplot(2,2,3)
<img src="https://huihuiteresa.github.io/image/youdao/20250512001.png" width="400">
# 使用plot方法繪制線形圖
ax3.plot(np.random.standard_normal(50).cumsum(),color="black",linestyle="dashed")
ax1.hist(np.random.standard_normal(100),bins=20,color="black",alpha=0.3)
ax2.scatter(np.arange(30),np.arange(30)+3*np.random.standard_normal(30))
<img src="https://huihuiteresa.github.io/image/youdao/20250512002.png" width="400">
# matplotlib提供了一個更便捷的方法plt.subplots,它可以創(chuàng)建一個新的圖形對象,并返回一個NumPy數(shù)組,其中包含創(chuàng)建好的子圖對象
# axes[0,1]引用的是位于第1行、第2列的子圖。你還可以通過sharex和sharey指定子圖應(yīng)該具有相同的x軸或y軸。
fig,axes = plt.subplots(2,3)
matplotlib.pyplot.subplots的選項:
- nrows:子圖的行數(shù)
- ncols:子圖的列數(shù)
- sharex:所有子圖應(yīng)該使用相同的x軸刻度(調(diào)整xlim將會影響所有子圖)
- sharey:所有子圖應(yīng)該使用相同的y軸刻度(調(diào)整ylim將會影響所有子圖)
- subplot_kw:傳入add_subplot的關(guān)鍵字參數(shù)字典,用于創(chuàng)建子圖
- **fig kw:創(chuàng)建圖形時,subplots的額外關(guān)鍵字參數(shù),比如plt.subplots(2,2,figsize=(8, 6))
調(diào)整子圖周圍的間距
利用Figure對象的subplots_adjust方法可以便捷地調(diào)整間距:
fig,axes = plt.subplots(2,2,sharex=True,sharey=True)
for i in range(2):
for j in range(2):
axes[i,j].hist(np.random.standard_normal(500),bins=50,color="black",alpha=0.5)
fig.subplots_adjust(wspace=0,hspace=0) # 各子圖之間間距為零
<img src="https://huihuiteresa.github.io/image/youdao/20250520001.png" width="400">
不難看出,其中的軸標(biāo)簽重疊了。matplotlib不會檢查標(biāo)簽是否重疊,所以對于這種情況,你只能自己設(shè)定刻度位置和刻度標(biāo)簽。
顏色、標(biāo)記和線型
matplotlib的線型plot函數(shù)接收一組x和y坐標(biāo),還可以接收一個表示顏色和線型的參數(shù)。
# 要根據(jù)x和y繪制綠色虛線
ax.plot(x, y, linestyle="--", color="green")
# 將標(biāo)記放到額外的格式選項中
ax = fig.add_subplot()
ax.plot(np.random.standard_normal(30).cumsum(),color="black",linestyle="dashed",marker="o")
在線形圖中,你會注意到插值數(shù)據(jù)點默認(rèn)是按線性方式插入的??梢酝ㄟ^drawstyle選項修改:
fig = plt.figure()
ax = fig.add_subplot()
data = np.random.standard_normal(30).cumsum()
ax.plot(data,color="black",linestyle="dashed",label="Default")
ax.plot(data,color="black",linestyle="dashed", drawstyle="steps-post",label="steps-post")
<img src="https://huihuiteresa.github.io/image/youdao/20250520002.png" width="400">
由于我們傳遞了label參數(shù)給plot,因此可以使用ax.legend創(chuàng)建一個圖例來區(qū)分每條線。
刻度、標(biāo)簽和圖例
對于大多數(shù)的圖表裝飾項,可以通過matplotlib軸對象的方法訪問。主要方法有xlim、xticks和xticklabels,它們分別控制圖像范圍、刻度位置和刻度標(biāo)簽。
- 調(diào)用時不帶參數(shù),則返回當(dāng)前的參數(shù)值(例如,ax.xlim()返回當(dāng)前的x軸繪圖范圍)。
- 調(diào)用時帶參數(shù),則設(shè)置參數(shù)值(例如,ax.xlim([0,10])會將x軸的范圍設(shè)置為0~10)。
設(shè)置標(biāo)題、軸標(biāo)簽、刻度以及刻度標(biāo)簽
fig,ax = plt.subplots()
ax.plot(np.random.standard_normal(1000).cumsum())
# 要修改x軸刻度,最簡單的辦法是使用set_xticks和set_xticklabels。
# y軸的修改方式與此類似,只需將上述代碼中的x替換為y即可
ticks = ax.set_xticks([0,250,500,750,1000])
labels = ax.set_xticklabels(["one","two","three","four","five"],rotation=30,fontsize=8)
# set_xlabel為x軸設(shè)置一個名稱
ax.set_xlabel("stages")
# 軸的類有set方法,可以批量設(shè)置繪圖選項
ax.set(title="My first matplotlib plot",xlabel="stages")
<img src="https://huihuiteresa.github.io/image/youdao/20250520003.png" width="400">
添加圖例
圖例是另一種用于標(biāo)識圖表元素的重要內(nèi)容。
要從圖例中去除一個或多個元素,可以不傳入label或傳入label="nolegend"。
fig,ax = plt.subplots()
# 在添加圖表各組件時傳入label參數(shù)
ax.plot(np.random.random(1000).cumsum(),color="black",label="one")
ax.plot(np.random.random(1000).cumsum(),color="black",linestyle="dashed",label="two")
# 調(diào)用ax.legend()來自動創(chuàng)建圖例
ax.legend()
<img src="https://huihuiteresa.github.io/image/youdao/20250520004.png" width="400">
注釋和繪制子圖
除了標(biāo)準(zhǔn)的繪圖類型,你可能還希望繪制一些繪圖注釋,注釋可能包含文本、箭頭或其他圖形。注釋和文字可以通過text、arrow和annotate函數(shù)進行添加。
# text可以將文本繪制在圖表的指定坐標(biāo)(x,y),還可以加上一些自定義格式
ax.text(x,y,"Hello world",family="monospace",fontsize=10)
根據(jù)2007年以來的標(biāo)準(zhǔn)普爾500指數(shù)的收盤價繪圖(數(shù)據(jù)來自Yahoo!Finance),并標(biāo)出2008—2009年金融危機期間的若干重要日期:
from datetime import datetime
fig,ax = plt.subplots()
data = pd.read_csv("examples/spx.csv",index_col=0,parse_dates=True)
spx = data["SPX"]
spx.plot(ax=ax,color="black")
crisis_data=[
(datetime(2007,10,11),"Peak of bull market"),
(datetime(2008,3,12),"Bear Stearns Fails"),
(datetime(2008,9,15),"Lehman Bankruptcy")
]
for date,label in crisis_data:
# 在指定的x和y坐標(biāo)軸繪制標(biāo)簽
ax.annotate(label,xy=(date,spx.asof(date)+75),
xytext=(date,spx.asof(date)+255),
arrowprops=dict(facecolor="black",headwidth=4,width=2,headlength=4),
horizontalalignment="left",verticalalignment="top")
# 放大2007-2010 年(手動設(shè)定起始邊界和結(jié)束邊界,而不使用matplotlib的默認(rèn)設(shè)置)
ax.set_xlim(["1/1/2007","1/1/2011"])
ax.set_ylim([600,1800])
# 添加圖標(biāo)標(biāo)題
ax.set_title("Important dates in the 2008-2009 financial crisis")
圖形的繪制要麻煩一些。matplotlib有一些表示常見形狀的對象,稱為補丁(patch)。其中一些(如Rectangle和Circle)可以在matplotlib.pyplot中找到,但完整集合位于matplotlib.patches。要在圖表中添加一個圖形,你需要創(chuàng)建一個補丁對象,然后將其傳遞給ax.add_patch,就能添加到子圖ax中了:
fig,ax = plt.subplots()
rect = plt.Rectangle((0.2,0.75),0.4,0.15,color="black",alpha=0.3)
circ = plt.Circle((0.7,0.2),0.15,color="blue",alpha=0.3)
pgon = plt.Polygon([[0.15,0.15],[0.35,0.4],[0.2,0.6]],color="green",alpha=0.5)
ax.add_patch(rect)
ax.add_patch(circ)
ax.add_patch(pgon)
<img src="https://huihuiteresa.github.io/image/youdao/20250521001.png" width="400">
如果查看常見圖表的具體實現(xiàn)代碼,你就會發(fā)現(xiàn)它們其實就是由補丁組裝而成的。
將圖表保存到文件
# 利用圖表對象的savefig實例方法,可以將當(dāng)前圖表保存到文件中
fig.savefig("figpath.svg")
# 文件類型是通過文件擴展名推斷出來的。因此,如果你使用的是.pdf,就會得到一個PDF文件。
# 我在發(fā)布圖片時最常用到的重要選項是dpi,用于控制“每英寸點數(shù)”的分辨率
fig.savefig("figpath.svg",dpi=400)
fig.savefig的一些選項:
- fname:含有文件路徑的字符串或Python 的文件型對象。圖像格式由文件擴展名推斷得出(例如,由·pdf 推斷出 PDF,或由·png 推斷出 PNG)
- dpi:圖像分辨率(每英寸點數(shù));IPython中默認(rèn)為100,Jupyter 中默認(rèn)為72,可以修改
- facecolo、edgecolor:位于子圖外的圖像背景色,默認(rèn)為"w"(白色)
- format:明確指定文件格式(比如"png"、"pdf"、"svg"、"ps"、"eps"等)
matplotlib配置
matplotlib自帶一些配色方案,以及為生成出版質(zhì)量的圖片而設(shè)定的默認(rèn)配置信息。幸運的是,幾乎所有默認(rèn)配置都能通過一組全局參數(shù)進行自定義,它們可以管理圖像大小、子圖邊距、配色方案、字體大小、網(wǎng)格類型等。使用rc方法是通過Python編程來修改配置的途徑之一。
所有當(dāng)前的配置設(shè)置存儲于字典plt.rcParams,調(diào)用plt.rcdefaults()函數(shù)即可恢復(fù)默認(rèn)值。
rc的第一個參數(shù)是想要自定義的組件,比如"figure"、"axes"、"xtick"、"ytick"、"grid"、"legend"等。
# 將全局的圖像默認(rèn)大小設(shè)置為10×10
plt.rc("figure",figsize=(10,10))
# 恢復(fù)默認(rèn)值
plt.rcdefaults()
# 設(shè)置字體
plt.rc("font",family="monospace",weight="bold",size=8)
使用pandas和seaborn繪圖
matplotlib實際上是比較底層的工具。要繪制一張圖表,將matplotlib的基本組件組裝起來就行:數(shù)據(jù)展示(即圖表類型:線形圖、柱狀圖、箱型圖、散點圖、等高線圖等)、圖例、標(biāo)題、刻度標(biāo)簽以及其他注釋信息。
在pandas中,我們可能有多列數(shù)據(jù),還有行標(biāo)簽和列標(biāo)簽。pandas自身就有內(nèi)置的方法,用于簡化從DataFrame和Series繪制圖形。另一個庫是seaborn(https://seaborn.pydata.org)——一個基于matplotlib的高級統(tǒng)計繪圖庫。seaborn簡化了許多常見圖表類型的創(chuàng)建過程。
線形圖
Series和DataFrame都有一個plot屬性,用于繪制基本圖表。
s = pd.Series(np.random.standard_normal(10).cumsum(),index=np.arange(0,100,10))
# 默認(rèn)情況下,plot()生成的是線形圖
s.plot()
<img src="https://huihuiteresa.github.io/image/youdao/20250521002.png" width="400">
Series.plot方法的參數(shù):
- label:用于圖例的標(biāo)簽
- ax:繪圖所用的 matplotlib子圖對象。如果沒有設(shè)置,則使用當(dāng)前的matplotlib 子圖
- style:傳遞給 matplotlib 的樣式字符串(如"ko--")
- alpha:圖表的填充不透明度 (0到1之間)
- kind:可以是"area""bar" "barh" "density""hist""kde" "line""pie"其中之一。默認(rèn)為"line"
- figsize:將要創(chuàng)建的圖像的大小
- logx:如果傳入True,則x軸使用對數(shù)縮放;如果傳入"sym",則使用對稱對數(shù),可以顯示負(fù)值
- logy:如果傳入True,則y軸使用對數(shù)縮放;如果傳入"sym",則使用對稱對數(shù),可以顯示負(fù)值
- title:圖的標(biāo)題
- use_index:將對象的索引用作刻度標(biāo)簽
- rot:旋轉(zhuǎn)刻度標(biāo)簽 (0到360度)
- xticks:用作x 軸刻度的值
- yticks:用作y軸刻度的值
- xlimx:軸的界限(例如[0,10])
- ylim:y軸的界限
- grid:顯示軸網(wǎng)格線 (默認(rèn)關(guān)閉)
df = pd.DataFrame(np.random.standard_normal((10,4)).cumsum(0),
columns=["A","B","C","D"],
index=np.arange(0,100,10))
# 將配色方案改為灰度
plt.style.use('grayscale')
# DataFrame的plot方法將各列繪制成同一子圖中不同的線,并自動創(chuàng)建圖例
df.plot()
<img src="https://huihuiteresa.github.io/image/youdao/20250521003.png" width="400">
專屬于DataFrame的plot參數(shù):
- subplots:將 DataFrame 的列繪入不同的子圖
- layouts:用于子圖布局的二元組 (行,列)
- sharex:如果 subplots=True,則共享x軸,關(guān)聯(lián)刻度和界限
- sharey:如果 subplots=True,則共享y軸,關(guān)聯(lián)刻度和界限
- legend:添加子圖圖例 (默認(rèn)為True)
- sort_columns:以字母順序繪制列;默認(rèn)使用列已有的順序
柱狀圖
plot.bar()和plot.barh()分別用于繪制水平柱狀圖和垂直柱狀圖。對于柱狀圖,Series或DataFrame的索引將用作x軸(bar)或y軸(barh)的刻度:
fig,axes = plt.subplots(2,1)
data = pd.Series(np.random.uniform(size=16),index=list("abcdefghijklmnop"))
data.plot.bar(ax=axes[0],color="black",alpha=0.7)
data.plot.barh(ax=axes[1],color="black",alpha=0.7)
<img src="https://huihuiteresa.github.io/image/youdao/20250521004.png" width="400">
對于DataFrame,柱狀圖會將每一行的值分為一組,并排顯示:
df = pd.DataFrame(np.random.uniform(size=(6,4)),
index=["one","two","three","four","five","six"],
columns=pd.Index(["A","B","C","D"],name="Genus"))
df.plot.bar()
<img src="https://huihuiteresa.github.io/image/youdao/20250521005.png" width="400">
# 傳入stacked=True,即可為DataFrame生成堆積柱狀圖,這樣每行的值就會水平堆積在一起
df.plot.barh(stacked=True,alpha=0.5)
# 使用value_counts對Series的各值出現(xiàn)頻率進行可視化非常有效,比如s.value_counts().plot.bar()。
<img src="https://huihuiteresa.github.io/image/youdao/20250521006.png" width="400">
再來看一個有關(guān)餐廳小費的數(shù)據(jù)集。假設(shè)我們想要做一張堆積柱狀圖,以展示每天各種聚會規(guī)模的數(shù)據(jù)點的百分比:
tips.csv文件地址:https://github.com/mwaskom/seaborn-data/blob/master/tips.csv
內(nèi)容如下圖:
[圖片上傳失敗...(image-752ef7-1747958731525)]
tips = pd.read_csv("tips.csv")
party_counts = pd.crosstab(tips["day"],tips["size"])
# size 1 2 3 4 5 6
# day
# Fri 1 16 1 1 0 0
# Sat 2 53 18 13 1 0
# Sun 0 39 15 18 3 1
# Thur 1 48 4 5 1 3
party_counts = party_counts.reindex(index=["Thur","Fri","Sat","Sun"])
# 存在許多一人聚會和六人聚會
party_counts = party_counts.loc[:,2:5]
# size 2 3 4 5
# day
# Thur 48 4 5 1
# Fri 16 1 1 0
# Sat 53 18 13 1
# Sun 39 15 18 3
# 正態(tài)化各行的和等于1
party_pcts = party_counts.div(party_counts.sum(axis="columns"),axis="index")
# size 2 3 4 5
# day
# Thur 0.827586 0.068966 0.086207 0.017241
# Fri 0.888889 0.055556 0.055556 0.000000
# Sat 0.623529 0.211765 0.152941 0.011765
# Sun 0.520000 0.200000 0.240000 0.040000
party_pcts.plot.bar(stacked=True)
<img src="https://huihuiteresa.github.io/image/youdao/20250522001.png" width="400">
對于在繪制圖形之前需要進行聚合和匯總的數(shù)據(jù),使用seaborn可以減少工作量(使用conda install seaborn安裝)。用seaborn可視化每天的小費比例:
seaborn的繪圖函數(shù)需要使用data參數(shù),可以用pandas的DataFrame。其他參數(shù)用于引用列名。因為day的每個值有多次觀測值,柱狀圖的值是tip_pct的平均值。繪制在柱狀圖上的黑線代表95%置信區(qū)間(可以通過可選參數(shù)配置)。
import seaborn as sns
tips = pd.read_csv("tips.csv")
tips["tip_pct"] = tips["tip"]/(tips["total_bill"] * tips["tip"])
sns.barplot(x="tip_pct",y="day",data=tips,orient="h") # 消費的每日比例,帶有誤差條
<img src="https://huihuiteresa.github.io/image/youdao/20250522003.png" width="400">
可以用seaborn.set_style在不同的圖形外觀之間切換:
# 設(shè)置為灰度調(diào)色板
sns.set_palette("Greys_r")
直方圖和密度圖
直方圖是一種可以對值頻率進行離散化顯示的柱狀圖。數(shù)據(jù)點被拆分到離散且間隔均勻的箱中,并繪制各箱中數(shù)據(jù)點的數(shù)量。
再以之前的個小費數(shù)據(jù)為例,通過在Series上使用plot.hist方法,我們可以生成“小費占消費總額百分比”的直方圖:
tips = pd.read_csv("tips.csv")
tips["tip_pct"] = tips["tip"]/(tips["total_bill"] * tips["tip"])
tips["tip_pct"].plot.hist(bins=50)
<img src="https://huihuiteresa.github.io/image/youdao/20250522004.png" width="400">
與直方圖相關(guān)的一種圖表類型是密度圖,它是通過計算“可能會產(chǎn)生觀測數(shù)據(jù)的連續(xù)概率分布估計”得到的。一般的過程是將該分布近似為“核”的混合,即諸如正態(tài)分布之類的簡單分布。因此,密度圖也稱作核密度估計(KDE)圖。使用plot.density生成一幅常規(guī)混合正態(tài)分布估計的密度圖:
# 密度圖需要使用SciPy
pip install scipy
tips = pd.read_csv("tips.csv")
tips["tip_pct"] = tips["tip"]/(tips["total_bill"] * tips["tip"])
tips["tip_pct"].plot.density()
<img src="https://huihuiteresa.github.io/image/youdao/20250522005.png" width="400">
用seaborn的histplot方法繪制直方圖和密度圖更加簡單,還可以同時畫出直方圖和連續(xù)密度估計圖。作為例子,考慮一個由兩個不同的標(biāo)準(zhǔn)正態(tài)分布組成的二項分布:
import seaborn as sns
comp1 = np.random.standard_normal(200)
comp2 = 10+2*np.random.standard_normal(200)
values = pd.Series(np.concatenate([comp1,comp2]))
sns.histplot(values,bins=100,color="black")
<img src="https://huihuiteresa.github.io/image/youdao/20250522006.png" width="400">
散點圖或點圖
點圖或散點圖是觀察兩個一維數(shù)據(jù)序列之間的關(guān)系的有效方式。在下面這個例子中,我加載了來自statsmodels項目的macrodata數(shù)據(jù)集,選擇了幾個變量,然后計算對數(shù)差:
macrodata.csv數(shù)據(jù)格式如下圖:
[圖片上傳失敗...(image-f12ee5-1747958731525)]
macro = pd.read_csv("macrodata.csv")
data=macro[["cpi","m1","tbilrate","unemp"]]
# cpi m1 tbilrate unemp
# 0 28.98 139.7 2.82 5.8
# 1 29.15 141.7 3.08 5.1
# 2 29.35 140.5 3.82 5.3
# 3 29.37 140.0 4.33 5.6
# 4 29.54 139.6 3.50 5.2
trans_data = np.log(data).diff().dropna()
# cpi m1 tbilrate unemp
# 1 0.005849 0.014215 0.088193 -0.128617
# 2 0.006838 -0.008505 0.215321 0.038466
# 3 0.000681 -0.003565 0.125317 0.055060
# 4 0.005772 -0.002861 -0.212805 -0.074108
# 5 0.000338 0.004289 -0.266946 0.000000
# 使用seaborn的regplot方法,它可以生成散點圖,并添加一條線性回歸擬合的直線
ax = sns.regplot(x="m1",y="unemp",data=trans_data)
<img src="https://huihuiteresa.github.io/image/youdao/20250522008.png" width="400">
# seaborn提供了一個便捷的pairplot函數(shù),它支持在對角線上放置每個變量的直方圖或密度估計
sns.pairplot(trans_data,diag_kind="kde",plot_kws={"alpha":0.2})
<img src="https://huihuiteresa.github.io/image/youdao/20250522009.png" width="600">
分面網(wǎng)格和分類數(shù)據(jù)
要是數(shù)據(jù)集有額外的分組維度,該怎么辦呢?對于有多個分類變量的數(shù)據(jù),一種可視化方法是使用分面網(wǎng)格。分面網(wǎng)格是一種二維繪圖布局,它根據(jù)特定變量所具有的不同值,將數(shù)據(jù)在每個軸上的繪圖中進行分割。seaborn有一個實用的內(nèi)置函數(shù)catplot,可以簡化制作多種分面圖。
tips = pd.read_csv("tips.csv")
tips["tip_pct"] = tips["tip"]/(tips["total_bill"] * tips["tip"])
sns.catplot(x="day",y="tip_pct",hue="time",col="smoker",kind="bar",data=tips[tips.tip_pct<1])
<img src="https://huihuiteresa.github.io/image/youdao/20250523001.png" width="600">
# 給每個time值添加一行來擴展分面網(wǎng)格
sns.catplot(x="day",y="tip_pct",row="time",col="smoker",kind="bar",data=tips[tips.tip_pct<1]) # 按天的小費百分比,通過time/smoker分面
<img src="https://huihuiteresa.github.io/image/youdao/20250523002.png" width="400">
# 箱型圖(它可以顯示中位數(shù)、四分位數(shù)和異常值)
sns.catplot(x="tip_pct",y="day",kind="box",data=tips[tips.tip_pct<0.5])
<img src="https://huihuiteresa.github.io/image/youdao/20250523003.png" width="400">