机器学习中最酷的9幅图
在数据科学领域,数据可视化作为一个核心工具,为我们提供了深入了解、探索和解释复杂数据集的能力。这不仅帮助我们清晰地呈现数据的特点,而且还允许非专家用户快速理解数据的关键趋势和模式。本文将介绍一系列精选的数据可视化技巧,这些技巧在实践中都被证明是非常有价值的。从KS图到小提琴图,每一种都有其独特的应用场景和优势。
KS 图
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()
这是一个KS图,它表示了数据的累积分布函数(CDF)与理论正态分布的CDF。在这个图中:
蓝色实线代表我们的数据CDF。黄色虚线代表理论的正态分布CDF。KS统计量表示这两个分布之间的最大差异。在这个例子中,KS统计量约为0.03,表示两个CDF之间的最大差异是0.03。
这种图形在检验数据是否符合某种理论分布时非常有用,如正态分布。
SHAP
由于SHAP图需要一个预训练的模型和特定的数据,我会简化这个过程并使用一个标准数据集和XGBoost模型进行演示。
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])
上述解释展示了每个特征如何推动模型的输出从基值(我们传递的训练数据集上的平均模型输出)到模型输出。推动预测更高的特征显示为红色,推动预测更低的特征显示为蓝色。
如果我们取很多像上面那样的力量图解释,将它们旋转90度,然后水平堆叠,我们可以看到整个数据集的解释
# 可视化所有的训练集预测
shap.plots.force(shap_values)
为了理解单一特征如何影响模型的输出,我们可以绘制该特征的SHAP值与数据集中所有示例的特征值。由于SHAP值代表特征对模型输出变化的责任,下面的图表示随着RM(一个地区的房屋平均房间数)的变化,预测的房价发生的变化。RM的单一值的垂直分散表示与其他特征的交互效应。为了帮助揭示这些交互作用,我们可以用另一个特征进行着色。如果我们将整个解释张量传递给颜色参数,散点图将选择最佳的颜色特征。在这种情况下,它选择了RAD(到放射状高速公路的可达性指数),因为这突出了对于具有高RAD值的地区,房屋的平均房间数对房价的影响较小。
# 创建一个依赖散点图,显示整个数据集中单一特征的效果
shap.plots.scatter(shap_values[:,"RM"], color=shap_values)
为了获得哪些特征对模型最重要的概览,我们可以绘制每个样本的每个特征的SHAP值。下面的图按照所有样本的SHAP值大小对特征进行排序,并使用SHAP值显示每个特征对模型输出的影响的分布。颜色代表特征值(红色高,蓝色低)。这揭示了例如高LSTAT(%较低的人口状态)降低了预测的房价。
# 总结所有特征的影响
shap.plots.beeswarm(shap_values)
Q-Q图
# 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()
这是一个QQ图,用于比较随机生成的正态分布数据与理论正态分布。在这个图中:
X轴表示理论正态分布的分位数。Y轴表示观察到的数据的分位数。如果数据完全遵循正态分布,那么点会紧密地围绕红色的参考线。从这个图中我们可以看到,数据点大致围绕参考线,这意味着我们的随机数据与正态分布非常接近。
累计方差图
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()
这是一个累积解释方差图,它展示了当我们使用主成分分析(PCA)进行降维时,每个主成分解释的方差的累积总和。
在图中:
X轴表示主成分的数量。Y轴表示对应数量的主成分所解释的累积方差的比例。例如,前5个主成分大致解释了近80%的方差。这意味着如果我们只使用前5个主成分来代表数据,我们将能够保留原始数据的大约80%的信息。
这种图形在决定PCA中要保留的主成分数量时非常有用,因为它可以帮助我们平衡降维与保留信息之间的权衡。
Gini不纯度 vs 熵
Gini不纯度 vs. 摘的图通常用于比较决策树分割的不纯度或混乱度。这两种度量方法都可以用 作决策树算法中的分裂标准。在不同的情境下,一个方法可能比另一个方法更优。
Gini不纯度:当一个节点是纯净的 (即,它只包含一个类的样本) 时,Gini不纯度为 0 。其最 大值取决于类的数量,但对于二分类问题,最大值为 0.5 。
桷:当一个节点是纯净的时,摘为 0 。与Gini不纯度一样,其最大值取决于头的数量。对于二 分夹问题,最大值为 1 。
为了可视化这两个度量方法,我们可以为给定的概率值计算Gini不纯度和摘,并将它们绘制在 同一张图上。这将帮助我们理解两者在不同概率值下的行为。让我们来彸制这个图。
# 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()
这是一个Gini不纯度 vs. 熵的图。在此图中:
X轴表示某一类的概率值。蓝色实线表示Gini不纯度。红色虚线表示熵。从图中可以看出:
当概率为0或1时(也就是说,节点是纯净的),Gini不纯度和熵都为0。对于二分类问题,当概率为0.5时(即,两个类的样本数量相等),Gini不纯度和熵都达到最大值。这两种度量方法都用于度量决策树节点的不纯度或混乱度。选择哪一个作为分裂标准取决于具体的应用和数据。
偏差-方差权衡图
偏差-方差权衡是监督学习中的一个核心概念,它解释了模型的总误差是如何由三个不同的误差类型组成的:
偏差(Bias):描述了模型预测的平均值与真实值之间的差异。高偏差意味着模型可能欠拟合数据。方差(Variance):描述了模型预测对于不同训练集的变化程度。高方差意味着模型可能过拟合数据。不可减少的误差:描述了即使在理想条件下,由于数据中的噪声导致的误差。
在实际应用中,我们通常会看到一个权衡关系:随着模型复杂度的增加,偏差会减少,但方差会增加,反之亦然。因此,我们的目标是找到一个平衡点,使得总误差最小化。
为了直观地展示这个权衡关系,我会绘制一个理论图,显示偏差、方差和总误差随模型复杂度的变化情况。
# 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()
这是模拟的偏差-方差权衡图。
在此图中:
X轴表示模型的复杂度。随着模型复杂度的增加,我们期望偏差降低但方差增加。蓝色曲线表示偏差的平方。可以看到,随着模型复杂度的增加,偏差的平方降低。红色曲线表示方差。随着模型复杂度的增加,方差增加。绿色曲线表示总误差,它是偏差的平方、方差和不可避免的误差之和。可以看到,总误差在某个模型复杂度点达到最小值。这是我们希望找到的理想的模型复杂度。通过这种可视化,我们可以更好地理解如何选择合适的模型复杂度,以平衡偏差和方差并最小化总误差。
ROC曲线
ROC曲线 (Receiver Operating Characteristic Curve)是用于评估分类模型性能的图。它显示了真正例率 (True Positive Rate, TPR) 和假正例率 (False Positive Rate, FPR) 之间的权衡关系,随着分类阈值的变化。
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数据集上训练的多类逻辑回归模型的ROC曲线。
在此图中:
X轴表示假正例率 (False Positive Rate, FPR)。Y轴表示真正例率 (True Positive Rate, TPR)。三种颜色(蓝色、红色和绿色)代表iris数据集的三个类别:setosa、versicolor和virginica。黑色虚线表示完全随机分类器的表现。每条曲线下的面积(AUC)表示模型对应类别的性能,值越接近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数据集上训练的多类逻辑回归模型的精确率-召回率曲线。
在此图中:
X轴表示召回率 (Recall)。Y轴表示精确率 (Precision)。三种颜色(蓝色、红色和绿色)代表iris数据集的三个类别:setosa、versicolor和virginica。每条曲线的平均精确率(average precision)表示模型对应类别的性能,值越接近1,表示模型的性能越好。与ROC曲线不同,精确率-召回率曲线特别适合处理类别不平衡的数据集。这是因为它专注于正类的预测,而不是整体的表现。
Elbow Curve
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数据集上的Elbow曲线。
在此图中:
X轴表示考虑的簇数(从1到10)。Y轴表示每个簇数对应的within-cluster sum of squares (WCSS)。WCSS是簇内所有点到簇中心的平方距离之和。“肘部”是曲线的一个关键点,表示增加更多的簇不会显著降低WCSS。在这个点,我们可以选择簇的最佳数量。从图中可以看出,肘部大约在2或3的位置,这意味着选择2或3个簇可能是最佳选择。
其他
以下是一些常见和有用的数据可视化技巧和方法:
直方图 (Histograms): 用于显示数值变量的分布。可以帮助识别数据中的模式,如正态性、偏斜等。
箱型图 (Box Plots): 用于显示数据的五数概括:最小值、第一四分位数、中位数、第三四分位数和最大值。可以帮助识别离群值。
散点图 (Scatter Plots): 用于显示两个数值变量之间的关系。可以帮助识别相关性、数据的集群等。
堆积柱状图和堆积面积图: 用于显示多个类别或时间序列数据的部分到整体的关系。
热图 (Heatmaps): 用于显示两个类别变量之间关系的强度或数值变量的密度。
雷达/蜘蛛图 (Radar/Spider Charts): 用于显示多个变量的一个实体或类别的值。常用于性能分析或比较多个实体。
地图可视化: 用于显示地理数据,如国家、城市或地理坐标上的数据点。
时间序列图: 用于显示随时间变化的数据。
小提琴图 (Violin Plots): 结合了箱型图和密度图的特点,显示数据的分布和概率密度。
配对图 (Pair Plots): 用于显示多个数值变量之间的所有可能的散点图。
树状图 (Tree Maps): 用于显示层次数据或部分到整体的关系。
圆环图 (Donut Charts): 一个饼图的变体,用于显示类别数据的比例。
词云 (Word Clouds): 用于显示文本数据中词的频率。最常见的词以较大的字体显示。
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("散点图 (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("雷达/蜘蛛图 (Radar/Spider Chart)")
# Time Series
axs[2, 0].plot(time_series_data)
axs[2, 0].set_title("时间序列图")
# 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()
有效的数据可视化是数据科学的基石。它不仅为我们提供了一种直观的方式来理解和解释数据,而且还为我们的分析增添了额外的价值。正如我们在上述各种图形中所看到的,选择合适的可视化方法可以帮助我们更好地解释和传达我们的发现。
参考资料:
[1] Daily Dose of Data Science. (n.d.). 250+ Python & Data Science Posts. DailyDoseofDS.com.
[2] https://github.com/shap/shap