将缺失值保留为“NaN”的 LabelEncoder

Nas*_*sri 6 python dataframe pandas

我正在尝试使用标签编码器将分类数据转换为数值。

我需要一个 LabelEncoder 将我的缺失值保留为 'NaN' 以便之后使用 Imputer。所以我想在像这样标记后使用掩码来替换原始数据框

df = pd.DataFrame({'A': ['x', np.NaN, 'z'], 'B': [1, 6, 9], 'C': [2, 1, np.NaN]})


    A   B   C
0   x   1   2.0
1   NaN 6   1.0
2   z   9   NaN


dfTmp = df
mask = dfTmp.isnull()

       A    B   C
0   False   False   False
1   True    False   False
2   False   False   True
Run Code Online (Sandbox Code Playgroud)

所以我得到一个带有真/假值的数据框

然后,在创建编码器中:

df = df.astype(str).apply(LabelEncoder().fit_transform)
Run Code Online (Sandbox Code Playgroud)

我该如何继续,以便对这些值进行编码?

谢谢

Mik*_*nov 10

第一个问题是:您希望单独对每一列进行编码还是使用一种编码对它们全部进行编码?

该表达式df = df.astype(str).apply(LabelEncoder().fit_transform)意味着您分别对所有列进行编码。

That case you can do the following:
df = df.apply(lambda series: pd.Series(
    LabelEncoder().fit_transform(series[series.notnull()]),
    index=series[series.notnull()].index
))
print(df)
Out:
     A  B    C
0  0.0  0  1.0
1  NaN  1  0.0
2  1.0  2  NaN
Run Code Online (Sandbox Code Playgroud)

下面解释它是如何工作的。但是,对于初学者,我将介绍此解决方案的几个缺点。

缺点
首先,列有混合类型:如果列包含NaN值,则列具有类型float,因为 nan 在 python 中是浮点数。

df.dtypes
A    float64
B      int64
C    float64
dtype: object
Run Code Online (Sandbox Code Playgroud)

对于标签来说,这似乎毫无意义。好的,稍后您可以忽略所有 nan 并将其余部分转换为整数。

第二点是:可能你需要记住一个LabelEncoder- 因为通常需要做,例如,逆变换。但是这个解决方案不会记住编码器,你没有这样的变量。

一个简单、明确的解决方案是:

encoders = dict()

for col_name in df.columns:
    series = df[col_name]
    label_encoder = LabelEncoder()
    df[col_name] = pd.Series(
        label_encoder.fit_transform(series[series.notnull()]),
        index=series[series.notnull()].index
    )
    encoders[col_name] = label_encoder

print(df)
Out:
     A  B    C
0  0.0  0  1.0
1  NaN  1  0.0
2  1.0  2  NaN
Run Code Online (Sandbox Code Playgroud)

- 更多的代码,但结果是一样的

print(encoders)
Out
{'A': LabelEncoder(), 'B': LabelEncoder(), 'C': LabelEncoder()}
Run Code Online (Sandbox Code Playgroud)

- 此外,还提供编码器。逆变换(之前应该删除 nan!):

encoders['B'].inverse_transform(df['B'])
Out:
array([1, 6, 9])
Run Code Online (Sandbox Code Playgroud)

此外,一些选项,如一些用于编码器的注册表超类也可用,它们与第一个解决方案兼容,但更容易遍历列。

这个怎么运作

df.apply(lambda series: ...)适用的返回一个函数pd.Series到每个列; 因此,它返回一个具有新值的数据帧。

分步表达:

pd.Series(
    LabelEncoder().fit_transform(series[series.notnull()]),
    index=series[series.notnull()].index
)
Run Code Online (Sandbox Code Playgroud)

-series[series.notnull()]删除NaN值,然后将其余部分提供给fit_transform.

- 当标签编码器返回 anumpy.array并抛出一个索引时,index=series[series.notnull()].index恢复它以正确连接它。如果不做索引:

print(df)
Out:
     A  B    C
0    x  1  2.0
1  NaN  6  1.0
2    z  9  NaN
df = df.apply(lambda series: pd.Series(
    LabelEncoder().fit_transform(series[series.notnull()]),
))
print(df)
Out:
     A  B    C
0  0.0  0  1.0
1  1.0  1  0.0
2  NaN  2  NaN
Run Code Online (Sandbox Code Playgroud)

- 值从正确的位置偏移 - 甚至IndexError可能发生。

所有列的单个编码器

这种情况下,堆叠数据帧,适合编码器,然后将其拆开

series_stack = df.stack().astype(str)
label_encoder = LabelEncoder()
df = pd.Series(
    label_encoder.fit_transform(series_stack),
    index=series_stack.index
).unstack()
print(df)
Out:
     A    B    C
0  5.0  0.0  2.0
1  NaN  3.0  1.0
2  6.0  4.0  NaN
Run Code Online (Sandbox Code Playgroud)

-为series_stackpd.Series包含NaN的,从数据帧的所有值是浮动,所以你可能更愿意将其转换。

希望能帮助到你。

  • 我很乐意提供帮助:) (2认同)