在本文中,我们将涉及多个有用的 EDA 例程。然而,为了保持简短和紧凑,我们可能并不总是深入挖掘或解释所有含义。但实际上,在适当的 EDA 上花费足够的时间来充分理解您的数据集是任何优秀数据科学项目的关键部分。根据经验,您可能会将 80% 的时间用于数据准备和探索,而只有 20% 的时间用于实际机器学习建模。
结构、质量和内容的调查
总的来说,EDA 方法是非常迭代的。在你的调查结束时,你可能会发现一些需要你重新做所有事情的事情。这很正常!但是为了至少强加一点结构,我建议您使用以下结构进行调查:
- 结构调查:探索数据集的一般形状,以及特征的数据类型。
- 质量调查:了解数据集的总体质量,包括重复项、缺失值和不需要的条目。
- 内容调查:一旦了解了数据集的结构和质量,我们就可以继续对特征值进行更深入的探索,并查看不同特征之间的关系。
但首先我们需要找到一个有趣的数据集。让我们继续从OpenML加载道路安全数据集。
- from sklearn.datasets import fetch_openml
在查看我们的特征矩阵X的内容之前,我们先来看看数据集的一般结构。例如,数据集有多少列和多少行?这些功能包括多少种不同的数据类型?
- df_X.shape
- >>> (363243, 67)
- import pandas as pd
- pd.value_counts(df_X.dtypes)
- >>> float64 61
- >>> object 6
- >>> dtype: int64
数据类型可以是数字和非数字。首先,让我们仔细看看非数字条目。
即使Sex_of_Driver是一个数字特征,它也以某种方式存储为非数字特征。这有时是由于数据记录中的一些错误造成的。在数据准备期间需要注意这些事情。
一旦解决了这个问题,我们就可以使用该.describe()函数来调查每个非数字特征有多少唯一值以及最突出的值出现的频率 - 使用代码df_X.describe(exclude="number"):
1.2 数值特征的结构
接下来,让我们仔细看看数值特征。更准确地说,让我们研究一下每个特征有多少个唯一值。这个过程会给我们一些关于数量的见解二进制(2 个唯一值),序数(3到~10个唯一值)和连续(超过 10 个唯一值)数据集中的特征。
- include="number").nunique().sort_values()
- unique_values.plot.bar(logy=True, figsize=(15, 4),
- title="Unique values per feature");
1.3 结构调查结论
在第一次调查结束时,我们应该对数据集的一般结构有更好的理解。样本和特征的数量,每个特征有什么样的数据类型,有多少是二元的、有序的、分类的或连续的。对于获取此类信息的另一种方法,您还可以使用df_X.info()或df_X.describe()。
2.数据质量
在关注这些特征中存储的实际内容之前,我们先来看看数据集的总体质量。目标是对数据集有一个关于重复、缺失值和不需要的条目或记录错误等问题的全局视图。
2.1 重复数据
重复项是多次表示同一采样点的条目。例如,如果一个测量被两个不同的人注册了两次。检测这样的重复并不总是容易的,因为每个数据集可能具有唯一的标识符特征(例如,每个新样本唯一的索引号或记录时间)。所以你可能想先忽略它们。一旦你知道数据集中重复的数量,你可以简单地用.drop_duplicates().
- print(f"You seem to have {n_duplicates} duplicates in your database.")
- >>>你的数据中似乎有22个重复。
- columns_to_consider = df_X.drop(labels=["Accident_Index"], axis=1).columns
- df_X = df_X.drop_duplicates(subset=columns_to_consider)
- df_X.shape
- >>> (363221, 67)
另一个值得研究的质量问题是缺失值。有一些缺失值是正常的。在这个阶段我们要识别的是数据集中的大洞,即具有大量缺失值的样本或特征。
2.2.1。每个样本
要查看每个样本的缺失值数量,我们有多种选择。最直接的方法是简单地可视化 的输出df_X.isna(),如下所示:
- import matplotlib.pyplot as plt
- plt.figure(figsize=(10, 8))
- plt.imshow(df_X.isna(), aspect="auto", interpolation="nearest", cmap="gray")
- plt.xlabel("Column Number")
- plt.ylabel("Sample Number");
此图在 y 轴上显示 360'000 个单独样本中的每一个,在 x 轴上显示 67 个特征中的任何一个是否包含缺失值。虽然这已经是一个有用的情节,但更好的方法是使用missingno库来获得这样的情节:
- import missingno as msno
- msno.matrix(df_X, labels=True, sort="descending");
从这两个图中我们可以看到数据集有一个巨大的漏洞,这是由一些样本丢失了超过 50% 的特征值造成的。对于这些样本,用一些替换值填充缺失值可能不是一个好主意。
因此,让我们继续删除缺失值超过 20% 的样本。该阈值的灵感来自该图右侧“数据完整性”列中的信息。
- df_X = df_X.dropna(thresh=df_X.shape[1] * 0.80, axis=0).reset_index(drop=True)
- df_X.shape
- >>> (319790, 67)
作为下一步,现在让我们看看每个特征的缺失值数量。为此,我们可以使用一些pandas技巧来快速识别每个特征的缺失值比率。
- df_X.isna().mean().sort_values().plot(
- kind="bar", figsize=(15, 4),
- title="Percentage of missing values per feature",
- ylabel="Ratio of missing values per feature");
从这个图中我们可以看到大多数特征不包含任何缺失值。尽管如此,像2nd_Road_Class, Junction_Control,等特征Age_of_Vehicle仍然包含相当多的缺失值。因此,让我们继续删除缺失值超过 15% 的任何特征。
- df_X = df_X.dropna(thresh=df_X.shape[0] * 0.85, axis=1)
- df_X.shape
- >>> (319790, 60)
缺失值:删除缺失值没有严格的顺序。对于某些数据集,先处理特征然后处理样本可能会更好。此外,您决定删除每个特征或样本的缺失值的阈值因数据集而异,并且取决于您稍后打算对数据集执行的操作。
此外,到目前为止,我们只解决了数据集中的大漏洞,还没有解决如何填补较小的空白。
2.3. 不需要的条目和记录错误
数据集中质量问题的另一个来源可能是由于不需要的条目或记录错误。将此类样本与简单的异常值区分开来很重要。虽然异常值是给定特征分布不寻常的数据点,但不需要的条目或记录错误是最初不应该存在的样本。
例如,瑞士 45°C 的温度记录可能是一个异常值(如“非常不寻常”),而 90°C 的记录可能是一个错误。同样,从勃朗峰顶部记录温度可能在物理上是可能的,但很可能不应该包含在有关瑞士城市的数据集中。
当然,检测此类错误和不需要的条目并将它们与异常值区分开来并不总是直截了当,并且高度依赖于数据集。解决此问题的一种方法是对数据集进行全局查看,看看您是否可以识别出一些非常不寻常的模式。
2.3.1。数值特征
要绘制数据集的全局视图,至少对于数值特征,您可以使用 pandas 的.plot()函数并将其与以下参数结合使用:
- lw=0:lw代表线宽。0表示我们不想显示任何线条
- marker=".":我们告诉绘图.用作每个数据点的标记,而不是线条
- subplots=True:subplots告诉pandas在单独的子图中绘制每个特征
- layout=(-1, 4):此参数告诉pandas子图使用多少行和列。这-1意味着“尽可能多地”,而2意味着每行使用 2 列。
- figsize=(15, 30), markersize=1:为确保图形足够大,我们建议图形高度与特征数量大致相同,并进行markersize相应调整。
那么这个情节是什么样的呢?
- df_X.plot(lw=0,
- marker=".",
- subplots=True,
- layout=(-1, 4),
- figsize=(15, 30),
- markersize=1);
该图中的每个点都是我们数据集中的一个样本(即一行),每个子图代表一个不同的特征。y 轴显示特征值,而 x 轴是样本索引。这些图可以给你很多关于数据清理和 EDA 的想法。通常,在您对该可视化的输出感到满意之前,根据需要投入尽可能多的时间是有意义的。
2.3.2. 非数字特征
在非数字特征上识别不需要的条目或记录错误有点棘手。鉴于此时,我们只想调查数据集的一般质量。因此,我们可以做的是大致了解这些非数字特征中的每一个包含多少唯一值,以及它们最常见的类别被表示的频率。为此,您可以使用:df_X.describe(exclude=["number", "datetime])
有多种方法可以潜在地简化每个单独的非数字特征的质量调查。它们都不是完美的,所有这些都需要一些后续调查。但是为了展示一个这样的解决方案,我们可以做的是遍历所有非数字特征,并为每个特征绘制每个唯一值的出现次数。
- fig, axes = plt.subplots(ncols=1, nrows=3, figsize=(12, 8))
- df_non_numerical = df_X.select_dtypes(exclude=["number", "datetime"])
- for col, ax in zip(df_non_numerical.columns, axes.ravel()):
- df_non_numerical[col].value_counts().plot(
- logy=True, title=col, lw=0, marker=".", ax=ax)
-
- plt.tight_layout();
我们可以看到,最常见的事故(即Accident_Index),涉及的人员超过 100 人。再深入一点(即查看这次事故的个别特征),我们可以确定这起事故发生在 2015 年 2 月 24 日 11:55 在英国卡迪夫。快速的互联网搜索显示,此条目对应于幸运的非致命事故,其中包括一辆装满养老金领取者的小巴。
应该如何处理这些相当独特的条目的决定再次留在分析数据集的人的主观手中。没有任何充分的理由来解释为什么,只是为了向您展示如何 - 让我们继续从该数据集中删除 10 个最常见的事故。
- accident_ids = df_non_numerical["Accident_Index"].value_counts().head(10).index
- df_X = df_X[~df_X["Accident_Index"].isin(accident_ids)]
- df_X.shape
- >>> (317665, 60)
在第二次调查结束时,我们应该更好地了解我们数据集的总体质量。我们查看了重复项、缺失值和不需要的条目或记录错误。需要指出的是,我们尚未讨论如何解决数据集中剩余的缺失值或异常值。这是下一次调查的任务,但不会在本文中介绍。
3. 内容调查
到目前为止,我们只关注数据集的一般结构和质量。现在让我们更进一步,看看实际的内容。在理想情况下,此类调查将逐个特征进行。但是,一旦您拥有超过 20 到 30 个功能,这将变得非常麻烦。
出于这个原因(并根据需要使本文尽可能简短),我们将探索三种不同的方法,可以让您快速了解存储在每个功能中的内容以及它们之间的关系。
3.1。特征分布
查看每个特征的值分布是更好地理解数据内容的好方法。此外,它可以帮助指导您的 EDA,并提供有关数据清理和特征转换的大量有用信息。对数值特征执行此操作的最快方法是使用直方图。幸运的是,pandas它带有一个内置的直方图功能,可以一次绘制多个特征。
- df_X.hist(bins=25,figsize=(15,25),layout=(-1,5),edgcolor="black")
- plt.tight_layout();
在这个情节中可以看到很多非常有趣的事情。例如…
最常见的条目:某些功能,例如Towing_and_Articulation或Was_Vehicle_Left_Hand_Drive?主要包含仅一个类别的条目。例如,使用该.mode()函数,我们可以提取每个特征的最频繁条目的比率并将该信息可视化。
- most_frequent_entry = df_X.mode()
- df_freq = df_X.eq(most_frequent_entry.values, axis=1)
- df_freq = df_freq.mean().sort_values(ascending=False)
- display(df_freq.head())
- df_freq.plot.bar(figsize=(15, 4));
- >>> Pedestrian_Crossing-Human_Control 0.995259
- >>> Was_Vehicle_Left_Hand_Drive 0.990137
- >>> Carriageway_Hazards 0.983646
- >>> Towing_and_Articulation 0.983221
- >>> Vehicle_Location-Restricted_Lane 0.982088
- >>> dtype: float64
偏值分布:某些数值特征也可以表现出强烈的非高斯分布。在这种情况下,您可能需要考虑如何转换这些值以使它们更正态分布。例如,对于右偏数据,您可以使用对数转换。
3.2. 特征模式
列表中的下一步是研究特定于特征的模式。这部分的目标有两个:
- 我们能否识别功能中的特定模式,以帮助我们决定是否需要删除或修改某些条目?
- 我们能否确定特征之间的特定关系,以帮助我们更好地理解数据集?
在深入探讨这两个问题之前,让我们仔细看看一些“随机选择”的特征。
- df_X[["Location_Northing_OSGR",
- "1st_Road_Number",
- "Journey_Purpose_of_Driver",
- "Pedestrian_Crossing-Physical_Facilities"]].plot(
- lw=0, marker=".", subplots=True, layout=(-1, 2),
- markersize=0.1, figsize=(15, 6));
在顶行中,我们可以看到具有连续值的特征(例如,从数轴上看似任何数字),而在底行中,我们可以看到具有离散值的特征(例如,1、2、3 但不是 2.34)。
虽然我们可以通过多种方式探索特定模式的特征,但让我们通过决定将具有少于 25 个独特特征的特征视为离散或有序特征,将其他特征视为连续特征来简化我们的选择。
- cols_continuous = df_X.select_dtypes(include="number").nunique() >= 25
现在我们有了选择连续特征的方法,让我们继续使用 seabornpairplot来可视化这些特征之间的关系。需要注意的是,seaborn 的 pairplot 例程可能需要很长时间才能创建所有子图。因此,我们建议一次不要将其用于超过 10 个功能。
鉴于在我们的例子中我们只有 11 个特征,我们可以继续使用 pairplot。否则,使用类似的东西df_continuous.iloc[:, :5]可能有助于减少要绘制的特征数量。
左上角的几个特征之间似乎有一种奇怪的关系。Location_Easting_OSGR和Longitude, 以及Location_Easting_OSGR和Latitude似乎有很强的线性关系。
- sns.pairplot(
- df_X,
- plot_kws={"s": 3, "alpha": 0.2},
- hue="Police_Force",
- palette="Spectral",
- x_vars=["Location_Easting_OSGR", "Location_Northing_OSGR", "Longitude"],
- y_vars="Latitude");
知道这些特征包含地理信息,关于地理定位的更深入的 EDA 可能是富有成效的。然而,现在我们将把对这个配对图的进一步研究留给好奇的读者,并继续探索离散和有序特征。
3.2.2. 离散和有序特征
在离散或有序特征中寻找模式有点棘手。但也在这里,一些快速的 pandas 和 seaborn 技巧可以帮助我们大致了解我们的数据集。首先,让我们选择要调查的列。
- df_discrete = df_x[cols_countinous[~cols_continous].index]
请注意,要将值沿 y 轴方向展开,我们需要选择一个特定的(希望能提供信息)特征。虽然“正确”特征可以帮助识别一些有趣的模式,但通常任何连续特征都可以解决问题。这种图的主要兴趣是查看每个离散值包含多少样本。