scikit-learn中跨多个列的标签编码

Bry*_*yan 192 python pandas scikit-learn

我正在尝试使用scikit-learn LabelEncoder来编码DataFrame字符串标签的大熊猫.由于数据框有很多(50+)列,我想避免LabelEncoder为每列创建一个对象; 我宁愿只有一个大LabelEncoder对象适用于我的所有数据列.

投掷整DataFrameLabelEncoder创建下面的错误.请记住,我在这里使用虚拟数据; 实际上我正在处理大约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)
Run Code Online (Sandbox Code Playgroud)

回溯(最近一次调用最后一次):文件"",第1行,在文件"/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/preprocessing/label.py",第103行,in y y = column_or_1d(Y,警告=真)文件 "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py",线306,在column_or_1d提高ValueError异常("坏输入形状{ 0.".format(shape))ValueError:输入形状错误(6,3)

有关如何解决这个问题的任何想法?

Nap*_*Jon 401

你可以轻松地做到这一点,

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

编辑:

由于这个答案是在一年多以前,并产生了许多赞成票(包括赏金),我应该进一步扩展这一点.

对于inverse_transform和transform,你必须做一些hack.

OneHotEncoder().fit_transform(df)
Run Code Online (Sandbox Code Playgroud)

有了这个,您现在将所有列保留LabelEncoder为字典.

from collections import defaultdict
d = defaultdict(LabelEncoder)
Run Code Online (Sandbox Code Playgroud)

  • 但是如果我想在管道中使用这个解决方案,例如单独的拟合和转换(适合在火车上,然后在测试集上使用 - >重新使用学习的字典)这是由`df.apply(LabelEncoder())支持的.fit_transform)`? (9认同)
  • 好的解决方案 如何仅在某些列中进行转换? (3认同)
  • 这很神奇,但在这种情况下,我们如何应用逆变换? (2认同)
  • 如何使用`LabelBinarizer`代替并重新使用字典作为测试集?我试过`d = defaultdict(LabelBinarizer)`然后`fit = df.apply(lambda x:d [x.name] .fit_transform(x))`但引发了异常:`例外:数据必须是1维的`.我不确定我期望得到的DataFrame看起来像......也许每列应该保存二进制化的向量. (2认同)

Pri*_*man 85

正如larsmans所提到的,LabelEncoder()只将一维数组作为参数.也就是说,滚动您自己的标签编码器非常容易,该编码器在您选择的多个列上运行,并返回转换后的数据帧.我的代码部分基于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)
Run Code Online (Sandbox Code Playgroud)

假设我们想要编码我们的两个分类属性(fruitcolor),同时保留数字属性weight.我们可以这样做:

MultiColumnLabelEncoder(columns = ['fruit','color']).fit_transform(fruit_data)
Run Code Online (Sandbox Code Playgroud)

这会转换我们的fruit_data数据集

在此输入图像描述

在此输入图像描述

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

MultiColumnLabelEncoder().fit_transform(fruit_data.drop('weight',axis=1))
Run Code Online (Sandbox Code Playgroud)

这转变了

在此输入图像描述

在此输入图像描述.

请注意,当它尝试编码已经是数字的属性时,它可能会窒息(如果你愿意,可以添加一些代码来处理这个).

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

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

  • 这是一次转换数据的好方法,但如果我想在验证集上重用此转换,该怎么办呢?你将不得不再次fit_transform并且可能出现问题,例如我的新数据集没有所有变量的所有类别.例如,说我的新数据集中没有显示绿色.这将搞乱编码. (4认同)
  • 同意@Ben.除了方法名称之外,这实际上并不模仿sklearn.如果你试图将它放在管道中,它将无法正常工作 (3认同)
  • 为了确保列车编码和测试集的标签编码一致,您需要对整个数据集执行编码(训练+测试).这可以在将它们拆分为训练和测试之前完成,也可以将它们组合,执行编码,然后再将它们拆分. (3认同)
  • 反过来怎么样?解码回原来的? (3认同)
  • 刚刚意识到数据暗示橙色是绿色的.哎呀.;) (2认同)

Ale*_*der 16

我们不需要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
Run Code Online (Sandbox Code Playgroud)

要创建映射字典,您可以使用字典理解来枚举类别:

>>> {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'}}
Run Code Online (Sandbox Code Playgroud)

  • 如果我想返回(反向)一列(示例目标变量:Y)我该怎么做? (3认同)

ogr*_*sel 12

从scikit-learn 0.20开始,您可以使用sklearn.compose.ColumnTransformersklearn.preprocessing.OneHotEncoder

如果只有分类变量,则OneHotEncoder直接:

from sklearn.preprocessing import OneHotEncoder

OneHotEncoder(handle_unknown='ignore').fit_transform(df)
Run Code Online (Sandbox Code Playgroud)

如果您具有异构类型的功能:

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)
Run Code Online (Sandbox Code Playgroud)

