scikit-learn 中跨多个列的标签编码
Posted
技术标签:
【中文标题】scikit-learn 中跨多个列的标签编码【英文标题】:Label encoding across multiple columns in scikit-learn 【发布时间】:2018-08-01 14:53:02 【问题描述】:我正在尝试使用 scikit-learn 的 LabelEncoder
对字符串标签的 pandas DataFrame
进行编码。由于数据框有很多(50+)列,我想避免为每列创建一个LabelEncoder
对象;我宁愿只拥有一个大的LabelEncoder
对象,它适用于所有我的数据列。
将整个 DataFrame
放入 LabelEncoder
会产生以下错误。请记住,我在这里使用的是虚拟数据;实际上,我正在处理大约 50 列字符串标记的数据,因此需要一个不按名称引用任何列的解决方案。
import pandas
from sklearn import preprocessing
df = pandas.DataFrame(
'pets': ['cat', 'dog', 'cat', 'monkey', 'dog', 'dog'],
'owner': ['Champ', 'Ron', 'Brick', 'Champ', 'Veronica', 'Ron'],
'location': ['San_Diego', 'New_York', 'New_York', 'San_Diego', 'San_Diego',
'New_York']
)
le = preprocessing.LabelEncoder()
le.fit(df)
Traceback(最近一次调用最后一次): 文件“”,第 1 行,在 文件“/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/preprocessing/label.py”,第 103 行,适合 y = column_or_1d(y, 警告=真) 文件“/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py”,第 306 行,在 column_or_1d raise ValueError("bad input shape 0".format(shape)) ValueError: bad input shape (6, 3)
关于如何解决这个问题的任何想法?
【问题讨论】:
你为什么要这样做? 简化对字符串数据的多列dataframe
的编码。我正在挑选编码对象,因此希望避免腌制/解开 50 个单独的对象。另外,我想知道是否有一种方法可以让编码器简化数据,即只为每列中的每个唯一变量组合返回一个带有标识符的行。
有一种简单的方法可以在 pandas 中完成这一切,方法是将字典字典传递给 replace
方法。 See this answer below
从scikit-learn 0.20
开始,无需实现自定义类来label encode 多个列。你可以简单地使用OrdinalEncoder
【参考方案1】:
不过,您可以轻松做到这一点,
df.apply(LabelEncoder().fit_transform)
EDIT2:
在 scikit-learn 0.20 中,推荐的方式是
OneHotEncoder().fit_transform(df)
因为 OneHotEncoder 现在支持字符串输入。 使用 ColumnTransformer 可以仅将 OneHotEncoder 应用于某些列。
编辑:
由于这个原始答案是一年多以前的,并且产生了很多赞成票(包括赏金),我可能应该进一步扩展它。
对于inverse_transform 和transform,你必须做一些小技巧。
from collections import defaultdict
d = defaultdict(LabelEncoder)
这样,您现在将所有列 LabelEncoder
保留为字典。
# Encoding the variable
fit = df.apply(lambda x: d[x.name].fit_transform(x))
# Inverse the encoded
fit.apply(lambda x: d[x.name].inverse_transform(x))
# Using the dictionary to label future data
df.apply(lambda x: d[x.name].transform(x))
MOAR 编辑:
使用 Neuraxle 的 FlattenForEach
步骤,也可以同时对所有展平数据使用相同的 LabelEncoder
:
FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df)
对于根据您的数据列使用单独的LabelEncoder
s,或者如果您的某些数据列需要标签编码而不需要其他列,那么使用ColumnTransformer
是一种允许更多控制的解决方案在您的列选择和您的 LabelEncoder 实例上。
【讨论】:
这太棒了,但在这种情况下我们如何应用逆变换? 但是如果我想在管道中使用这个解决方案,例如df.apply(LabelEncoder().fit_transform)
支持单独的拟合和转换(在火车上拟合,然后在测试集上使用 --> 重新使用学习过的字典)?
如何使它与LabelBinarizer
一起工作,并将字典重新用于测试集?我尝试了d = defaultdict(LabelBinarizer)
,然后尝试了fit = df.apply(lambda x: d[x.name].fit_transform(x))
,但出现了异常:Exception: Data must be 1-dimensional
。我不确定我期望生成的 DataFrame 是什么样子......也许每一列都应该包含二值化向量。
不错的解决方案。如何仅在某些列中进行转换?
如果我想反转一列的编码,我该怎么做?【参考方案2】:
我们可以使用来自 scikit learn 的 OrdinalEncoder
来代替 LabelEncoder
,它允许多列编码。
将分类特征编码为整数数组。 这个转换器的输入应该是一个类似数组的整数或字符串,表示分类(离散)特征所采用的值。特征被转换为序数整数。这会导致每个特征有一列整数(0 到 n_categories - 1)。
>>> from sklearn.preprocessing import OrdinalEncoder
>>> enc = OrdinalEncoder()
>>> X = [['Male', 1], ['Female', 3], ['Female', 2]]
>>> enc.fit(X)
OrdinalEncoder()
>>> enc.categories_
[array(['Female', 'Male'], dtype=object), array([1, 2, 3], dtype=object)]
>>> enc.transform([['Female', 3], ['Male', 1]])
array([[0., 2.],
[1., 0.]])
描述和示例均从其文档页面复制而来,您可以在此处找到:
https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OrdinalEncoder.html#sklearn.preprocessing.OrdinalEncoder
【讨论】:
这应该是scikit-learn 0.20
的方式。谢谢你的建议!【参考方案3】:
如果你具备 object 类型的所有特性,那么上面写的第一个答案就很好用 https://***.com/a/31939145/5840973。
但是,假设我们有混合类型的列。然后我们可以通过编程方式获取类型对象类型的特征名称列表,然后对它们进行标签编码。
#Fetch features of type Object
objFeatures = dataframe.select_dtypes(include="object").columns
#Iterate a loop for features of type object
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
for feat in objFeatures:
dataframe[feat] = le.fit_transform(dataframe[feat].astype(str))
dataframe.info()
【讨论】:
【参考方案4】:使用Neuraxle
TLDR;您可以在这里使用 FlattenForEach 包装类来简单地转换您的 df,例如:
FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df)
。
使用此方法,您的标签编码器将能够适应和转换常规scikit-learn Pipeline。让我们简单地导入:
from sklearn.preprocessing import LabelEncoder
from neuraxle.steps.column_transformer import ColumnTransformer
from neuraxle.steps.loop import FlattenForEach
列共享编码器:
这是一个共享的 LabelEncoder 将如何应用于所有数据以对其进行编码:
p = FlattenForEach(LabelEncoder(), then_unflatten=True)
结果:
p, predicted_output = p.fit_transform(df.values)
expected_output = np.array([
[6, 7, 6, 8, 7, 7],
[1, 3, 0, 1, 5, 3],
[4, 2, 2, 4, 4, 2]
]).transpose()
assert np.array_equal(predicted_output, expected_output)
每列不同的编码器:
这里是第一个独立的 LabelEncoder 将如何应用于宠物,第二个将共享给列所有者和位置。所以准确地说,我们这里混合了不同的和共享的标签编码器:
p = ColumnTransformer([
# A different encoder will be used for column 0 with name "pets":
(0, FlattenForEach(LabelEncoder(), then_unflatten=True)),
# A shared encoder will be used for column 1 and 2, "owner" and "location":
([1, 2], FlattenForEach(LabelEncoder(), then_unflatten=True)),
], n_dimension=2)
结果:
p, predicted_output = p.fit_transform(df.values)
expected_output = np.array([
[0, 1, 0, 2, 1, 1],
[1, 3, 0, 1, 5, 3],
[4, 2, 2, 4, 4, 2]
]).transpose()
assert np.array_equal(predicted_output, expected_output)
【讨论】:
【参考方案5】:这是事实发生后的一年半,但我也需要能够同时 .transform()
多个 pandas 数据框列(并且也能够 .inverse_transform()
它们)。这扩展了上面@PriceHardman 的出色建议:
class MultiColumnLabelEncoder(LabelEncoder):
"""
Wraps sklearn LabelEncoder functionality for use on multiple columns of a
pandas dataframe.
"""
def __init__(self, columns=None):
self.columns = columns
def fit(self, dframe):
"""
Fit label encoder to pandas columns.
Access individual column classes via indexig `self.all_classes_`
Access individual column encoders via indexing
`self.all_encoders_`
"""
# if columns are provided, iterate through and get `classes_`
if self.columns is not None:
# ndarray to hold LabelEncoder().classes_ for each
# column; should match the shape of specified `columns`
self.all_classes_ = np.ndarray(shape=self.columns.shape,
dtype=object)
self.all_encoders_ = np.ndarray(shape=self.columns.shape,
dtype=object)
for idx, column in enumerate(self.columns):
# fit LabelEncoder to get `classes_` for the column
le = LabelEncoder()
le.fit(dframe.loc[:, column].values)
# append the `classes_` to our ndarray container
self.all_classes_[idx] = (column,
np.array(le.classes_.tolist(),
dtype=object))
# append this column's encoder
self.all_encoders_[idx] = le
else:
# no columns specified; assume all are to be encoded
self.columns = dframe.iloc[:, :].columns
self.all_classes_ = np.ndarray(shape=self.columns.shape,
dtype=object)
for idx, column in enumerate(self.columns):
le = LabelEncoder()
le.fit(dframe.loc[:, column].values)
self.all_classes_[idx] = (column,
np.array(le.classes_.tolist(),
dtype=object))
self.all_encoders_[idx] = le
return self
def fit_transform(self, dframe):
"""
Fit label encoder and return encoded labels.
Access individual column classes via indexing
`self.all_classes_`
Access individual column encoders via indexing
`self.all_encoders_`
Access individual column encoded labels via indexing
`self.all_labels_`
"""
# if columns are provided, iterate through and get `classes_`
if self.columns is not None:
# ndarray to hold LabelEncoder().classes_ for each
# column; should match the shape of specified `columns`
self.all_classes_ = np.ndarray(shape=self.columns.shape,
dtype=object)
self.all_encoders_ = np.ndarray(shape=self.columns.shape,
dtype=object)
self.all_labels_ = np.ndarray(shape=self.columns.shape,
dtype=object)
for idx, column in enumerate(self.columns):
# instantiate LabelEncoder
le = LabelEncoder()
# fit and transform labels in the column
dframe.loc[:, column] =\
le.fit_transform(dframe.loc[:, column].values)
# append the `classes_` to our ndarray container
self.all_classes_[idx] = (column,
np.array(le.classes_.tolist(),
dtype=object))
self.all_encoders_[idx] = le
self.all_labels_[idx] = le
else:
# no columns specified; assume all are to be encoded
self.columns = dframe.iloc[:, :].columns
self.all_classes_ = np.ndarray(shape=self.columns.shape,
dtype=object)
for idx, column in enumerate(self.columns):
le = LabelEncoder()
dframe.loc[:, column] = le.fit_transform(
dframe.loc[:, column].values)
self.all_classes_[idx] = (column,
np.array(le.classes_.tolist(),
dtype=object))
self.all_encoders_[idx] = le
return dframe.loc[:, self.columns].values
def transform(self, dframe):
"""
Transform labels to normalized encoding.
"""
if self.columns is not None:
for idx, column in enumerate(self.columns):
dframe.loc[:, column] = self.all_encoders_[
idx].transform(dframe.loc[:, column].values)
else:
self.columns = dframe.iloc[:, :].columns
for idx, column in enumerate(self.columns):
dframe.loc[:, column] = self.all_encoders_[idx]\
.transform(dframe.loc[:, column].values)
return dframe.loc[:, self.columns].values
def inverse_transform(self, dframe):
"""
Transform labels back to original encoding.
"""
if self.columns is not None:
for idx, column in enumerate(self.columns):
dframe.loc[:, column] = self.all_encoders_[idx]\
.inverse_transform(dframe.loc[:, column].values)
else:
self.columns = dframe.iloc[:, :].columns
for idx, column in enumerate(self.columns):
dframe.loc[:, column] = self.all_encoders_[idx]\
.inverse_transform(dframe.loc[:, column].values)
return dframe.loc[:, self.columns].values
例子:
如果df
和df_copy()
是混合类型pandas
数据帧,您可以通过以下方式将MultiColumnLabelEncoder()
应用于dtype=object
列:
# get `object` columns
df_object_columns = df.iloc[:, :].select_dtypes(include=['object']).columns
df_copy_object_columns = df_copy.iloc[:, :].select_dtypes(include=['object']).columns
# instantiate `MultiColumnLabelEncoder`
mcle = MultiColumnLabelEncoder(columns=object_columns)
# fit to `df` data
mcle.fit(df)
# transform the `df` data
mcle.transform(df)
# returns output like below
array([[1, 0, 0, ..., 1, 1, 0],
[0, 5, 1, ..., 1, 1, 2],
[1, 1, 1, ..., 1, 1, 2],
...,
[3, 5, 1, ..., 1, 1, 2],
# transform `df_copy` data
mcle.transform(df_copy)
# returns output like below (assuming the respective columns
# of `df_copy` contain the same unique values as that particular
# column in `df`
array([[1, 0, 0, ..., 1, 1, 0],
[0, 5, 1, ..., 1, 1, 2],
[1, 1, 1, ..., 1, 1, 2],
...,
[3, 5, 1, ..., 1, 1, 2],
# inverse `df` data
mcle.inverse_transform(df)
# outputs data like below
array([['August', 'Friday', '2013', ..., 'N', 'N', 'CA'],
['April', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
['August', 'Monday', '2014', ..., 'N', 'N', 'NJ'],
...,
['February', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
['April', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
['March', 'Tuesday', '2013', ..., 'N', 'N', 'NJ']], dtype=object)
# inverse `df_copy` data
mcle.inverse_transform(df_copy)
# outputs data like below
array([['August', 'Friday', '2013', ..., 'N', 'N', 'CA'],
['April', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
['August', 'Monday', '2014', ..., 'N', 'N', 'NJ'],
...,
['February', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
['April', 'Tuesday', '2014', ..., 'N', 'N', 'NJ'],
['March', 'Tuesday', '2013', ..., 'N', 'N', 'NJ']], dtype=object)
您可以通过索引访问各个列类、列标签和列编码器,以适应每一列:
mcle.all_classes_
mcle.all_encoders_
mcle.all_labels_
【讨论】:
嗨 Jason,mcle.all_labels_ 似乎不起作用(Python 3.5、Conda 4.3.29、Sklearn 0.18.1、Pandas 0.20.1。我得到:AttributeError:'MultiColumnLabelEncoder' 对象没有属性'all_labels_' @Jason 嗨,对不起,我直到今天才看到这个:/ 但如果我不得不猜测,我会说你只是使用了上面的fit
方法,实际上不会生成任何标签,直到您将其(transform
/ fit_transform
)应用于数据。
我认为你需要举一个更好的例子 - 我无法重新运行你的所有代码。
非常感谢。这真的很有帮助。【参考方案6】:
这个怎么样?
def MultiColumnLabelEncode(choice, columns, X):
LabelEncoders = []
if choice == 'encode':
for i in enumerate(columns):
LabelEncoders.append(LabelEncoder())
i=0
for cols in columns:
X[:, cols] = LabelEncoders[i].fit_transform(X[:, cols])
i += 1
elif choice == 'decode':
for cols in columns:
X[:, cols] = LabelEncoders[i].inverse_transform(X[:, cols])
i += 1
else:
print('Please select correct parameter "choice". Available parameters: encode/decode')
这不是最有效的,但是它很有效,而且超级简单。
【讨论】:
【参考方案7】:import pandas as pd
from sklearn.preprocessing import LabelEncoder
train=pd.read_csv('.../train.csv')
#X=train.loc[:,['waterpoint_type_group','status','waterpoint_type','source_class']].values
# Create a label encoder object
def MultiLabelEncoder(columnlist,dataframe):
for i in columnlist:
labelencoder_X=LabelEncoder()
dataframe[i]=labelencoder_X.fit_transform(dataframe[i])
columnlist=['waterpoint_type_group','status','waterpoint_type','source_class','source_type']
MultiLabelEncoder(columnlist,train)
在这里,我正在从位置读取 csv,在函数中,我正在传递我想要标记编码的列列表和我想要应用它的数据框。
【讨论】:
【参考方案8】:从 scikit-learn 0.20 开始,您可以使用 sklearn.compose.ColumnTransformer
和 sklearn.preprocessing.OneHotEncoder
:
如果你只有分类变量,直接OneHotEncoder
:
from sklearn.preprocessing import OneHotEncoder
OneHotEncoder(handle_unknown='ignore').fit_transform(df)
如果你有异构类型的特征:
from sklearn.compose import make_column_transformer
from sklearn.preprocessing import RobustScaler
from sklearn.preprocessing import OneHotEncoder
categorical_columns = ['pets', 'owner', 'location']
numerical_columns = ['age', 'weigth', 'height']
column_trans = make_column_transformer(
(categorical_columns, OneHotEncoder(handle_unknown='ignore'),
(numerical_columns, RobustScaler())
column_trans.fit_transform(df)
文档中的更多选项:http://scikit-learn.org/stable/modules/compose.html#columntransformer-for-heterogeneous-data
【讨论】:
inverse_transform()
在 ColumnTransformer 上不受支持。至少,暂时不是:github.com/scikit-learn/scikit-learn/issues/11463。这对我的应用程序来说是一个很大的缺点,并且可能对其他人也是如此。
虽然 ColumnTransformer 是一个很好的建议,但此代码无法运行(括号不平衡,column_transformer 无法/不再以这种方式工作)
我已提议对原始答案进行编辑以修复代码【参考方案9】:
问题在于您传递给 fit 函数的数据(pd 数据框)的形状。 你必须通过一维列表。
【讨论】:
【参考方案10】:我查看了 LabelEncoder 的源代码 (https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/preprocessing/label.py)。它基于一组 numpy 转换,其中一个是 np.unique()。这个函数只需要一维数组输入。 (如果我错了,请纠正我)。
非常粗略的想法... 首先,确定哪些列需要 LabelEncoder,然后遍历每一列。
def cat_var(df):
"""Identify categorical features.
Parameters
----------
df: original df after missing operations
Returns
-------
cat_var_df: summary df with col index and col name for all categorical vars
"""
col_type = df.dtypes
col_names = list(df)
cat_var_index = [i for i, x in enumerate(col_type) if x=='object']
cat_var_name = [x for i, x in enumerate(col_names) if i in cat_var_index]
cat_var_df = pd.DataFrame('cat_ind': cat_var_index,
'cat_name': cat_var_name)
return cat_var_df
from sklearn.preprocessing import LabelEncoder
def column_encoder(df, cat_var_list):
"""Encoding categorical feature in the dataframe
Parameters
----------
df: input dataframe
cat_var_list: categorical feature index and name, from cat_var function
Return
------
df: new dataframe where categorical features are encoded
label_list: classes_ attribute for all encoded features
"""
label_list = []
cat_var_df = cat_var(df)
cat_list = cat_var_df.loc[:, 'cat_name']
for index, cat_feature in enumerate(cat_list):
le = LabelEncoder()
le.fit(df.loc[:, cat_feature])
label_list.append(list(le.classes_))
df.loc[:, cat_feature] = le.transform(df.loc[:, cat_feature])
return df, label_list
返回的 df 将是编码后的值,而 label_list 将向您显示所有这些值在相应列中的含义。 这是我为工作编写的数据处理脚本的 sn-p。让我知道您是否认为有任何进一步的改进。
编辑: 只想在这里提一下,上面的方法适用于数据框,不会错过最好的。不确定它是如何处理包含缺失数据的数据框的。 (在执行上述方法之前,我已经处理了缺少的程序)
【讨论】:
【参考方案11】:主要使用@Alexander 的答案,但不得不做出一些改变 -
cols_need_mapped = ['col1', 'col2']
mapper = col: cat: n for n, cat in enumerate(df[col].astype('category').cat.categories)
for col in df[cols_need_mapped]
for c in cols_need_mapped :
df[c] = df[c].map(mapper[c])
然后为了将来重复使用,您可以将输出保存到 json 文档中,并在需要时将其读入并使用 .map()
函数,就像我在上面所做的那样。
【讨论】:
【参考方案12】:在这里和其他地方进行大量搜索和实验后,我认为您的答案是here:
pd.DataFrame(columns=df.columns, data=LabelEncoder().fit_transform(df.values.flatten()).reshape(df.shape))
这将跨列保留类别名称:
import pandas as pd
from sklearn.preprocessing import LabelEncoder
df = pd.DataFrame([['A','B','C','D','E','F','G','I','K','H'],
['A','E','H','F','G','I','K','','',''],
['A','C','I','F','H','G','','','','']],
columns=['A1', 'A2', 'A3','A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10'])
pd.DataFrame(columns=df.columns, data=LabelEncoder().fit_transform(df.values.flatten()).reshape(df.shape))
A1 A2 A3 A4 A5 A6 A7 A8 A9 A10
0 1 2 3 4 5 6 7 9 10 8
1 1 5 8 6 7 9 10 0 0 0
2 1 3 9 6 8 7 0 0 0 0
【讨论】:
【参考方案13】:如果您在数据框中有数字和分类两种类型的数据 您可以使用:这里 X 是我的数据框,具有分类和数值两个变量
from sklearn import preprocessing
le = preprocessing.LabelEncoder()
for i in range(0,X.shape[1]):
if X.dtypes[i]=='object':
X[X.columns[i]] = le.fit_transform(X[X.columns[i]])
注意:如果您对将它们转换回来不感兴趣,这种技术很好。
【讨论】:
【参考方案14】:这一切都可以直接在 pandas 中完成,并且非常适合 replace
方法的独特功能。
首先,让我们制作一个字典字典,将列及其值映射到它们的新替换值。
transform_dict =
for col in df.columns:
cats = pd.Categorical(df[col]).categories
d =
for i, cat in enumerate(cats):
d[cat] = i
transform_dict[col] = d
transform_dict
'location': 'New_York': 0, 'San_Diego': 1,
'owner': 'Brick': 0, 'Champ': 1, 'Ron': 2, 'Veronica': 3,
'pets': 'cat': 0, 'dog': 1, 'monkey': 2
由于这始终是一对一的映射,我们可以反转内部字典以将新值映射回原始值。
inverse_transform_dict =
for col, d in transform_dict.items():
inverse_transform_dict[col] = v:k for k, v in d.items()
inverse_transform_dict
'location': 0: 'New_York', 1: 'San_Diego',
'owner': 0: 'Brick', 1: 'Champ', 2: 'Ron', 3: 'Veronica',
'pets': 0: 'cat', 1: 'dog', 2: 'monkey'
现在,我们可以使用replace
方法的独特功能来获取字典的嵌套列表,并将外部键用作列,将内部键用作我们要替换的值。
df.replace(transform_dict)
location owner pets
0 1 1 0
1 0 2 1
2 0 0 0
3 1 1 2
4 1 3 1
5 0 2 1
我们可以通过再次链接 replace
方法轻松地回到原始状态
df.replace(transform_dict).replace(inverse_transform_dict)
location owner pets
0 San_Diego Champ cat
1 New_York Ron dog
2 New_York Brick cat
3 San_Diego Champ monkey
4 San_Diego Veronica dog
5 New_York Ron dog
【讨论】:
【参考方案15】:LabelEncoder()
使用dict()
的多个列的捷径:
from sklearn.preprocessing import LabelEncoder
le_dict = col: LabelEncoder() for col in columns
for col in columns:
le_dict[col].fit_transform(df[col])
您可以使用此le_dict
对任何其他列进行labelEncode:
le_dict[col].transform(df_another[col])
【讨论】:
【参考方案16】:跟进@PriceHardman的解决方案提出的cmets,我将提出以下版本的课程:
class LabelEncodingColoumns(BaseEstimator, TransformerMixin):
def __init__(self, cols=None):
pdu._is_cols_input_valid(cols)
self.cols = cols
self.les = col: LabelEncoder() for col in cols
self._is_fitted = False
def transform(self, df, **transform_params):
"""
Scaling ``cols`` of ``df`` using the fitting
Parameters
----------
df : DataFrame
DataFrame to be preprocessed
"""
if not self._is_fitted:
raise NotFittedError("Fitting was not preformed")
pdu._is_cols_subset_of_df_cols(self.cols, df)
df = df.copy()
label_enc_dict =
for col in self.cols:
label_enc_dict[col] = self.les[col].transform(df[col])
labelenc_cols = pd.DataFrame(label_enc_dict,
# The index of the resulting DataFrame should be assigned and
# equal to the one of the original DataFrame. Otherwise, upon
# concatenation NaNs will be introduced.
index=df.index
)
for col in self.cols:
df[col] = labelenc_cols[col]
return df
def fit(self, df, y=None, **fit_params):
"""
Fitting the preprocessing
Parameters
----------
df : DataFrame
Data to use for fitting.
In many cases, should be ``X_train``.
"""
pdu._is_cols_subset_of_df_cols(self.cols, df)
for col in self.cols:
self.les[col].fit(df[col])
self._is_fitted = True
return self
该类在训练集上拟合编码器,并在转换时使用拟合版本。初始版本的代码可以在here找到。
【讨论】:
【参考方案17】:如果我们有单列来进行标签编码及其逆变换,那么当python中有多个列时,很容易做到这一点
def stringtocategory(dataset):
'''
@author puja.sharma
@see The function label encodes the object type columns and gives label encoded and inverse tranform of the label encoded data
@param dataset dataframe on whoes column the label encoding has to be done
@return label encoded and inverse tranform of the label encoded data.
'''
data_original = dataset[:]
data_tranformed = dataset[:]
for y in dataset.columns:
#check the dtype of the column object type contains strings or chars
if (dataset[y].dtype == object):
print("The string type features are : " + y)
le = preprocessing.LabelEncoder()
le.fit(dataset[y].unique())
#label encoded data
data_tranformed[y] = le.transform(dataset[y])
#inverse label transform data
data_original[y] = le.inverse_transform(data_tranformed[y])
return data_tranformed,data_original
【讨论】:
【参考方案18】:这并不能直接回答您的问题(Naputipulu Jon 和 PriceHardman 的回答非常棒)
但是,出于一些分类任务等的目的,您可以使用
pandas.get_dummies(input_df)
这可以输入带有分类数据的数据框并返回带有二进制值的数据框。变量值被编码为结果数据框中的列名。 more
【讨论】:
【参考方案19】:我们不需要 LabelEncoder。
您可以将列转换为分类,然后获取它们的代码。我使用下面的字典理解将此过程应用于每一列,并将结果包装回具有相同索引和列名的相同形状的数据框。
>>> pd.DataFrame(col: df[col].astype('category').cat.codes for col in df, index=df.index)
location owner pets
0 1 1 0
1 0 2 1
2 0 0 0
3 1 1 2
4 1 3 1
5 0 2 1
要创建映射字典,您可以使用字典推导枚举类别:
>>> col: n: cat for n, cat in enumerate(df[col].astype('category').cat.categories)
for col in df
'location': 0: 'New_York', 1: 'San_Diego',
'owner': 0: 'Brick', 1: 'Champ', 2: 'Ron', 3: 'Veronica',
'pets': 0: 'cat', 1: 'dog', 2: 'monkey'
【讨论】:
如果我想返回(反向)一列(示例目标变量:Y)我该怎么做? 不错的亚历山大!我很喜欢这个!!【参考方案20】:正如 larsmans 所说,LabelEncoder() only takes a 1-d array as an argument。也就是说,滚动您自己的标签编码器非常容易,该编码器在您选择的多个列上运行,并返回转换后的数据帧。我的代码部分基于 Zac Stewart 的优秀博文 here。
创建自定义编码器只需创建一个响应fit()
、transform()
和fit_transform()
方法的类。在你的情况下,一个好的开始可能是这样的:
import pandas as pd
from sklearn.preprocessing import LabelEncoder
from sklearn.pipeline import Pipeline
# Create some toy data in a Pandas dataframe
fruit_data = pd.DataFrame(
'fruit': ['apple','orange','pear','orange'],
'color': ['red','orange','green','green'],
'weight': [5,6,3,4]
)
class MultiColumnLabelEncoder:
def __init__(self,columns = None):
self.columns = columns # array of column names to encode
def fit(self,X,y=None):
return self # not relevant here
def transform(self,X):
'''
Transforms columns of X specified in self.columns using
LabelEncoder(). If no columns specified, transforms all
columns in X.
'''
output = X.copy()
if self.columns is not None:
for col in self.columns:
output[col] = LabelEncoder().fit_transform(output[col])
else:
for colname,col in output.iteritems():
output[colname] = LabelEncoder().fit_transform(col)
return output
def fit_transform(self,X,y=None):
return self.fit(X,y).transform(X)
假设我们想对我们的两个分类属性(fruit
和 color
)进行编码,而单独留下数字属性 weight
。我们可以这样做:
MultiColumnLabelEncoder(columns = ['fruit','color']).fit_transform(fruit_data)
这将我们的 fruit_data
数据集从
到
向它传递一个完全由分类变量组成的数据框并省略 columns
参数将导致每一列都被编码(我相信这是你最初寻找的):
MultiColumnLabelEncoder().fit_transform(fruit_data.drop('weight',axis=1))
这会改变
到
.
请注意,当它尝试对已经是数字的属性进行编码时,它可能会阻塞(如果你愿意,可以添加一些代码来处理这个问题)。
另一个很好的特性是我们可以在管道中使用这个自定义转换器:
encoding_pipeline = Pipeline([
('encoding',MultiColumnLabelEncoder(columns=['fruit','color']))
# add more pipeline steps as needed
])
encoding_pipeline.fit_transform(fruit_data)
【讨论】:
刚刚意识到数据暗示橙色是绿色的。哎呀。 ;) 这是一次转换数据的好方法,但是如果我想在验证集上重用这个转换怎么办。您将不得不再次 fit_transform 并且可能会出现问题,例如我的新数据集没有所有变量的所有类别。例如,假设我的新数据集中没有出现绿色。这会弄乱编码。 同意@Ben。除了方法名称之外,这实际上并没有模仿 sklearn。如果您尝试将其放入管道中,它将无法正常工作 为确保标签编码在训练集和测试集之间保持一致,您需要对整个数据集(训练 + 测试)执行编码。这可以在将它们拆分为训练和测试之前完成,也可以将它们组合起来,执行编码,然后再次拆分出来。 逆向怎么样?解码回原来的?【参考方案21】:假设您只是想获得一个可用于表示列的sklearn.preprocessing.LabelEncoder()
对象,您所要做的就是:
le.fit(df.columns)
在上面的代码中,您将有一个与每一列对应的唯一编号。
更准确地说,您将拥有 df.columns
到 le.transform(df.columns.get_values())
的 1:1 映射。要获取列的编码,只需将其传递给le.transform(...)
。例如,下面将获取每一列的编码:
le.transform(df.columns.get_values())
假设您要为所有行标签创建一个 sklearn.preprocessing.LabelEncoder()
对象,您可以执行以下操作:
le.fit([y for x in df.get_values() for y in x])
在这种情况下,您很可能有非唯一的行标签(如您的问题所示)。要查看编码器创建的类,您可以执行le.classes_
。您会注意到这应该与set(y for x in df.get_values() for y in x)
中的元素相同。再次将行标签转换为编码标签使用le.transform(...)
。例如,如果要检索 df.columns
数组中第一列和第一行的标签,可以这样做:
le.transform([df.get_value(0, df.columns[0])])
您在评论中提出的问题有点复杂,但仍然可以 完成:
le.fit([str(z) for z in set((x[0], y) for x in df.iteritems() for y in x[1])])
上面的代码做了以下事情:
-
对所有(列、行)对进行唯一组合
将每对表示为元组的字符串版本。这是克服
LabelEncoder
类不支持元组作为类名的解决方法。
适合LabelEncoder
的新项目。
现在使用这个新模型有点复杂。假设我们要提取我们在上一个示例中查找的相同项目的表示(df.columns 中的第一列和第一行),我们可以这样做:
le.transform([str((df.columns[0], df.get_value(0, df.columns[0])))])
请记住,现在每个查找都是元组的字符串表示, 包含(列,行)。
【讨论】:
【参考方案22】:不,LabelEncoder
不会这样做。它采用一维类标签数组并生成一维数组。它旨在处理分类问题中的类标签,而不是任意数据,任何将其强制用于其他用途的尝试都需要代码将实际问题转换为它解决的问题(并将解决方案返回到原始空间)。
【讨论】:
好的,鉴于此,您对我一次用整个DataFrame
对字符串标签进行编码的最佳方式有何建议?
@Bryan 查看LabelEncoder
代码并进行修改。我自己不使用 Pandas,所以我不知道会有多难。
我会让其他pandas
的人也来解决这个问题——我敢肯定我不是唯一面临这个挑战的人,所以我希望可能有一个预内置解决方案。以上是关于scikit-learn 中跨多个列的标签编码的主要内容,如果未能解决你的问题,请参考以下文章
SciKit-Learn 标签编码器导致错误“参数必须是字符串或数字”