使用scikit-learn的OneHotEncoder处理分类数据中的缺失值(NaN)的方法是什么?

22

我最近开始学习Python,使用机器学习方法为研究项目开发预测模型。我有一个由数值和分类数据组成的大型数据集,其中有很多缺失值。目前我正在尝试使用OneHotEncoder对分类特征进行编码。当我阅读关于OneHotEncoder的资料时,我的理解是对于缺失值(NaN),OneHotEncoder会将该特征的所有类别都赋值为0,如下所示:

0     Male 
1     Female
2     NaN

应用OneHotEncoder之后:

0     10 
1     01
2     00

然而,当运行以下代码时:

    # Encoding categorical data
    from sklearn.compose import ColumnTransformer
    from sklearn.preprocessing import OneHotEncoder


    ct = ColumnTransformer([('encoder', OneHotEncoder(handle_unknown='ignore'), [1])],
                           remainder='passthrough')
    obj_df = np.array(ct.fit_transform(obj_df))
    print(obj_df)

我遇到了错误ValueError: Input contains NaN

因此,我猜测我之前对OneHotEncoder处理缺失值的理解是错误的。有没有办法让我获得上述功能?我知道在编码之前填充缺失值可以解决这个问题,但我不愿意这样做,因为我正在处理医疗数据,并担心填充可能会降低模型的预测准确性。

我找到了这个问题,它很相似,但答案没有提供足够详细的解决方案来处理NaN值。

请让我知道你的想法,谢谢。


1
请关注 https://github.com/scikit-learn/scikit-learn/issues/11996。 - Ben Reiniger
谢谢@BenReiniger,很高兴知道开发人员们同意OneHotEncoder应该处理缺失值! :) - sums22
7个回答

14

在进行OneHot编码之前,您需要填补缺失的值。您可以使用Pipeline定义一个填补步骤,使用SimpleImputer设置constant策略来为空字段输入一个新分类:

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
import numpy as np

categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('encoder', OneHotEncoder(handle_unknown='ignore'))])

preprocessor = ColumnTransformer(
    transformers=[
        ('cat', categorical_transformer, [0])
    ])

df = pd.DataFrame(['Male', 'Female', np.nan])
preprocessor.fit_transform(df)
array([[0., 1., 0.],
       [1., 0., 0.],
       [0., 0., 1.]])

感谢您的回答和有用的代码。您认为在列中使用最频繁的值进行输入不会影响模型的预测准确性吗? - sums22
1
这是一种针对分类数据相当常见的策略。虽然并不理想,但毕竟你确实有缺失数据。不过,在缺失数据不太多的情况下,这是一个合理的方法。@sums - yatu
这是我一直在寻找替代方案,看是否能够避免过多地操纵数据。不幸的是,我的数据缺失相当严重。也许我不得不采用他的方法。感谢您的建议。 - sums22
3
我建议使用strategy='constant'fill_value='missing'来保留类别结构,而不是使用最频繁值进行填补。 - Ben Reiniger
1
@BenReiniger 这是否意味着在编码后我将有一个额外的类别,即“缺失”类别?如果是这样,您建议我是否应该删除所有已编码特征中的“缺失”类别(类似于Om Rastogi发布的答案)还是保留它? - sums22
1
@sums22,这取决于您后续的建模。个人认为:如果您需要避免多重共线性,请舍弃它;否则,请保留它。 - Ben Reiniger

7
从版本0.24开始,OneHotEncoder现在将缺失值视为其自己的类别。详情请查看What's New entry

5
  1. NaN值替换为“Others”
  2. 然后进行独热编码。
  3. 接着可以删除“Others”列。

2
你的意思是使用pandas.DataFrame.fillna将NaN替换为字符串"Others"吗? - sums22
是的,请使用fillna函数。 - Om Rastogi
1
我不会使用“其他”,因为这是性别,在医学上,最可能应该是“男”或“女”。除非我们知道男人是跨性别者等情况,否则“其他”没有太多意义。 - RFAI
“other” 最终应该被移除。如果您担心命名,可以使用标签“NotMentioned”。 - Om Rastogi

1

这里有一个选项可以使用pandas的get_dummies()函数,文档在这里dummy_na参数可以被改变以包括NaN作为一个单独的类别。根据您所需的解决方案,看起来默认值就可以。

obj_df_encoded = pd.get_dummies(obj_df)
print(obj_df)
>> 1 0
>> 0 1
>> 0 0

如果需要使用Scikit-Learn的One Hot编码器,您可以使用pandas的filna('something')填充nan值,将其作为新类别进行One Hot编码,然后删除此列。

1

实际上,我也在寻找同样的东西,并找到了回答你问题的答案,但除了 @Om Rastogi 的回答外,其他答案对我帮助不大。但后来,我找到了一种更简单的方法,所以我在这里发布了两种方法。

1. 长篇方法:假设我有一个包含NaN值的数据框。

new_df.fillna("Others",inplace=True,axis = 1)

new_df = pd.get_dummies(new_df, columns=cat_col)

col_to_drop = [col for col in new_df.columns.tolist() if col.__contains__("Others")]

new_df.drop(col_to_drop, axis=1,inplace=True)

只需添加一个参数,就能节省很多工作。
cat_col = data.dtypes[data.dtypes == 'O'].index.tolist() #to get the list of categorical variables

new_df = pd.get_dummies(new_df, columns=cat_col, drop_first=True,dummy_na=True)

希望你对我的回答满意,你可以给这个回答点赞。谢谢。

0

OneHotEncoder将缺失值添加为新列。您可以通过手动设置类别(如下所示)或使用OneHotEncoder的“drop”参数来防止创建这个可能无用的列。此编码器将为您提供所示的输出:

enc = OneHotEncoder(categories = [[0, 1]], handle_unknown='ignore')

-1
就我长时间处理这个问题的经验来看,问题在于训练测试集的分割中有一些列对于所有数据样本都具有相同的值。如果您保存的数据行彼此更相似,则更有可能发生这种情况。在这种情况下,洗牌数据可以帮助解决问题。这似乎是scikit的一个错误。我正在使用版本0.23.2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接