DropMissingData#

从数据集中移除带有 nan 值的行是数据科学和机器学习项目中的常见做法。

您可能对使用 pandas dropna 很熟悉。基本上,您会取一个 pandas 数据框或 pandas 系列,应用 dropna,并消除那些在一列或多列中包含 nan 值的行。

这里,我们有一个该语法的示例:

import numpy as np
import pandas as pd

X = pd.DataFrame(dict(
       x1 = [np.nan,1,1,0,np.nan],
       x2 = ["a", np.nan, "b", np.nan, "a"],
       ))

X.dropna(inplace=True)
print(X)

之前的代码返回一个没有缺失值的数据框:

    x1 x2
2  1.0  b

Feature-engine 的 DropMissingData() 在转换器中封装了 pandas 的 dropna,该转换器将在遵循 scikit-learn 的 fittransform 功能的同时,删除包含 na 值的行。

这里我们有一个 DropMissingData() 语法的快照:

import pandas as pd
import numpy as np
from feature_engine.imputation import DropMissingData

X = pd.DataFrame(dict(
       x1 = [np.nan,1,1,0,np.nan],
       x2 = ["a", np.nan, "b", np.nan, "a"],
       ))

dmd = DropMissingData()
dmd.fit(X)
dmd.transform(X)

之前的代码返回一个没有缺失值的数据框:

    x1 x2
2  1.0  b

DropMissingData() 因此允许你在任何 scikit-learn 特征工程工作流程中移除空值。

DropMissingData#

DropMissingData() 相对于 pandas 有一些优势:

  • 它学习和存储应删除包含 nan 值的行的变量。

  • 它可以在类似 Scikit-learn 的管道中使用。

使用 DropMissingData(),你可以从数值和分类变量中删除 nan 值。换句话说,你可以从数值、分类或对象数据类型中移除空值。

您可以选择从所有列中删除 nan 值,或者仅从其中一部分列中删除。另外,如果某行包含超过一定百分比的 nan 值,您也可以选择删除该行。

让我们通过代码示例更好地说明 DropMissingData() 的功能。

Dropna#

让我们从导入 pandas 和 numpy 开始,并创建一个包含两列 nan 值的玩具数据框:

import numpy as np
import pandas as pd

from feature_engine.imputation import DropMissingData

X = pd.DataFrame(
    dict(
        x1=[2, 1, 1, 0, np.nan],
        x2=["a", np.nan, "b", np.nan, "a"],
        x3=[2, 3, 4, 5, 5],
    )
)
y = pd.Series([1, 2, 3, 4, 5])

print(X.head())

下面我们看到新的数据框:

    x1   x2  x3
0  2.0    a   2
1  1.0  NaN   3
2  1.0    b   4
3  0.0  NaN   5
4  NaN    a   5

我们可以如下所示删除所有列中的 nan 值:

dmd =  DropMissingData()
Xt = dmd.fit_transform(X)
Xt.head()

我们看到转换后的数据框没有空值:

    x1 x2  x3
0  2.0  a   2
2  1.0  b   4

默认情况下,DropMissingData() 会在拟合过程中找到并存储在训练集中存在缺失数据的列。它们存储在这里:

dmd.variables_
['x1', 'x2']

这意味着每次我们对一个新的数据框应用 transform() 时,转换器将仅在那些列中删除带有 nan 值的行。

如果我们想强制 DropMissingData() 删除所有列中的缺失值,无论它们在拟合期间是否包含 nan 值,我们需要这样设置类:

dmd =  DropMissingData(missing_only=False)
Xt = dmd.fit_transform(X)

现在,当我们探索参数 variables_ 时,我们看到训练集中的所有变量都被存储,因此,将被用来移除 nan 值:

dmd.variables_
['x1', 'x2', 'x3']

在dropna后调整目标#

DropMissingData() 有一个选项可以从训练集和目标变量中移除包含 nan 的行。这样,我们可以获得一个与转换后的数据框对齐的目标。

方法 transform_x_y 从训练集中移除含有空值的行,然后重新对齐目标。让我们来看一下:

Xt, yt = dmd.transform_x_y(X, y)
Xt

下面我们看到没有 nan 的数据框:

    x1 x2  x3
0  2.0  a   2
2  1.0  b   4
yt

在这里我们看到目标,这些行对应于转换后的数据框中的剩余行:

0    1
2    3
dtype: int64

