什麼是核密度估算圖(KDE圖)?

maxresdefault-3

理解資料分佈是資料分析最重要的方面之一。視覺化分佈有助於我們理解可能隱藏在原始數字中的模式、趨勢和異常。雖然直方圖通常用於此目的,但它們有時可能過於塊狀,無法顯示一些細微的細節。核密度估計 (KDE) 圖透過估計連續資料的機率密度函式,提供了一種更平滑、更準確的視覺化方法。這使得資料科學家和分析師能夠更清晰地看到重要的特徵,例如多個峰值、偏度和異常值。學習使用 KDE 圖是更好地理解資料洞察的寶貴技能。在本文中,我們將介紹 KDE 圖及其實現。

什麼是核密度估計 (KDE) 圖?

核密度估計 (KDE) 是一種非引數方法,用於估計連續隨機變數的機率密度函式 (PDF)。簡而言之,KDE 會生成一條平滑的曲線(密度估計值),以近似資料的分佈,而不是像直方圖那樣使用分隔的區間。從概念上講,我們在每個資料點上都有一個“核”(一個平滑且對稱的函式),並將它們相加以形成連續的密度。從數學上講,如果我們有資料點 x1,…,xn,那麼點 x 處的 KDE 為:

核密度估計 (KDE)

其中 K 是核函式(通常是鐘形函式),h 是頻寬(平滑引數)。由於分佈沒有像“正態”或“指數”這樣的固定形式,因此 KDE 被稱為非引數估計器。KDE 透過將每個資料點變成一座小山丘來“平滑直方圖”;所有這些小山丘加在一起構成了總密度(如下圖所示)。

平滑直方圖

根據用例,會使用不同型別的核函式。例如,高斯(或正態)核因其平滑度而廣受歡迎,但也可以使用其他核函式,例如 Epanechnikov(拋物線)、均勻、三角形、雙權重甚至三權重。預設情況下,許多庫都使用高斯核,這意味著每個資料點都會使估計值呈現鐘形凸起。Epanechnikov 核可以最小化所有資料點之間的均方誤差,但人們通常仍然選擇高斯核只是為了方便。

密度圖在分析資料以顯示分佈形狀方面非常有用。它們適用於大資料集,並且可以顯示直方圖可能隱藏的內容(例如多個峰值或長尾)。例如,KDE 圖可以捕捉雙峰或傾斜形狀,從而告知您有關子組或異常值的資訊。在探索新的數值變數時,繪製 KDE 通常是人們首先要做的事情之一。在某些領域(如訊號處理或計量經濟學),KDE 也稱為 Parzen-Rosenblatt 視窗方法。

重要概念

理解 KDE 圖的工作原理時,需要牢記以下關鍵點:

  • 非引數 PDF 估計:KDE 不假設底層分佈。它直接根據資料構建平滑的估計值。
  • 核函式:核函式 K(例如高斯核)是一個對稱加權函式。常見的核函式包括高斯核、Epanechnikov 核、均勻核等。只要調整頻寬,核函式的選擇對結果的影響很小。
  • 頻寬(平滑):引數 h(或等效地,bw)會縮放核函式。h 越大,曲線越平滑(越寬);h 越小,曲線越緊密、越詳細。最佳頻寬通常縮放比例為 n−1/5。
  • 偏差-方差權衡:一個關鍵考慮因素是在細節和平滑度之間取得平衡:h 太小會導致估計值有噪聲;太大的 h 可能會使重要的峰或谷變得過於平滑。

在Python中使用KDE繪圖

Seaborn(基於 Matplotlib 構建)和 Pandas 都可以輕鬆地在 Python 中建立 KDE 繪圖。現在,我將展示一些使用模式、引數和自定義技巧。

Seaborn的kdeplot

首先,使用 seaborn.kdeplot 函式。該函式為資料集繪製單變數(或雙變數)KDE 曲線。它內部預設使用高斯核,並支援許多其他選項。例如,繪製鳶尾花資料集中 sepal_width 變數的分佈。

使用 Seaborn 繪製單變數 KDE 繪圖(Iris 資料集示例)

以下示例演示如何為單個連續變數建立 KDE 繪圖。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import seaborn as sns
import matplotlib.pyplot as plt
# Load example dataset
df = sns.load_dataset('iris')
# Plot 1D KDE
sns.kdeplot(data=df, x='sepal_width', fill=True)
plt.title("KDE of Iris Sepal Width")
plt.xlabel("Sepal Width")
plt.ylabel("Density")
plt.show()
import seaborn as sns import matplotlib.pyplot as plt # Load example dataset df = sns.load_dataset('iris') # Plot 1D KDE sns.kdeplot(data=df, x='sepal_width', fill=True) plt.title("KDE of Iris Sepal Width") plt.xlabel("Sepal Width") plt.ylabel("Density") plt.show()
import seaborn as sns
import matplotlib.pyplot as plt
# Load example dataset
df = sns.load_dataset('iris')
# Plot 1D KDE
sns.kdeplot(data=df, x='sepal_width', fill=True)
plt.title("KDE of Iris Sepal Width")
plt.xlabel("Sepal Width")
plt.ylabel("Density")
plt.show()

