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

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


当前回答

使用Neuraxle

TLDR;你可以在这里使用flatforeach包装类简单地转换你的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)

其他回答

如果你拥有object类型的所有特征,那么上面写的第一个答案很好https://stackoverflow.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()

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

这个怎么样?

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

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

这是我解决你问题的办法。为了将包含文本的数据帧列转换为编码值,只需使用我的函数text_to_numbers,它返回LE的字典。Key是列LabelEncoder()作为值的列名。

def text_to_numbers(df):
        le_dict = dict()
        for i in df.columns:
            if df[i].dtype not in ["float64", "bool", "int64"]:
                le_dict[i] = preprocessing.LabelEncoder()
                df[i] = le_dict[i].fit_transform(df[i])
    
        return df, le_dict

下面的函数将使保留原始的未编码数据帧成为可能。

 def numbers_to_text(df, le_dict):
        for i in le_dict.keys():
            df[i] = le_dict[i].inverse_transform(df[i])
    
        return df

我查看了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将是编码后的df, label_list将显示所有这些值在相应列中的含义。 这是我为工作编写的数据处理脚本的一个片段。如果你觉得还有什么改进的地方,请告诉我。

编辑: 这里只想提一下,上述方法在处理数据帧时不会遗漏最佳数据。不确定它是如何工作的数据帧包含丢失的数据。(在执行上述方法之前,我已经处理了缺失过程)