让我们检查转换后的数据框和目标的形状是否相同:

Xt.shape, yt.shape

我们看到生成的训练集和目标各有2行,而不是原来的5行。

((2, 3), (2,))

返回包含 nan 的行#

当我们在生产环境中有一个模型时,了解哪些行被转换器删除可能是有用的。我们可以按如下方式获取该信息:

dmd.return_na_data(X)

上一个命令返回包含 nan 的行。换句话说,它与 transform() 或 pandas.dropna 相反。

    x1   x2  x3
1  1.0  NaN   3
3  0.0  NaN   5
4  NaN    a   5

从变量子集中删除缺失值#

我们可以选择仅从特定列或一组列中删除缺失数据。我们只需要将列名或列名传递给 variables 参数:

在这里,我们将从变量“x1”,“x3”中删除缺失值。

dmd = DropMissingData(variables=["x1", "x3"], missing_only=False)
Xt = dmd.fit_transform(X)
Xt.head()

下面,我们看到转换后的数据框。它删除了在“x1”中包含nan的行,我们看到那些在“x2”中包含nan的行仍然在数据框中:

    x1   x2  x3
0  2.0    a   2
1  1.0  NaN   3
2  1.0    b   4
3  0.0  NaN   5

只有“x1”和“x3”中包含nan的行被移除。我们可以通过检查 variables_ 参数来证实这一点:

重要

当你指定哪些变量应该被检查以删除包含 nan 的行时,请确保将参数 missing_only 设置为布尔值 False。否则,DropMissingData() 将仅从你的列表中选择那些在训练集中显示了 nan 值的变量。

例如,看看当我们这样设置类时会发生什么:

dmd = DropMissingData(variables=["x1", "x3"], missing_only=True)
Xt = dmd.fit_transform(X)
dmd.variables_

注意,我们指示要从“x1”和“x3”中移除nan。然而,只有“x1”在X中有nan。因此,转换器学习到应该只从“x1”中移除nan:

['x1']

DropMissingData() 采用了列表中指示的2个变量,并且在拟合过程中仅存储了显示为nan的变量。这意味着在转换未来的数据框时,它将仅删除在“x1”中包含nan的行。

换句话说,如果你传递一个变量列表进行插补并设置 missing_only=True,而你列表中的一些变量在训练集中没有缺失数据,那么在转换过程中,这些特定变量的缺失数据将不会被移除。

missing_only=True 时,转换器会“再次检查”输入的变量在训练集中是否有缺失数据。如果没有,则在 transform() 过程中忽略它们。

在未传递变量列表进行插补时,建议使用 missing_only=True

基于非NaN值百分比的dropna#

我们可以设置 DropMissingData() 以要求一行中保留一定百分比的非NA值。我们可以通过 threshold 参数来控制这种行为,该参数等同于 pandas.dropna 的 thresh 参数。

如果 threshold=1,所有变量都需要有数据才能保留一行。如果 threshold=0.5,50% 的变量需要有数据才能保留一行。如果 threshold=0.01,10% 的变量需要有数据才能保留一行。如果 threshold=None,任何变量中有 NA 的行都将被删除。

让我们通过一个例子来看看。我们创建一个新的数据框,其中每一行都有不同比例的非nan值。

X = pd.DataFrame(
    dict(
        x1=[2, 1, 1, np.nan, np.nan],
        x2=["a", np.nan, "b", np.nan, np.nan],
        x3=[2, 3, 4, 5, np.nan],
    )
)
X

我们看到底部行在所有列中都有 nan,第 3 行在 3 列中有 2 列是 nan,而第 1 行在 1 个变量中有 nan:

    x1   x2   x3
0  2.0    a  2.0
1  1.0  NaN  3.0
2  1.0    b  4.0
3  NaN  NaN  5.0
4  NaN  NaN  NaN

现在,我们可以设置 DropMissingData() 来删除其值超过50%为nan的行:

dmd = DropMissingData(threshold=.5)
dmd.fit(X)
dmd.transform(X)

我们看到最后两行被丢弃了,因为它们超过50%的值是nan。

    x1   x2   x3
0  2.0    a  2.0
1  1.0  NaN  3.0
2  1.0    b  4.0

相反,我们可以设置 class:DropMissingData() 来删除其值超过70%为nan的行,如下所示:

dmd = DropMissingData(threshold=.3)
dmd.fit(X)
dmd.transform(X)

