【问题标题】:label-encoder encoding missing values标签编码器编码缺失值
【发布时间】:2016-08-16 22:43:41
【问题描述】:

我正在使用标签编码器将分类数据转换为数值。

LabelEncoder 如何处理缺失值?

from sklearn.preprocessing import LabelEncoder
import pandas as pd
import numpy as np
a = pd.DataFrame(['A','B','C',np.nan,'D','A'])
le = LabelEncoder()
le.fit_transform(a)

输出:

array([1, 2, 3, 0, 4, 1])

对于上面的示例,标签编码器将 NaN 值更改为类别。我怎么知道哪个类别代表缺失值?

【问题讨论】:

标签: python pandas scikit-learn


【解决方案1】:

这是我的尝试!

import numpy as np
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
#Now lets encode the incomplete Cabin feature
titanic_train_le['Cabin'] = le.fit_transform(titanic_train_le['Cabin'].astype(str))
#get nan code for the cabin categorical feature
cabin_nan_code=le.transform(['nan'])[0]
#Now, retrieve the nan values in the encoded data
titanic_train_le['Cabin'].replace(cabin_nan_code,np.nan,inplace=True)

【讨论】:

  • 请在您的回答中提供更多详细信息。正如目前所写的那样,很难理解您的解决方案。
  • 请添加更多详细信息以扩展您的答案,例如工作代码或文档引用。
【解决方案2】:

我想和你分享我的解决方案。
我创建了一个模块,它采用混合数据集并将其从分类转换为数值 和逆。

这个模块也可以在my Github 中找到,并带有示例。
如果您喜欢我的解决方案,请点赞。

谢谢, 伊丹

class label_encoder_contain_missing_values :

        def __init__ (self) :    
            pass  

        def categorical_to_numeric (self,dataset):
            import numpy as np
            import pandas as pd
            
            self.dataset = dataset
            self.summary = None
            self.table_encoder= {}

            for index in self.dataset.columns :
                if self.dataset[index].dtypes == 'object' :               
                   column_data_frame = pd.Series(self.dataset[index],name='column').to_frame()
                   unique_values = pd.Series(self.dataset[index].unique())
                   i = 0
                   label_encoder = pd.DataFrame({'value_name':[],'Encode':[]})
                   while i <= len(unique_values)-1:
                         if unique_values.isnull()[i] == True : 
                           label_encoder = label_encoder.append({'value_name': unique_values[i],'Encode':np.nan}, ignore_index=True) #np.nan = -1
                         else:
                           label_encoder = label_encoder.append({'value_name': unique_values[i],'Encode':i}, ignore_index=True)
                         i+=1 

                   output = pd.merge(left=column_data_frame,right = label_encoder, how='left',left_on='column',right_on='value_name')
                   self.summary = output[['column','Encode']].drop_duplicates().reset_index(drop=True)
                   self.dataset[index] = output.Encode 
                   self.table_encoder.update({index:self.summary})
                    
                else :
                     pass
                     
            # ---- Show Encode Table ----- #               
            print('''\nLabel Encoding completed in Successfully.\n
                       Next steps: \n
                       1.  To view table_encoder, Execute the follow: \n
                           for index in table_encoder :
                           print(f'\\n{index} \\n',table_encoder[index])
                           
                       2. For inverse, execute the follow : \n
                          df = label_encoder_contain_missing_values().
                               inverse_numeric_to_categorical(table_encoder, df) ''') 
                        
            return self.table_encoder  ,self.dataset 
        

        def inverse_numeric_to_categorical (self,table_encoder, df):
            dataset = df.copy()
            for column in table_encoder.keys():
                df_column = df[column].to_frame()
                output = pd.merge(left=df_column,right = table_encoder[column], how='left',left_on= column,right_on='Encode')#.rename(columns={'column_x' :'encode','column_y':'category'})
                df[column]= output.column
            print('\nInverse Label Encoding, from categorical to numerical completed in Successfully.\n')
            return df
            
**execute command from categorical to numerical** <br>
table_encoder, df = label_encoder_contain_missing_values().categorical_to_numeric(df) 

