Seaborn(二)之數據集分布可視化
當處理一個數據集的時候,我們經常會想要先看看特征變量是如何分布的。這會讓我們對數據特征有個很好的初始認識,同時也會影響后續數據分析以及特征工程的方法。本篇將會介紹如何使用 seaborn 的一些工具來檢測單變量和雙變量分布情況。
%matplotlib inline
import numpy as np
import pandas as pd
from scipy import stats, integrate
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(color_codes=True)
np.random.seed(sum(map(ord, "distributions")))
注意:這里的數據集是隨機產生的分布數據,由 numpy 生成,數據類型是ndarray。當然,pandas 的 Series 數據類型也是可以使用的,比如我們經常需要從 DataFrame 表中提取某一特征(某一列)來查看分布情況。
繪制單變量分布
在 seaborn 中,快速觀察單變量分布的最方便的方法就是使用 distplot() 函數。默認會使用柱狀圖(histogram)來繪制,並提供一個適配的核密度估計(KDE)。
x = np.random.normal(size = 100)
sns.distplot(x)
<matplotlib.axes._subplots.AxesSubplot at 0x1a182da940>
直方圖(histograms)
直方圖是比較常見的,並且在 matplotlib 中已經存在了 hist 函數。直方圖在橫坐標的數據值范圍內均等分的形成一定數量的數據段(bins),並在每個數據段內用矩形條(bars)顯示y軸觀察數量的方式,完成了對的數據分布的可視化展示。
為了說明這個,我們可以移除 kde plot,然后添加 rug plot(在每個觀察點上的垂直小標簽)。當然,你也可以使用 rug plot 自帶的 rugplot() 函數,但是也同樣可以在 distplot 中實現:
sns.distplot(x, kde = False, rug = True)
<matplotlib.axes._subplots.AxesSubplot at 0x1a1867d358>
當繪制直方圖時,你最需要確定的參數是矩形條的數目以及如何放置它們。distplot()使用了一個簡單的規則推測出默認情況下最合適的數量,但是或多或少的對 bins 數量進行一些嘗試也許能找出數據的其它特征:
sns.distplot(x, bins=20, kde=False, rug=True)
<matplotlib.axes._subplots.AxesSubplot at 0x1a1882f8d0>
核密度估計(Kernel density estimation)
核密度估計可能不被大家所熟悉,但它對於繪制分布的形狀是一個非常有用的工具。就像直方圖那樣,KDE plots 會在一個軸上通過高度沿着其它軸將觀察的密度編碼。
sns.distplot(x, hist=False, rug=True);
繪制 KDE 比繪制直方圖需要更多的計算。它的計算過程是這樣的,每個觀察點首先都被以這個點為中心的正態分布曲線所替代。
x = np.random.normal(0, 1, size=30)
bandwidth = 1.06 * x.std() * x.size ** (-1 / 5.)
support = np.linspace(-4, 4, 200)
kernels = []
for x_i in x:
kernel = stats.norm(x_i, bandwidth).pdf(support)
kernels.append(kernel)
plt.plot(support, kernel, color="r")
sns.rugplot(x, color=".2", linewidth=3);
然后,這些替代的曲線進行加和,並計算出在每個點的密度值。最終生成的曲線被歸一化,以使得曲線下面包圍的面積是1。
density = np.sum(kernels, axis=0)
density /= integrate.trapz(density, support)
plt.plot(support, density)
[<matplotlib.lines.Line2D at 0x1a18bf3048>]
我們可以看到,如果我們使用 kdeplot() 函數,我們可以得到相同的曲線。這個函數實際上也被 distplot() 所使用,但是如果你就只想要密度估計,那么 kdeplot() 會提供一個直接的接口更簡單的操作其它選項。
sns.kdeplot(x, shade=True)
<matplotlib.axes._subplots.AxesSubplot at 0x1a18c8b518>
KDE 的帶寬參數(bw)控制着密度估計曲線的寬窄形狀,有點類似直方圖中的 bins 參數的作用。它對應着我們上面繪制的 KDE 的寬度。默認情況下,函數會按照一個通用的參考規則來估算出一個合適的值,但是嘗試更大或者更小也可能會有幫助:
sns.kdeplot(x)
sns.kdeplot(x, bw=.2, label="bw: 0.2")
sns.kdeplot(x, bw=2, label="bw: 2")
plt.legend()
如上所述,高斯KDE過程的意味着估計延續了數據集中最大和最小的值。 可以通過cut參數來控制繪制曲線的極值值的距離; 然而,這只影響曲線的繪制方式,而不是曲線如何擬合:
sns.kdeplot(x, shade=True, cut=0)
sns.rugplot(x);
擬合參數分布
也可以使用distplot()將參數分布擬合到數據集,並可視化地評估其與觀察數據的對應程度:
x = np.random.gamma(6, size=200)
sns.distplot(x, kde=False, fit=stats.gamma);
繪制雙變量分布
對於雙變量分布的可視化也是非常有用的。在 seaborn 中最簡單的方法就是使用 joinplot() 函數,它能夠創建一個多面板圖形來展示兩個變量之間的聯合關系,以及每個軸上單變量的分布情況。
mean, cov = [0, 1], [(1, .5), (.5, 1)]
data = np.random.multivariate_normal(mean, cov, 200)
df = pd.DataFrame(data, columns=["x", "y"])
Scatterplots
雙變量分布最熟悉的可視化方法無疑是散點圖了,在散點圖中每個觀察結果以x軸和y軸值所對應的點展示。你可以用 matplotlib 的 plt.scatter 函數來繪制一個散點圖,它也是jointplot()函數顯示的默認方式。
sns.jointplot(x="x", y="y", data=df)
<seaborn.axisgrid.JointGrid at 0x1a18df47f0>
Hexbin plots
直方圖 histogram 的雙變量類似圖被稱為 “hexbin” 圖,因為它展示了落在六角形箱內的觀測量。這種繪圖對於相對大的數據集效果最好。它可以通過 matplotlib 的 plt.hexbin 函數使用,也可以作為 jointplot 的一種類型參數使用。它使用白色背景的時候視覺效果最好。
x, y = np.random.multivariate_normal(mean, cov, 1000).T
with sns.axes_style("white"):
sns.jointplot(x=x, y=y, kind="hex", color="k");
Kernel density estimation
還使用上面描述的核密度估計過程來可視化雙變量分布。在 seaborn 中,這種繪圖以等高線圖展示,並且可以作為 jointplot()的一種類型參數使用。
sns.jointplot(x="x", y="y", data=df, kind="kde");
如果你希望讓雙變量密度看起來更連續,您可以簡單地增加 n_levels 參數增加輪廓級數:
f, ax = plt.subplots(figsize=(6, 6))
cmap = sns.cubehelix_palette(as_cmap=True, dark=0, light=1, reverse=True)
sns.kdeplot(df.x, df.y, cmap=cmap, n_levels=60, shade=True);
jointplot()函數使用JointGrid來管理圖形。為了獲得更多的靈活性,您可能需要直接使用JointGrid繪制圖形。jointplot()在繪制后返回JointGrid對象,你可以用它來添加更多層或調整可視化的其他方面:
g = sns.jointplot(x="x", y="y", data=df, kind="kde", color="m")
g.plot_joint(plt.scatter, c="w", s=30, linewidth=1, marker="+")
g.ax_joint.collections[0].set_alpha(0)
g.set_axis_labels("$X$", "$Y$");
可視化數據集成對關系
為了繪制數據集中多個成對的雙變量,你可以使用 pairplot() 函數。這創建了一個軸矩陣,並展示了在一個 DataFrame 中每對列的關系。默認情況下,它也繪制每個變量在對角軸上的單變量。
iris = sns.load_dataset("iris")
sns.pairplot(iris)
<seaborn.axisgrid.PairGrid at 0x1a19742278>
就像 joinplot() 和 JoinGrid 之間的關系,pairplot() 函數建立在 PairGrid 對象之上,直接使用可以更靈活。
g = sns.PairGrid(iris)
g.map_diag(sns.kdeplot)
g.map_offdiag(sns.kdeplot, cmap="Blues_d", n_levels=6)
<seaborn.axisgrid.PairGrid at 0x1a1b5ed978>