现在我们看到只有最后一行被移除了。

    x1   x2   x3
0  2.0    a  2.0
1  1.0  NaN  3.0
2  1.0    b  4.0
3  NaN  NaN  5.0

Scikit-learn 兼容#

DropMissingData() 完全兼容 Scikit-learn API,因此你会发现一些在 Scikit-learn 转换器中常见的的方法,例如,get_feature_names_out() 方法用于获取转换后的数据框中的变量名称。

管道#

当我们从数据框中删除缺失值后,我们需要重新对齐目标。我们之前看到可以通过使用 transform_x_y 方法来实现这一点。

我们也可以在管道内自动将目标与生成的数据框对齐,通过利用 Feature-engine 的管道。

首先,让我们导入必要的库:

import numpy as np
import pandas as pd

from feature_engine.imputation import DropMissingData
from feature_engine.encoding import OrdinalEncoder
from feature_engine.pipeline import Pipeline

让我们创建一个新的数据框,其中某些行包含 nan 值,两个数值变量和一个分类变量,以及其对应的目标变量:

X = pd.DataFrame(
    dict(
        x1=[2, 1, 1, 0, np.nan],
        x2=["a", np.nan, "b", np.nan, "a"],
        x3=[2, 3, 4, 5, 5],
    )
)
y = pd.Series([1, 2, 3, 4, 5])

X.head()

下面,我们看到了生成的数据框:

    x1   x2  x3
0  2.0    a   2
1  1.0  NaN   3
2  1.0    b   4
3  0.0  NaN   5
4  NaN    a   5

现在让我们设置一个管道,首先删除缺失值,然后使用序数编码对分类变量进行编码:

pipe = Pipeline(
    [
        ("drop", DropMissingData()),
        ("enc", OrdinalEncoder(encoding_method="arbitrary")),
    ]
)

pipe.fit_transform(X, y)

当我们应用 fittransformfit_transform 时,我们只会得到转换后的训练集:

    x1  x2  x3
0  2.0   0   2
2  1.0   1   4

为了获取变换训练集和目标,我们使用 transform_x_y

pipe.fit(X,y)
Xt, yt = pipe.transform_x_y(X, y)
Xt

这里我们看到转换后的训练集:

    x1  x2  x3
0  2.0   0   2
2  1.0   1   4
yt

在这里我们看到重新对齐的目标变量:

0    1
2    3

最后,让我们在管道中添加一个估计器:

import numpy as np
import pandas as pd

from sklearn.linear_model import Lasso

from feature_engine.imputation import DropMissingData
from feature_engine.encoding import OrdinalEncoder
from feature_engine.pipeline import Pipeline

df = pd.DataFrame(
    dict(
        x1=[2, 1, 1, 0, np.nan],
        x2=["a", np.nan, "b", np.nan, "a"],
        x3=[2, 3, 4, 5, 5],
    )
)
y = pd.Series([1, 2, 3, 4, 5])

pipe = Pipeline(
    [
        ("drop", DropMissingData()),
        ("enc", OrdinalEncoder(encoding_method="arbitrary")),
        ("lasso", Lasso(random_state=2))
    ]
)

pipe.fit(df, y)
pipe.predict(df)
array([2., 2.])

Dropna 还是 fillna?#

DropMissingData() 具有与 pandas.series.dropnapandas.dataframe.dropna 相同的功能。如果你想使用与 pandas.fillna 兼容的功能,请查看我们的其他插补转换器。

删除包含 nan 的列#

目前,Feature-engine 没有能够找到缺失值达到一定百分比的列并自动删除它们的转换器。相反,你可以手动找到这些列,然后借助选择模块中的 DropFeatures 来删除它们。

另见#

查看我们关于 LagFeaturesWindowFeatures 的教程,了解如何将 DropMissingData() 与滞后或滚动窗口结合使用,以创建用于预测的特征。

教程、书籍和课程#

在以下Jupyter笔记本中,在我们的配套Github仓库中,您将找到更多使用 DropMissingData() 的示例。

有关此功能和其他特征工程方法的教程,请查看我们的在线课程:

../../_images/feml.png

机器学习的特征工程#











或者阅读我们的书:

../../_images/cookbook.png

Python 特征工程手册#














我们的书籍和课程都适合初学者和更高级的数据科学家。通过购买它们,您正在支持 Feature-engine 的主要开发者 Sole。