單變數 KDE 繪圖

從上圖中,我們可以看到 speal_width 值的密度曲線平滑。此外,fill=True 引數會塑造麴線下方的面積,如果 fill = False,則只能看到深藍色的線。

比較不同類別的 KDE 圖

到目前為止,我們已經瞭解了簡單的單變數 KDE 圖。現在,讓我們看看 Seaborn 的 kdeplot 方法最強大的用途之一,即它能夠使用 hue 引數比較不同子組的分佈。

假設我們想要分析午餐和晚餐時段餐廳總賬單的分佈差異。因此,為此,我們將使用 tips 資料集。這樣,我們可以在同一軸上疊加兩個 KDE 圖(一個用於午餐,一個用於晚餐),以便直接比較。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset('tips')
sns.kdeplot(data=tips, x='total_bill', hue='time', fill=True,
common_norm=False, alpha=0.5)
plt.title("KDE of Total Bill (Lunch vs Dinner)")
plt.show()
import seaborn as sns import matplotlib.pyplot as plt tips = sns.load_dataset('tips') sns.kdeplot(data=tips, x='total_bill', hue='time', fill=True, common_norm=False, alpha=0.5) plt.title("KDE of Total Bill (Lunch vs Dinner)") plt.show()
import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset('tips')
sns.kdeplot(data=tips, x='total_bill', hue='time', fill=True,
common_norm=False, alpha=0.5)
plt.title("KDE of Total Bill (Lunch vs Dinner)")
plt.show()

疊加兩個 KDE 圖

因此,我們可以看到上面的程式碼疊加了兩條密度曲線。fill=True 會在每條曲線下方新增陰影,使差異更加明顯;common_norm=False 確保每個組的密度值獨立縮放; alpha=0.5 則增加了透明度,使重疊區域更容易理解。

您還可以嘗試使用 multiple=‘layer’、‘stack’ 或 ‘fill’ 來更改多重密度圖的顯示方式。

Pandas和Matplotlib

如果您使用 Pandas,還可以使用內建繪圖功能來獲取 KDE 圖。Pandas 系列資料包含 plot(kind='density')  或 plot.density() 方法,它們充當 Matplotlib 中相關方法的包裝器。

程式碼:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import pandas as pd
import numpy as np
data = np.random.randn(1000) # 1000 random points from a normal distribution
s = pd.Series(data)
s.plot(kind='density')
plt.title("Pandas Density Plot")
plt.xlabel("Value")
plt.show()
import pandas as pd import numpy as np data = np.random.randn(1000) # 1000 random points from a normal distribution s = pd.Series(data) s.plot(kind='density') plt.title("Pandas Density Plot") plt.xlabel("Value") plt.show()
import pandas as pd
import numpy as np
data = np.random.randn(1000) # 1000 random points from a normal distribution
s = pd.Series(data)
s.plot(kind='density')
plt.title("Pandas Density Plot")
plt.xlabel("Value")
plt.show()

Pandas 繪製 KDE 圖

或者,我們可以使用 SciPy 的 gaussian_kde 方法手動計算和繪製 KDE。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import numpy as np
from scipy.stats import gaussian_kde
data = np.concatenate([np.random.normal(-2, 0.5, 300), np.random.normal(3,
1.0, 500)])
kde = gaussian_kde(data, bw_method=0.3) # bandwidth can be a factor or
'silverman', 'scott'
xs = np.linspace(min(data), max(data), 200)
density = kde(xs)
plt.plot(xs, density)
plt.title("Manual KDE via scipy")
plt.xlabel("Value"); plt.ylabel("Density")
plt.show()
import numpy as np from scipy.stats import gaussian_kde data = np.concatenate([np.random.normal(-2, 0.5, 300), np.random.normal(3, 1.0, 500)]) kde = gaussian_kde(data, bw_method=0.3) # bandwidth can be a factor or 'silverman', 'scott' xs = np.linspace(min(data), max(data), 200) density = kde(xs) plt.plot(xs, density) plt.title("Manual KDE via scipy") plt.xlabel("Value"); plt.ylabel("Density") plt.show()
import numpy as np
from scipy.stats import gaussian_kde
data = np.concatenate([np.random.normal(-2, 0.5, 300), np.random.normal(3,
1.0, 500)])
kde = gaussian_kde(data, bw_method=0.3) # bandwidth can be a factor or
'silverman', 'scott'
xs = np.linspace(min(data), max(data), 200)
density = kde(xs)
plt.plot(xs, density)
plt.title("Manual KDE via scipy")
plt.xlabel("Value"); plt.ylabel("Density")
plt.show()

