我试图使用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)

回溯(最近一次调用): 文件“”,第1行,在 文件"/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/预处理/label.py",第103行 y = column_or_1d(y, warn=True) 文件"/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py",第306行,在column_or_1d中 raise ValueError("错误的输入形状{0}".format(形状)) ValueError:错误的输入形状(6,3)

对于如何解决这个问题有什么想法吗?


当前回答

使用dict()实现LabelEncoder()多列的简单方法:

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])

其他回答

下面是我一次性转换多列的解决方案,以及精确的inverse_transform

from sklearn import preprocessing
columns = ['buying','maint','lug_boot','safety','cls']  # columns names where transform is required
for X in columns:
  exec(f'le_{X} = preprocessing.LabelEncoder()')  #create label encoder with name "le_X", where X is column name
  exec(f'df.{X} = le_{X}.fit_transform(df.{X})')  #execute fit transform for column X with respective lable encoder "le_X", where X is column name
df.head()  # to display transformed results

for X in columns:
  exec(f'df.{X} = le_{X}.inverse_transform(df.{X})')  #execute inverse_transform for column X with respective lable encoder "le_X", where X is column name
df.head() # to display Inverse transformed results of df

你可以很容易地做到,

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的flatforeach步骤,也可以在一次对所有平坦数据使用相同的LabelEncoder:

FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df)

要根据数据列使用单独的LabelEncoders,或者如果只有一些数据列需要进行标签编码,而不需要其他数据列,那么使用ColumnTransformer是一种解决方案,它允许对列选择和LabelEncoder实例进行更多控制。

正如larsmans提到的,LabelEncoder()只接受1维数组作为参数。也就是说,可以很容易地滚动自己的标签编码器,对您选择的多个列进行操作,并返回转换后的数据框架。我在这里的代码部分基于Zac Stewart的优秀博客文章。

创建自定义编码器只需要创建一个响应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)进行编码,而不使用数字属性权重。我们可以这样做:

MultiColumnLabelEncoder(columns = ['fruit','color']).fit_transform(fruit_data)

它转换了我们的fruit_data数据集

to

传递给它一个完全由分类变量组成的数据框架,省略columns参数将导致每个列都被编码(我相信这是你最初寻找的):

MultiColumnLabelEncoder().fit_transform(fruit_data.drop('weight',axis=1))

这个转换

to

.

请注意,当它试图编码已经是数值的属性时可能会阻塞(如果您愿意,可以添加一些代码来处理这个问题)。

另一个很好的特性是我们可以在管道中使用这个自定义转换器:

encoding_pipeline = Pipeline([
    ('encoding',MultiColumnLabelEncoder(columns=['fruit','color']))
    # add more pipeline steps as needed
])
encoding_pipeline.fit_transform(fruit_data)

主要使用@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()函数,就像我上面所做的那样。

这个怎么样?

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')

这不是最有效的,但它工作,它是超级简单。