在數(shù)據(jù)科學(xué)領(lǐng)域,數(shù)據(jù)可視化作為一個(gè)核心工具,為我們提供了深入了解、探索和解釋復(fù)雜數(shù)據(jù)集的能力。這不僅幫助我們清晰地呈現(xiàn)數(shù)據(jù)的特點(diǎn),而且還允許非專家用戶快速理解數(shù)據(jù)的關(guān)鍵趨勢(shì)和模式。本文將介紹一系列精選的數(shù)據(jù)可視化技巧,這些技巧在實(shí)踐中都被證明是非常有價(jià)值的。從KS圖到小提琴圖,每一種都有其獨(dú)特的應(yīng)用場(chǎng)景和優(yōu)勢(shì)。
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats
# Generate random data and a normal distribution for demonstration purposes
np.random.seed(0)
data = np.random.normal(0, 1, 1000)
theoretical = np.sort(np.random.normal(0, 1, 1000))
# Compute KS statistic
ks_statistic, p_value = stats.ks_2samp(data, theoretical)
# Plot KS Plot
plt.figure(figsize=(10, 6))
plt.plot(np.sort(data), np.linspace(0, 1, len(data), endpoint=False), label='Data CDF')
plt.plot(theoretical, np.linspace(0, 1, len(theoretical), endpoint=False), label='Theoretical CDF', linestyle='--')
plt.title(f'KS Plot (KS Statistic = {ks_statistic:.2f})')
plt.legend()
plt.xlabel('Value')
plt.ylabel('CDF')
plt.grid(True)
plt.show()
這是一個(gè)KS圖,它表示了數(shù)據(jù)的累積分布函數(shù)(CDF)與理論正態(tài)分布的CDF。在這個(gè)圖中:
藍(lán)色實(shí)線代表我們的數(shù)據(jù)CDF。黃色虛線代表理論的正態(tài)分布CDF。KS統(tǒng)計(jì)量表示這兩個(gè)分布之間的最大差異。在這個(gè)例子中,KS統(tǒng)計(jì)量約為0.03,表示兩個(gè)CDF之間的最大差異是0.03。
這種圖形在檢驗(yàn)數(shù)據(jù)是否符合某種理論分布時(shí)非常有用,如正態(tài)分布。
由于SHAP圖需要一個(gè)預(yù)訓(xùn)練的模型和特定的數(shù)據(jù),我會(huì)簡(jiǎn)化這個(gè)過程并使用一個(gè)標(biāo)準(zhǔn)數(shù)據(jù)集和XGBoost模型進(jìn)行演示。
import xgboost
import shap
# train an XGBoost model
X, y = shap.datasets.boston()
model = xgboost.XGBRegressor().fit(X, y)
# explain the model's predictions using SHAP
# (same syntax works for LightGBM, CatBoost, scikit-learn, transformers, Spark, etc.)
explainer = shap.Explainer(model)
shap_values = explainer(X)
# visualize the first prediction's explanation
shap.plots.waterfall(shap_values[0])
上述解釋展示了每個(gè)特征如何推動(dòng)模型的輸出從基值(我們傳遞的訓(xùn)練數(shù)據(jù)集上的平均模型輸出)到模型輸出。推動(dòng)預(yù)測(cè)更高的特征顯示為紅色,推動(dòng)預(yù)測(cè)更低的特征顯示為藍(lán)色。
如果我們?nèi)『芏嘞裆厦婺菢拥牧α繄D解釋,將它們旋轉(zhuǎn)90度,然后水平堆疊,我們可以看到整個(gè)數(shù)據(jù)集的解釋
# 可視化所有的訓(xùn)練集預(yù)測(cè)
shap.plots.force(shap_values)
為了理解單一特征如何影響模型的輸出,我們可以繪制該特征的SHAP值與數(shù)據(jù)集中所有示例的特征值。由于SHAP值代表特征對(duì)模型輸出變化的責(zé)任,下面的圖表示隨著RM(一個(gè)地區(qū)的房屋平均房間數(shù))的變化,預(yù)測(cè)的房?jī)r(jià)發(fā)生的變化。RM的單一值的垂直分散表示與其他特征的交互效應(yīng)。為了幫助揭示這些交互作用,我們可以用另一個(gè)特征進(jìn)行著色。如果我們將整個(gè)解釋張量傳遞給顏色參數(shù),散點(diǎn)圖將選擇最佳的顏色特征。在這種情況下,它選擇了RAD(到放射狀高速公路的可達(dá)性指數(shù)),因?yàn)檫@突出了對(duì)于具有高RAD值的地區(qū),房屋的平均房間數(shù)對(duì)房?jī)r(jià)的影響較小。
# 創(chuàng)建一個(gè)依賴散點(diǎn)圖,顯示整個(gè)數(shù)據(jù)集中單一特征的效果
shap.plots.scatter(shap_values[:,'RM'], color=shap_values)
為了獲得哪些特征對(duì)模型最重要的概覽,我們可以繪制每個(gè)樣本的每個(gè)特征的SHAP值。下面的圖按照所有樣本的SHAP值大小對(duì)特征進(jìn)行排序,并使用SHAP值顯示每個(gè)特征對(duì)模型輸出的影響的分布。顏色代表特征值(紅色高,藍(lán)色低)。這揭示了例如高LSTAT(%較低的人口狀態(tài))降低了預(yù)測(cè)的房?jī)r(jià)。
# 總結(jié)所有特征的影響
shap.plots.beeswarm(shap_values)
# Generate QQ Plot using the random data for KS plot
plt.figure(figsize=(10, 6))
stats.probplot(np.random.normal(0, 1, 1000), dist='norm', plot=plt)
plt.title('QQ Plot for Randomly Generated Normal Data')
plt.grid(True)
plt.show()
這是一個(gè)QQ圖,用于比較隨機(jī)生成的正態(tài)分布數(shù)據(jù)與理論正態(tài)分布。在這個(gè)圖中:
X軸表示理論正態(tài)分布的分位數(shù)。Y軸表示觀察到的數(shù)據(jù)的分位數(shù)。如果數(shù)據(jù)完全遵循正態(tài)分布,那么點(diǎn)會(huì)緊密地圍繞紅色的參考線。從這個(gè)圖中我們可以看到,數(shù)據(jù)點(diǎn)大致圍繞參考線,這意味著我們的隨機(jī)數(shù)據(jù)與正態(tài)分布非常接近。
from sklearn.decomposition import PCA
# Use PCA on the Boston dataset
pca = PCA()
X_pca = pca.fit_transform(X)
# Calculate cumulative explained variance
cumulative_variance = np.cumsum(pca.explained_variance_ratio_)
# Plot Cumulative Explained Variance Plot
plt.figure(figsize=(10, 6))
plt.plot(range(1, len(cumulative_variance)+1), cumulative_variance, marker='o', linestyle='--')
plt.title('Cumulative Explained Variance Plot')
plt.xlabel('Number of Components')
plt.ylabel('Cumulative Explained Variance')
plt.grid(True)
plt.show()
這是一個(gè)累積解釋方差圖,它展示了當(dāng)我們使用主成分分析(PCA)進(jìn)行降維時(shí),每個(gè)主成分解釋的方差的累積總和。
在圖中:
X軸表示主成分的數(shù)量。Y軸表示對(duì)應(yīng)數(shù)量的主成分所解釋的累積方差的比例。例如,前5個(gè)主成分大致解釋了近80%的方差。這意味著如果我們只使用前5個(gè)主成分來代表數(shù)據(jù),我們將能夠保留原始數(shù)據(jù)的大約80%的信息。
這種圖形在決定PCA中要保留的主成分?jǐn)?shù)量時(shí)非常有用,因?yàn)樗梢詭椭覀兤胶饨稻S與保留信息之間的權(quán)衡。
Gini不純度 vs. 摘的圖通常用于比較決策樹分割的不純度或混亂度。這兩種度量方法都可以用 作決策樹算法中的分裂標(biāo)準(zhǔn)。在不同的情境下,一個(gè)方法可能比另一個(gè)方法更優(yōu)。
Gini不純度:當(dāng)一個(gè)節(jié)點(diǎn)是純凈的 (即,它只包含一個(gè)類的樣本) 時(shí),Gini不純度為 0 。其最 大值取決于類的數(shù)量,但對(duì)于二分類問題,最大值為 0.5 。
桷:當(dāng)一個(gè)節(jié)點(diǎn)是純凈的時(shí),摘為 0 。與Gini不純度一樣,其最大值取決于頭的數(shù)量。對(duì)于二 分夾問題,最大值為 1 。
為了可視化這兩個(gè)度量方法,我們可以為給定的概率值計(jì)算Gini不純度和摘,并將它們繪制在 同一張圖上。這將幫助我們理解兩者在不同概率值下的行為。讓我們來彸制這個(gè)圖。
# Calculate Gini Impurity and Entropy for a range of probability values
probabilities = np.linspace(0, 1, 100)
gini = [1 - p**2 - (1-p)**2 for p in probabilities]
entropy = [-p*np.log2(p) - (1-p)*np.log2(1-p) if p != 0 and p != 1 else 0 for p in probabilities]
# Plot Gini vs Entropy
plt.figure(figsize=(10, 6))
plt.plot(probabilities, gini, label='Gini Impurity', color='blue')
plt.plot(probabilities, entropy, label='Entropy', color='red', linestyle='--')
plt.title('Gini Impurity vs. Entropy')
plt.xlabel('Probability')
plt.ylabel('Impurity/Entropy Value')
plt.legend()
plt.grid(True)
plt.show()
這是一個(gè)Gini不純度 vs. 熵的圖。在此圖中:
X軸表示某一類的概率值。藍(lán)色實(shí)線表示Gini不純度。紅色虛線表示熵。從圖中可以看出:
當(dāng)概率為0或1時(shí)(也就是說,節(jié)點(diǎn)是純凈的),Gini不純度和熵都為0。對(duì)于二分類問題,當(dāng)概率為0.5時(shí)(即,兩個(gè)類的樣本數(shù)量相等),Gini不純度和熵都達(dá)到最大值。這兩種度量方法都用于度量決策樹節(jié)點(diǎn)的不純度或混亂度。選擇哪一個(gè)作為分裂標(biāo)準(zhǔn)取決于具體的應(yīng)用和數(shù)據(jù)。
偏差-方差權(quán)衡是監(jiān)督學(xué)習(xí)中的一個(gè)核心概念,它解釋了模型的總誤差是如何由三個(gè)不同的誤差類型組成的:
偏差(Bias):描述了模型預(yù)測(cè)的平均值與真實(shí)值之間的差異。高偏差意味著模型可能欠擬合數(shù)據(jù)。方差(Variance):描述了模型預(yù)測(cè)對(duì)于不同訓(xùn)練集的變化程度。高方差意味著模型可能過擬合數(shù)據(jù)。不可減少的誤差:描述了即使在理想條件下,由于數(shù)據(jù)中的噪聲導(dǎo)致的誤差。
在實(shí)際應(yīng)用中,我們通常會(huì)看到一個(gè)權(quán)衡關(guān)系:隨著模型復(fù)雜度的增加,偏差會(huì)減少,但方差會(huì)增加,反之亦然。因此,我們的目標(biāo)是找到一個(gè)平衡點(diǎn),使得總誤差最小化。
為了直觀地展示這個(gè)權(quán)衡關(guān)系,我會(huì)繪制一個(gè)理論圖,顯示偏差、方差和總誤差隨模型復(fù)雜度的變化情況。
# Simulating the Bias-Variance Tradeoff
# Model complexity range (for the sake of this example)
complexity = np.linspace(0, 1, 200)
# Simulated bias and variance values (just for visualization)
bias_squared = (1 - complexity)**2.5
variance = complexity**2.5
# Total error is the sum of bias^2, variance, and some irreducible error
total_error = bias_squared + variance + 0.2
# Plotting the Bias-Variance tradeoff
plt.figure(figsize=(10, 6))
plt.plot(complexity, bias_squared, label='Bias^2', color='blue')
plt.plot(complexity, variance, label='Variance', color='red')
plt.plot(complexity, total_error, label='Total Error', color='green')
plt.xlabel('Model Complexity')
plt.ylabel('Error')
plt.title('Bias-Variance Tradeoff')
plt.legend()
plt.grid(True)
plt.show()
這是模擬的偏差-方差權(quán)衡圖。
在此圖中:
X軸表示模型的復(fù)雜度。隨著模型復(fù)雜度的增加,我們期望偏差降低但方差增加。藍(lán)色曲線表示偏差的平方。可以看到,隨著模型復(fù)雜度的增加,偏差的平方降低。紅色曲線表示方差。隨著模型復(fù)雜度的增加,方差增加。綠色曲線表示總誤差,它是偏差的平方、方差和不可避免的誤差之和??梢钥吹剑傉`差在某個(gè)模型復(fù)雜度點(diǎn)達(dá)到最小值。這是我們希望找到的理想的模型復(fù)雜度。通過這種可視化,我們可以更好地理解如何選擇合適的模型復(fù)雜度,以平衡偏差和方差并最小化總誤差。
ROC曲線 (Receiver Operating Characteristic Curve)是用于評(píng)估分類模型性能的圖。它顯示了真正例率 (True Positive Rate, TPR) 和假正例率 (False Positive Rate, FPR) 之間的權(quán)衡關(guān)系,隨著分類閾值的變化。
from sklearn.datasets import load_iris
from sklearn.preprocessing import label_binarize
from sklearn.multiclass import OneVsRestClassifier
from sklearn.metrics import roc_curve, auc
# Load iris dataset
iris = load_iris()
X_iris = iris.data
y_iris = iris.target
# Binarize the output labels for multi-class ROC curve
y_iris_bin = label_binarize(y_iris, classes=[0, 1, 2])
n_classes = y_iris_bin.shape[1]
# Split the data
X_train_iris, X_test_iris, y_train_iris, y_test_iris = train_test_split(X_iris, y_iris_bin, test_size=0.5, random_state=42)
# Train a One-vs-Rest logistic regression model
clf_iris = OneVsRestClassifier(LogisticRegression(max_iter=10000))
y_score_iris = clf_iris.fit(X_train_iris, y_train_iris).decision_function(X_test_iris)
# Compute ROC curve for each class
fpr_iris = dict()
tpr_iris = dict()
roc_auc_iris = dict()
for i in range(n_classes):
fpr_iris[i], tpr_iris[i], _ = roc_curve(y_test_iris[:, i], y_score_iris[:, i])
roc_auc_iris[i] = auc(fpr_iris[i], tpr_iris[i])
# Plot the ROC curve for each class
plt.figure(figsize=(10, 6))
colors = ['blue', 'red', 'green']
for i, color in zip(range(n_classes), colors):
plt.plot(fpr_iris[i], tpr_iris[i], color=color, label=f'ROC curve for class {i} (area = {roc_auc_iris[i]:.2f})')
plt.plot([0, 1], [0, 1], 'k--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate (FPR)')
plt.ylabel('True Positive Rate (TPR)')
plt.title('Receiver Operating Characteristic (ROC) Curve for Iris Dataset')
plt.legend(loc='lower right')
plt.grid(True)
plt.show()
這是在iris數(shù)據(jù)集上訓(xùn)練的多類邏輯回歸模型的ROC曲線。
在此圖中:
X軸表示假正例率 (False Positive Rate, FPR)。Y軸表示真正例率 (True Positive Rate, TPR)。三種顏色(藍(lán)色、紅色和綠色)代表iris數(shù)據(jù)集的三個(gè)類別:setosa、versicolor和virginica。黑色虛線表示完全隨機(jī)分類器的表現(xiàn)。每條曲線下的面積(AUC)表示模型對(duì)應(yīng)類別的性能,值越接近1,表示模型的性能越好。
from sklearn.metrics import precision_recall_curve, average_precision_score
# Compute Precision-Recall curve for each class
precision_iris = dict()
recall_iris = dict()
average_precision_iris = dict()
for i in range(n_classes):
precision_iris[i], recall_iris[i], _ = precision_recall_curve(y_test_iris[:, i], y_score_iris[:, i])
average_precision_iris[i] = average_precision_score(y_test_iris[:, i], y_score_iris[:, i])
# Plot the Precision-Recall curve for each class
plt.figure(figsize=(10, 6))
colors = ['blue', 'red', 'green']
for i, color in zip(range(n_classes), colors):
plt.plot(recall_iris[i], precision_iris[i], color=color,
label=f'Precision-Recall curve for class {i} (average precision = {average_precision_iris[i]:.2f})')
plt.xlabel('Recall')
plt.ylabel('Precision')
plt.ylim([0.0, 1.05])
plt.xlim([0.0, 1.0])
plt.title('Precision-Recall Curve for Iris Dataset')
plt.legend(loc='upper right')
plt.grid(True)
plt.show()
這是在iris數(shù)據(jù)集上訓(xùn)練的多類邏輯回歸模型的精確率-召回率曲線。
在此圖中:
X軸表示召回率 (Recall)。Y軸表示精確率 (Precision)。三種顏色(藍(lán)色、紅色和綠色)代表iris數(shù)據(jù)集的三個(gè)類別:setosa、versicolor和virginica。每條曲線的平均精確率(average precision)表示模型對(duì)應(yīng)類別的性能,值越接近1,表示模型的性能越好。與ROC曲線不同,精確率-召回率曲線特別適合處理類別不平衡的數(shù)據(jù)集。這是因?yàn)樗鼘W⒂谡惖念A(yù)測(cè),而不是整體的表現(xiàn)。
from sklearn.cluster import KMeans
# Compute the sum of squared distances for different number of clusters
wcss = [] # within-cluster sum of squares
n_clusters_range = range(1, 11) # considering from 1 to 10 clusters
for i in n_clusters_range:
kmeans = KMeans(n_clusters=i, random_state=42)
kmeans.fit(X_iris)
wcss.append(kmeans.inertia_)
# Plot the Elbow curve
plt.figure(figsize=(10, 6))
plt.plot(n_clusters_range, wcss, marker='o')
plt.title('Elbow Curve for KMeans Clustering on Iris Dataset')
plt.xlabel('Number of clusters')
plt.ylabel('Within-Cluster Sum of Squares (WCSS)')
plt.grid(True)
plt.show()
這是iris數(shù)據(jù)集上的Elbow曲線。
在此圖中:
X軸表示考慮的簇?cái)?shù)(從1到10)。Y軸表示每個(gè)簇?cái)?shù)對(duì)應(yīng)的within-cluster sum of squares (WCSS)。WCSS是簇內(nèi)所有點(diǎn)到簇中心的平方距離之和?!爸獠俊笔乔€的一個(gè)關(guān)鍵點(diǎn),表示增加更多的簇不會(huì)顯著降低WCSS。在這個(gè)點(diǎn),我們可以選擇簇的最佳數(shù)量。從圖中可以看出,肘部大約在2或3的位置,這意味著選擇2或3個(gè)簇可能是最佳選擇。
以下是一些常見和有用的數(shù)據(jù)可視化技巧和方法:
直方圖 (Histograms): 用于顯示數(shù)值變量的分布??梢詭椭R(shí)別數(shù)據(jù)中的模式,如正態(tài)性、偏斜等。
箱型圖 (Box Plots): 用于顯示數(shù)據(jù)的五數(shù)概括:最小值、第一四分位數(shù)、中位數(shù)、第三四分位數(shù)和最大值??梢詭椭R(shí)別離群值。
散點(diǎn)圖 (Scatter Plots): 用于顯示兩個(gè)數(shù)值變量之間的關(guān)系。可以幫助識(shí)別相關(guān)性、數(shù)據(jù)的集群等。
堆積柱狀圖和堆積面積圖: 用于顯示多個(gè)類別或時(shí)間序列數(shù)據(jù)的部分到整體的關(guān)系。
熱圖 (Heatmaps): 用于顯示兩個(gè)類別變量之間關(guān)系的強(qiáng)度或數(shù)值變量的密度。
雷達(dá)/蜘蛛圖 (Radar/Spider Charts): 用于顯示多個(gè)變量的一個(gè)實(shí)體或類別的值。常用于性能分析或比較多個(gè)實(shí)體。
地圖可視化: 用于顯示地理數(shù)據(jù),如國(guó)家、城市或地理坐標(biāo)上的數(shù)據(jù)點(diǎn)。
時(shí)間序列圖: 用于顯示隨時(shí)間變化的數(shù)據(jù)。
小提琴圖 (Violin Plots): 結(jié)合了箱型圖和密度圖的特點(diǎn),顯示數(shù)據(jù)的分布和概率密度。
配對(duì)圖 (Pair Plots): 用于顯示多個(gè)數(shù)值變量之間的所有可能的散點(diǎn)圖。
樹狀圖 (Tree Maps): 用于顯示層次數(shù)據(jù)或部分到整體的關(guān)系。
圓環(huán)圖 (Donut Charts): 一個(gè)餅圖的變體,用于顯示類別數(shù)據(jù)的比例。
詞云 (Word Clouds): 用于顯示文本數(shù)據(jù)中詞的頻率。最常見的詞以較大的字體顯示。
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
# Generating some random data for visualization
np.random.seed(42)
data = np.random.randn(1000)
data2 = np.random.randn(1000) + 5
time_series_data = np.cumsum(np.random.randn(1000))
categories = np.random.choice(['A', 'B', 'C'], size=1000)
categories_2 = np.random.choice(['D', 'E', 'F'], size=1000)
df = pd.DataFrame({
'data': data,
'data2': data2,
'time': np.arange(1000),
'categories': categories,
'categories_2': categories_2
})
# Creating subplots
fig, axs = plt.subplots(3, 3, figsize=(15, 15))
# Plotting
# Histogram
axs[0, 0].hist(data, bins=30, color='skyblue', edgecolor='black')
axs[0, 0].set_title('直方圖 (Histogram)')
# Boxplot
sns.boxplot(data=[data, data2], ax=axs[0, 1])
axs[0, 1].set_title('箱型圖 (Box Plot)')
# Scatter Plot
axs[0, 2].scatter(data, data2, alpha=0.6)
axs[0, 2].set_title('散點(diǎn)圖 (Scatter Plot)')
# Stacked bar plot
df_group = df.groupby('categories')['data'].mean()
df_group.plot(kind='bar', stacked=True, ax=axs[1, 0])
axs[1, 0].set_title('堆積柱狀圖')
# Heatmap
sns.heatmap(df.corr(), annot=True, cmap='coolwarm', ax=axs[1, 1])
axs[1, 1].set_title('熱圖 (Heatmap)')
# Radar/Spider Chart
from math import pi
categories_list = list(df_group.index)
N = len(categories_list)
values = df_group.values.flatten().tolist()
values += values[:1]
angles = [n / float(N) * 2 * pi for n in range(N)]
angles += angles[:1]
axs[1, 2].plot(angles, values, linewidth=2, linestyle='solid')
axs[1, 2].fill(angles, values, alpha=0.4)
axs[1, 2].set_xticks(angles[:-1])
axs[1, 2].set_xticklabels(categories_list)
axs[1, 2].set_title('雷達(dá)/蜘蛛圖 (Radar/Spider Chart)')
# Time Series
axs[2, 0].plot(time_series_data)
axs[2, 0].set_title('時(shí)間序列圖')
# Violin Plot
sns.violinplot(x='categories', y='data', data=df, ax=axs[2, 1])
axs[2, 1].set_title('小提琴圖 (Violin Plot)')
# Removing the last empty subplot
fig.delaxes(axs[2, 2])
plt.tight_layout()
plt.show()
有效的數(shù)據(jù)可視化是數(shù)據(jù)科學(xué)的基石。它不僅為我們提供了一種直觀的方式來理解和解釋數(shù)據(jù),而且還為我們的分析增添了額外的價(jià)值。正如我們?cè)谏鲜龈鞣N圖形中所看到的,選擇合適的可視化方法可以幫助我們更好地解釋和傳達(dá)我們的發(fā)現(xiàn)。
參考資料:
[1] Daily Dose of Data Science. (n.d.). 250+ Python & Data Science Posts. DailyDoseofDS.com.
[2] https://github.com/shap/shap
聯(lián)系客服