手動計算和繪製 KDE

上述程式碼建立了一個雙峰資料集並估算了其密度。實際上,使用 Seaborn 或 Pandas 實現相同功能要容易得多。

解讀KDE圖或核密度估計圖

解讀 KDE 圖類似於直方圖,但 KDE 圖的曲線較為平滑。點 x 處的曲線高度與該點的估計機率密度成正比。曲線在一定範圍內的面積對應於落在該範圍內的機率。由於曲線是連續的,因此任何一點的確切值都不如整體形狀重要:

  • 峰(眾數):高峰表示資料中存在一個共同值或聚類。多個峰表示存在多個眾數(例如,子群體的混合)。
  • 散佈:曲線的寬度表示離散程度。曲線越寬表示變異性越大(標準差越大),而曲線越窄、越高則表示資料聚集程度較高。
  • 尾部:觀察密度減小的速度。粗尾表示存在異常值;短尾表示資料有界。
  • 比較曲線:當疊加組時,尋找變化(一個分佈系統地更高或更低)或形狀的差異。

用例和示例

KDE 圖在日常資料分析中有很多實用的應用:

  • 探索性資料分析 (EDA):當我們首次檢視資料集時,KDE 可以幫助我們瞭解變數的分佈情況,無論它們是正態分佈、偏態分佈還是具有多個峰值(多峰)。眾所周知,逐個檢查變數的分佈可能是獲得新資料集後應該做的第一件事。KDE 比直方圖更平滑,因此在 EDA 過程中瞭解資料時通常更有幫助。
  • 比較分佈:當我們想要比較不同群體的表現時,KDE 非常有效。例如,在同一軸上繪製男孩和女孩測試分數的 KDE 值,可以顯示平均值或方差是否存在差異。Seaborn 可以非常輕鬆地使用不同顏色疊加 KDE。KDE 圖通常比並排的直方圖更簡潔,並且能夠更好地展現不同群體之間的差異。
  • 平滑直方圖:KDE 可以被認為是直方圖的平滑版本。當直方圖看起來過於波動或隨 bin 大小變化很大時,KDE 會提供更穩定、更清晰的影像。例如,上面的 Airbnb 價格示例可以用直方圖顯示,但 KDE 使其更容易解讀。KDE 有助於對資料形狀進行更連續的估計,這非常方便,尤其是在資料量適中的情況下。

核密度圖的替代方案

雖然核密度圖對於顯示分佈的平滑估計非常有用,但它並非總是最佳選擇。根據資料大小或具體目標,您還可以嘗試其他型別的圖。以下是一些常見的圖:

直方圖

說實話,這是觀察分佈最基本的方法。你只需將資料切分成幾個箱,然後計算每個箱中有多少資料。使用起來很簡單,但如果使用的箱數太多或太少,就會變得混亂。有時它會隱藏一些規律。KDE 可以透過平滑這些起伏來解決這個問題。

直方圖

箱線圖(也稱為盒須圖)

如果您只是想知道大多數資料的位置,例如中位數、四分位數等,那麼箱線圖就很有用。它可以快速識別異常值。但它不像 KDE 那樣能夠真正展現資料的形狀。如果您不需要所有細節,它仍然很有用。

箱線圖(也稱為盒須圖)

小提琴圖

你可以把它們想象成箱線圖的升級版,同時還能展示KDE形狀。它兼具兩者的優點,既能提供彙總統計資料,又能直觀地瞭解分佈情況。我用它來並排比較不同組的資料。

小提琴圖

地毯圖

地毯圖很簡單。它們只是將每個資料點顯示為軸上的小垂直線。通常與 KDE 配合使用,以顯示實際資料點的位置。但是,當資料量過大時,地毯圖看起來可能會有些混亂。

地毯圖

直方圖 + KDE 組合

有些人喜歡將直方圖與 KDE 結合使用,因為直方圖顯示計數,而 KDE 在頂部新增一條平滑曲線。這樣,他們就可以同時看到原始頻率和平滑後的模式。

直方圖 + KDE 組合

說實話,使用哪種方式取決於你的需求。KDE 非常適合平滑模式,但有時你並不需要所有這些;也許一個簡單的箱線圖或直方圖就足夠了,尤其是在你時間緊迫或只是快速瀏覽內容的情況下。

小結

KDE 圖提供了一種強大且直觀的方式來視覺化連續資料的分佈。與普通的直方圖不同,它們透過核函式估計機率密度函式,從而繪製出平滑連續的曲線,這使得諸如偏度、多模態或異常值等細微模式更容易被注意到。無論您是在進行探索性資料分析、比較分佈還是發現異常,KDE 圖都能為您提供極大的幫助。Seaborn 或 Pandas 等工具使建立和使用 KDE 圖變得非常簡單。

評論留言