繪圖和可視化

繪圖和可視化

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">

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容