**execute command from numerical to categorical** <br>
df = label_encoder_contain_missing_values().inverse_numeric_to_categorical(table_encoder, df)

【讨论】:

    【解决方案3】:

    此函数从数据框中获取一列并返回仅对非 NaN 进行标签编码的列,其余部分保持不变

    import pandas as pd
    from sklearn.preprocessing import LabelEncoder
    
    def label_encode_column(col):
        nans = col.isnull()
        nan_lst = []
        nan_idx_lst = []
        label_lst = []
        label_idx_lst = []
    
        for idx, nan in enumerate(nans):
            if nan:
                nan_lst.append(col[idx])
                nan_idx_lst.append(idx)
            else:
                label_lst.append(col[idx])
                label_idx_lst.append(idx)
    
        nan_df = pd.DataFrame(nan_lst, index=nan_idx_lst)
        label_df = pd.DataFrame(label_lst, index=label_idx_lst) 
    
        label_encoder = LabelEncoder()
        label_df = label_encoder.fit_transform(label_df.astype(str))
        label_df = pd.DataFrame(label_df, index=label_idx_lst)
        final_col = pd.concat([label_df, nan_df])
        
        return final_col.sort_index()
    

    【讨论】:

      【解决方案4】:

      这是我的解决方案,因为我对此处发布的解决方案不满意。我需要一个 LabelEncoder 将我的缺失值保留为 NaN 以便之后使用 Imputer。所以我写了我自己的LabelEncoder 类。它适用于 DataFrame。

      from sklearn.base import BaseEstimator
      from sklearn.base import TransformerMixin
      from sklearn.preprocessing import LabelEncoder
      
      class LabelEncoderByCol(BaseEstimator, TransformerMixin):
          def __init__(self,col):
              #List of column names in the DataFrame that should be encoded
              self.col = col
              #Dictionary storing a LabelEncoder for each column
              self.le_dic = {}
              for el in self.col:
                  self.le_dic[el] = LabelEncoder()
      
          def fit(self,x,y=None):
              #Fill missing values with the string 'NaN'
              x[self.col] = x[self.col].fillna('NaN')
              for el in self.col:
                  #Only use the values that are not 'NaN' to fit the Encoder
                  a = x[el][x[el]!='NaN']
                  self.le_dic[el].fit(a)
              return self
      
          def transform(self,x,y=None):
              #Fill missing values with the string 'NaN'
              x[self.col] = x[self.col].fillna('NaN')
              for el in self.col:
                  #Only use the values that are not 'NaN' to fit the Encoder
                  a = x[el][x[el]!='NaN']
                  #Store an ndarray of the current column
                  b = x[el].to_numpy()
                  #Replace the elements in the ndarray that are not 'NaN'
                  #using the transformer
                  b[b!='NaN'] = self.le_dic[el].transform(a)
                  #Overwrite the column in the DataFrame
                  x[el]=b
              #return the transformed DataFrame
              return x
      

      您可以输入 DataFrame,而不仅仅是 1-dim Series。使用 col 你可以选择应该编码的列。

      我想在这里提供一些反馈。

      【讨论】:

      • 我使用了newdf = LabelEncoderByCol(df) - 现在如何将其转换为熊猫?
      • 如果测试集的值不在训练集中怎么办。
      • @whatsnext 它与原始的 LabelEncoder 具有相同的行为,但具有看不见的值。您可能需要一些额外的行来实现体面的行为。参考:stackoverflow.com/questions/21057621/…如果您编辑我的答案以改进,我会批准。
      • 能否包含inverse_transform函数?
      【解决方案5】:

      您可以通过将缺失值替换为字符串“NaN”来处理缺失值。类别可以通过le.transfrom()获取。

      le.fit_transform(a.fillna('NaN'))
      category = le.transform(['NaN'])
      

      另一种解决方案是标签编码器忽略缺失值。

      a = le.fit_transform(a.astype(str))
      

      【讨论】:

        【解决方案6】:

        @Kerem 投票最多的答案有错别字,因此我在此处发布更正和改进的答案:

        from sklearn.preprocessing import LabelEncoder
        import pandas as pd
        import numpy as np
        a = pd.DataFrame(['A','B','C',np.nan,'D','A'])
        for j in a.columns.values:
            le = LabelEncoder()
        ### fit with the desired col, col in position 0 for this ###example
            fit_by = pd.Series([i for i in a[j].unique() if type(i) == str])
            le.fit(fit_by)
            ### Set transformed col leaving np.NaN as they are
            a["transformed"] = a[j].apply(lambda x: le.transform([x])[0] if type(x) == str else x)
        
        

        【讨论】:

          【解决方案7】:

          您也可以使用掩码替换标记后的原始数据框

          df = pd.DataFrame({'A': ['x', np.NaN, 'z'], 'B': [1, 6, 9], 'C': [2, 1, np.NaN]})
          
              A   B   C
          0   x   1   2.0
          1   NaN 6   1.0
          2   z   9   NaN
          
          original = df
          mask = df_1.isnull()
                 A    B   C
          0   False   False   False
          1   True    False   False
          2   False   False   True
          
          df = df.astype(str).apply(LabelEncoder().fit_transform)
          df.where(~mask, original)
          
          A   B   C
          0   1.0 0   1.0
          1   NaN 1   0.0
          2   2.0 2   NaN
          

          【讨论】:

            【解决方案8】:

            我也想贡献我的解决方法,因为在处理包含缺失值的分类数据时,我发现其他解决方法有点乏味

            # Create a random dataframe
            foo = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
            
            # Randomly intersperse column 'A' with missing data (NaN)
            foo['A'][np.random.randint(0,len(foo), size=20)] = np.nan
            
            # Convert this series to string, to simulate our problem
            series = foo['A'].astype(str)
            
            # np.nan are converted to the string "nan", mask these out
            mask = (series == "nan")
            
            # Apply the LabelEncoder to the unmasked series, replace the masked series with np.nan
            series[~mask] = LabelEncoder().fit_transform(series[~mask])
            series[mask] = np.nan
            
            foo['A'] = series
            

            【讨论】:

              【解决方案9】:

              这是一个简单的方法

              是Titanic的例子

              LABEL_COL = ["Sex", "Embarked"]
              
              def label(df):
                  _df = df.copy()
                  le = LabelEncoder()
                  for col in LABEL_COL:
                      # Not NaN index
                      idx = ~_df[col].isna()
                      _df.loc[idx, col] \
                          = le.fit(_df.loc[idx, col]).transform(_df.loc[idx, col])
                  return _df
              

              【讨论】:

              • 很好,您可以立即使用 fit_transform:_df.loc[idx, col] = le.fit_transform(_df.loc[idx, col])
              【解决方案10】:

              我就是这样做的:

              import pandas as pd
              from sklearn.preprocessing import LabelEncoder
              
              UNKNOWN_TOKEN = '<unknown>'
              a = pd.Series(['A','B','C', 'D','A'], dtype=str).unique().tolist()
              a.append(UNKNOWN_TOKEN)
              le = LabelEncoder()
              le.fit_transform(a)
              embedding_map = dict(zip(le.classes_, le.transform(le.classes_)))
              

              当应用于新的测试数据时:

              test_df = test_df.apply(lambda x: x if x in embedding_map else UNKNOWN_TOKEN)
              le.transform(test_df)
              

              【讨论】:

                【解决方案11】:

                以下编码器地址每个类别中都没有值。

                class MultiColumnLabelEncoder:
                    def __init__(self):
                        self.columns = None
                        self.led = defaultdict(preprocessing.LabelEncoder)
                
                    def fit(self, X):
                        self.columns = X.columns
                        for col in self.columns:
                            cat = X[col].unique()
                            cat = [x if x is not None else "None" for x in cat]
                            self.led[col].fit(cat)
                        return self
                
                    def fit_transform(self, X):
                        if self.columns is None:
                            self.fit(X)
                        return self.transform(X)
                
                    def transform(self, X):
                        return X.apply(lambda x:  self.led[x.name].transform(x.apply(lambda e: e if e is not None else "None")))
                
                    def inverse_transform(self, X):
                        return X.apply(lambda x: self.led[x.name].inverse_transform(x))
                

                使用示例

                df = pd.DataFrame({
                    'pets': ['cat', 'dog', 'cat', 'monkey', 'dog', 'dog'],
                    'owner': ['Champ', 'Ron', 'Brick', None, 'Veronica', 'Ron'],
                    'location': ['San_Diego', 'New_York', 'New_York', 'San_Diego', 'San_Diego',
                                 None]
                })
                
                
                print(df)
                
                   location     owner    pets
                0  San_Diego     Champ     cat
                1   New_York       Ron     dog
                2   New_York     Brick     cat
                3  San_Diego      None  monkey
                4  San_Diego  Veronica     dog
                5       None       Ron     dog
                
                le = MultiColumnLabelEncoder()
                le.fit(df)
                
                transformed = le.transform(df)
                print(transformed)
                
                   location  owner  pets
                0         2      1     0
                1         0      3     1
                2         0      0     0
                3         2      2     2
                4         2      4     1
                5         1      3     1
                
                inverted = le.inverse_transform(transformed)
                print(inverted)
                
                        location     owner    pets
                0  San_Diego     Champ     cat
                1   New_York       Ron     dog
                2   New_York     Brick     cat
                3  San_Diego      None  monkey
                4  San_Diego  Veronica     dog
                5       None       Ron     dog
                

                【讨论】:

                  【解决方案12】:

                  您好,我为自己的工作做了一点计算技巧:

                  from sklearn.preprocessing import LabelEncoder
                  import pandas as pd
                  import numpy as np
                  a = pd.DataFrame(['A','B','C',np.nan,'D','A'])
                  le = LabelEncoder()
                  ### fit with the desired col, col in position 0 for this example
                  fit_by = pd.Series([i for i in a.iloc[:,0].unique() if type(i) == str])
                  le.fit(fit_by)
                  ### Set transformed col leaving np.NaN as they are
                  a["transformed"] = fit_by.apply(lambda x: le.transform([x])[0] if type(x) == str else x)
                  

                  【讨论】:

                  • fit_by是一个列表,列表没有.apply()方法,请更正
                  • 他的回答可能只是一个错字。使用 apply(function,axis=1) 或 map。例如:df['col'] = df['col'].map(lambda x: le.transform([x])[0] if type(x)==str else x)
                  【解决方案13】:

                  您可以用一些值填充 na,然后将数据框列类型更改为字符串以使其正常工作。

                  from sklearn.preprocessing import LabelEncoder
                  import pandas as pd
                  import numpy as np
                  a = pd.DataFrame(['A','B','C',np.nan,'D','A'])
                  a.fillna(99)
                  le = LabelEncoder()
                  le.fit_transform(a.astype(str))
                  

                  【讨论】:

                    【解决方案14】:

                    我遇到了同样的问题,但以上方法都不适合我。所以我在只包含“nan”的训练数据中添加了一个新行

                    【讨论】:

                      【解决方案15】:

                      不要使用带有缺失值的LabelEncoder。我不知道您使用的是哪个版本的 scikit-learn,但在 0.17.1 中,您的代码会引发 TypeError: unorderable types: str() &gt; float()。

                      如您所见,in the source 它使用numpy.unique 对数据进行编码,如果发现缺失值,则会引发TypeError。如果要对缺失值进行编码,首先将其类型改为字符串:

                      a[pd.isnull(a)]  = 'NaN'
                      

                      【讨论】:

                      • 所以您会将“NaN”编码为虚拟值?我有同样的问题,但想使用估算值进行线性回归。
                      • 模型对缺失值 (nan) 和“Nan”的处理方式不同。一种解决方法是仅使用具有非缺失值的 LabelEnconder,并让 nan 值保持不变: df['col'] = df['col'].map(lambda x: le.transform([x])[0] if type(x)==str else x)
                      猜你喜欢
                      • 1970-01-01
                      • 2021-09-24
                      • 2020-08-05
                      • 2022-01-06
                      • 1970-01-01
                      • 2018-11-22
                      • 2016-09-05
                      • 2020-06-14
                      • 1970-01-01
                      相关资源
                      最近更新 更多