文档中的更多选项:http : //scikit-learn.org/stable/modules/compose.html#columntransformer-for-heterogeneous-data


Jas*_*ich 8

这是一年半之后的事实,但我也需要能够同时处理.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
Run Code Online (Sandbox Code Playgroud)

例子:

如果dfdf_copy()是混合型pandasdataframes,您可以应用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)
Run Code Online (Sandbox Code Playgroud)

您可以通过索引访问用于拟合每列的各个列类、列标签和列编码器:

mcle.all_classes_
mcle.all_encoders_
mcle.all_labels_


Anu*_*shi 7

这并没有直接回答你的问题(Naputipulu Jon和PriceHardman对此有很棒的回复)

但是,出于少数分类任务的目的,您可以使用

pandas.get_dummies(input_df) 
Run Code Online (Sandbox Code Playgroud)

这可以输入带有分类数据的数据帧,并返回带有二进制值的数据帧.变量值在结果数据帧中编码为列名.更多


Teh*_*Guy 6

假设您只是尝试获取一个sklearn.preprocessing.LabelEncoder()可用于表示您的列的对象,那么您要做的就是:

le.fit(df.columns)
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,您将有一个对应于每一列的唯一编号。更准确地说,你将有一个1:1映射df.columnsle.transform(df.columns.get_values())。要获取列的编码,只需将其传递给le.transform(...)。例如,以下将获取每一列的编码:

le.transform(df.columns.get_values())
Run Code Online (Sandbox Code Playgroud)

假设要sklearn.preprocessing.LabelEncoder()为所有行标签创建一个对象,可以执行以下操作:

le.fit([y for x in df.get_values() for y in x])
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您很可能具有非唯一的行标签(如您的问题所示)。要查看编​​码器创建的类,可以执行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])])
Run Code Online (Sandbox Code Playgroud)

您在评论中遇到的问题比较复杂,但仍然可以解决:

le.fit([str(z) for z in set((x[0], y) for x in df.iteritems() for y in x[1])])
Run Code Online (Sandbox Code Playgroud)

上面的代码执行以下操作:

  1. 对所有的(列,行)进行唯一组合
  2. 将每对表示为元组的字符串版本。这是一种解决方法,可以克服LabelEncoder不支持将元组用作类名的类。
  3. 使新项目适合LabelEncoder

现在使用这种新模型要复杂一些。假设我们要提取在上一个示例中查找的相同项目的表示形式(df.columns中的第一列和第一行),我们可以这样做:

le.transform([str((df.columns[0], df.get_value(0, df.columns[0])))])
Run Code Online (Sandbox Code Playgroud)

请记住,每个查找现在都是包含(列,行)的元组的字符串表示形式。


RVK*_*RVK 6

这是脚本

from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
col_list = df.select_dtypes(include = "object").columns
for colsn in col_list:
    df[colsn] = le.fit_transform(df[colsn].astype(str))
Run Code Online (Sandbox Code Playgroud)


Fre*_*Foo 5

不,LabelEncoder不这样做。它接受一维数组的类标签,并生成一维数组。它旨在处理分类问题中的类标签,而不是任意数据,并且任何试图将其用于其他用途的尝试都将需要代码将实际问题转换为要解决的问题(并将解决方案还原到原始空间)。


Ted*_*rou 5

可以直接在 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}}
Run Code Online (Sandbox Code Playgroud)

由于这将始终是一对一映射,因此我们可以反转内部字典以将新值映射回原始值。

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'}}
Run Code Online (Sandbox Code Playgroud)

现在,我们可以使用该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
Run Code Online (Sandbox Code Playgroud)

我们可以通过再次链接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
Run Code Online (Sandbox Code Playgroud)


Vik*_*pta 5

如果数据框中有数字和分类两种类型的数据,您可以使用:这里 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]])
Run Code Online (Sandbox Code Playgroud)

注意:如果您对将它们转换回来不感兴趣,则此技术很好。


Chr*_*her 5

经过大量搜索和实验,在这里和其他地方找到了一些答案,我想你的答案就在这里

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
Run Code Online (Sandbox Code Playgroud)


wil*_*ccc 5

我检查了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
Run Code Online (Sandbox Code Playgroud)

返回的df将是编码后的 df,label_list将向您显示所有这些值在相应列中的含义。这是我为工作编写的数据处理脚本的片段。如果您认为还可以进一步改进,请告诉我。

编辑:只想在这里提一下,上面的方法适用于数据框架,不会错过最好的。不确定它如何处理包含丢失数据的数据框。(在执行上述方法之前我处理了丢失的程序)