Label encoding across multiple columns in scikit-learn

后端 未结 22 1916
礼貌的吻别
礼貌的吻别 2020-11-22 09:02

I\'m trying to use scikit-learn\'s LabelEncoder to encode a pandas DataFrame of string labels. As the dataframe has many (50+) columns, I want to a

相关标签:
22条回答
  • 2020-11-22 09:40

    Using Neuraxle

    TLDR; You here can use the FlattenForEach wrapper class to simply transform your df like: FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df).

    With this method, your label encoder will be able to fit and transform within a regular scikit-learn Pipeline. Let's simply import:

    from sklearn.preprocessing import LabelEncoder
    from neuraxle.steps.column_transformer import ColumnTransformer
    from neuraxle.steps.loop import FlattenForEach
    

    Same shared encoder for columns:

    Here is how one shared LabelEncoder will be applied on all the data to encode it:

        p = FlattenForEach(LabelEncoder(), then_unflatten=True)
    

    Result:

        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)
    

    Different encoders per column:

    And here is how a first standalone LabelEncoder will be applied on the pets, and a second will be shared for the columns owner and location. So to be precise, we here have a mix of different and shared label encoders:

        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)
    

    Result:

        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)
    
    0 讨论(0)
  • 2020-11-22 09:42

    We don't need a LabelEncoder.

    You can convert the columns to categoricals and then get their codes. I used a dictionary comprehension below to apply this process to every column and wrap the result back into a dataframe of the same shape with identical indices and column names.

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

    To create a mapping dictionary, you can just enumerate the categories using a dictionary comprehension:

    >>> {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'}}
    
    0 讨论(0)
  • 2020-11-22 09:42

    A short way to LabelEncoder() multiple columns with a 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])
    

    and you can use this le_dict to labelEncode any other column:

    le_dict[col].transform(df_another[col])
    
    0 讨论(0)
  • 2020-11-22 09:43

    If you have numerical and categorical both type of data in dataframe You can use : here X is my dataframe having categorical and numerical both variables

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

    Note: This technique is good if you are not interested in converting them back.

    0 讨论(0)
  • 2020-11-22 09:44

    You can easily do this though,

    df.apply(LabelEncoder().fit_transform)
    

    EDIT2:

    In scikit-learn 0.20, the recommended way is

    OneHotEncoder().fit_transform(df)
    

    as the OneHotEncoder now supports string input. Applying OneHotEncoder only to certain columns is possible with the ColumnTransformer.

    EDIT:

    Since this answer is over a year ago, and generated many upvotes (including a bounty), I should probably extend this further.

    For inverse_transform and transform, you have to do a little bit of hack.

    from collections import defaultdict
    d = defaultdict(LabelEncoder)
    

    With this, you now retain all columns LabelEncoder as dictionary.

    # 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))
    
    0 讨论(0)
  • 2020-11-22 09:46

    As mentioned by larsmans, LabelEncoder() only takes a 1-d array as an argument. That said, it is quite easy to roll your own label encoder that operates on multiple columns of your choosing, and returns a transformed dataframe. My code here is based in part on Zac Stewart's excellent blog post found here.

    Creating a custom encoder involves simply creating a class that responds to the fit(), transform(), and fit_transform() methods. In your case, a good start might be something like this:

    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)
    

    Suppose we want to encode our two categorical attributes (fruit and color), while leaving the numeric attribute weight alone. We could do this as follows:

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

    Which transforms our fruit_data dataset from

    enter image description here to

    enter image description here

    Passing it a dataframe consisting entirely of categorical variables and omitting the columns parameter will result in every column being encoded (which I believe is what you were originally looking for):

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

    This transforms

    enter image description here to

    enter image description here.

    Note that it'll probably choke when it tries to encode attributes that are already numeric (add some code to handle this if you like).

    Another nice feature about this is that we can use this custom transformer in a pipeline:

    encoding_pipeline = Pipeline([
        ('encoding',MultiColumnLabelEncoder(columns=['fruit','color']))
        # add more pipeline steps as needed
    ])
    encoding_pipeline.fit_transform(fruit_data)
    
    0 讨论(0)
提交回复
热议问题