我有一个系列,比如:
df['ID'] = ['ABC123', 'IDF345', ...]
我正在使用scikit的LabelEncoder
将其转换为数值以输入RandomForestClassifier
。
在培训期间,我执行以下操作:
le_id = LabelEncoder()
df['ID'] = le_id.fit_transform(df.ID)
但是,现在对于测试/预测,当我传入新数据时,我想根据le_id
从这些数据中转换"ID",即,如果存在相同的值,则根据上面的标签编码器对其进行转换,否则分配一个新的数值。
在测试文件中,我执行以下操作:
new_df['ID'] = le_dpid.transform(new_df.ID)
但是,我收到以下错误:ValueError: y contains new labels
我该如何解决这个问题??谢谢!
更新:
所以我的任务是使用以下(例如)作为训练数据,并预测新BankNum,ID组合的'High', 'Mod', 'Low'
值。模型应该学习给出"高"的特征,其中给出训练数据集的"低"。例如,当有多个条目具有相同的BankNum和不同的ID时,会给出"高"。
df =
BankNum | ID | Labels
0098-7772 | AB123 | High
0098-7772 | ED245 | High
0098-7772 | ED343 | High
0870-7771 | ED200 | Mod
0870-7771 | ED100 | Mod
0098-2123 | GH564 | Low
然后预测如下:
BankNum | ID |
00982222 | AB999 |
00982222 | AB999 |
00981111 | AB890 |
我正在做这样的事情:
df['BankNum'] = df.BankNum.astype(np.float128)
le_id = LabelEncoder()
df['ID'] = le_id.fit_transform(df.ID)
X_train, X_test, y_train, y_test = train_test_split(df[['BankNum', 'ID'], df.Labels, test_size=0.25, random_state=42)
clf = RandomForestClassifier(random_state=42, n_estimators=140)
clf.fit(X_train, y_train)
我认为错误消息非常明确:您的测试数据集包含ID
标签,这些标签尚未包含在训练数据集中。对于此项,LabelEncoder
找不到合适的数值来表示。有几种方法可以解决此问题。您可以尝试平衡数据集,以确保每个标签不仅存在于测试中,还存在于训练数据中。否则,您可以尝试遵循此处提出的想法之一。
一种可能的解决方案是,在开始时搜索数据集,获取所有唯一ID
值的列表,在此列表中训练LabelEncoder
,并使其余代码保持当前状态。
另一种可能的解决方案是,检查测试数据是否只有训练过程中看到的标签。如果有新标签,则必须将其设置为某个回退值,例如unknown_id
(或类似的东西)。这样做,您将所有新的、未知ID
放在一个类中;对于此项,预测将失败,但您可以像现在一样使用其余代码。
你可以从"sklearn"尝试解决方案。标签编码器与前所未见的值"https://stackoverflow.com/a/48169252/9043549 事情是用类创建字典,而不是映射列并用一些"已知值"填充新类
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
suf="_le"
col="a"
df[col+suf] = le.fit_transform(df[col])
dic = dict(zip(le.classes_, le.transform(le.classes_)))
col='b'
df[col+suf]=df[col].map(dic).fillna(dic["c"]).astype(int)
如果你的数据pd.DataFrame
我建议你这个简单的解决方案......
我构建了一个自定义转换器,用于整数映射每个分类特征。安装后,您可以转换所需的所有数据。您还可以计算简单的标签编码或 onehot 编码。
如果新数据中存在新的看不见的类别或 NaN:
1]对于标签编码,0 是为映射这些情况而保留的特殊标记。
2]对于 onehot 编码,在这些情况下,所有 onehot 列都是零。
class FeatureTransformer:
def __init__(self, categorical_features):
self.categorical_features = categorical_features
def fit(self, X):
if not isinstance(X, pd.DataFrame):
raise ValueError("Pass a pandas.DataFrame")
if not isinstance(self.categorical_features, list):
raise ValueError(
"Pass categorical_features as a list of column names")
self.encoding = {}
for c in self.categorical_features:
_, int_id = X[c].factorize()
self.encoding[c] = dict(zip(list(int_id), range(1,len(int_id)+1)))
return self
def transform(self, X, onehot=True):
if not isinstance(X, pd.DataFrame):
raise ValueError("Pass a pandas.DataFrame")
if not hasattr(self, 'encoding'):
raise AttributeError("FeatureTransformer must be fitted")
df = X.drop(self.categorical_features, axis=1)
if onehot: # one-hot encoding
for c in sorted(self.categorical_features):
categories = X[c].map(self.encoding[c]).values
for val in self.encoding[c].values():
df["{}_{}".format(c,val)] = (categories == val).astype('int16')
else: # label encoding
for c in sorted(self.categorical_features):
df[c] = X[c].map(self.encoding[c]).fillna(0)
return df
用法:
X_train = pd.DataFrame(np.random.randint(10,20, (100,10)))
X_test = pd.DataFrame(np.random.randint(20,30, (100,10)))
ft = FeatureTransformer(categorical_features=[0,1,3])
ft.fit(X_train)
ft.transform(X_test, onehot=False).shape
这实际上是LabelEncoder上的一个已知错误:BUG for fit_transform ...基本上你必须适应它,然后转换。它会正常工作!建议将编码器的字典保留到每一列,以便在逆变换中能够检索原始分类值。
在处理DataFrames
时,我能够在心理上更好地处理操作。下面的方法使用训练数据拟合和变换LabelEncoder()
,然后使用一系列pd.merge
联接将训练的拟合/转换编码器值映射到测试数据。当训练数据中未显示测试数据值时,代码默认为最大训练编码器值 + 1。
# encode class values as integers
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
encoder.fit(y_train)
encoded_y_train = encoder.transform(y_train)
# make a dataframe of the unique train values and their corresponding encoded integers
y_map = pd.DataFrame({'y_train': y_train, 'encoded_y_train': encoded_y_train})
y_map = y_map.drop_duplicates()
# map the unique test values to the trained encoded integers
y_test_df = pd.DataFrame({'y_test': y_test})
y_test_unique = y_test_df.drop_duplicates()
y_join = pd.merge(y_test_unique, y_map,
left_on = 'y_test', right_on = 'y_train',
how = 'left')
# if the test category is not found in the training category group, then make the
# value the maximum value of the training group + 1
y_join['encoded_y_test'] = np.where(y_join['encoded_y_train'].isnull(),
y_map.shape[0] + 1,
y_join['encoded_y_train']).astype('int')
encoded_y_test = pd.merge(y_test_df, y_join, on = 'y_test', how = 'left')
.encoded_y_test.values
我发现了一个围绕这个问题的简单技巧。
假设 X 是特征的数据帧,
-
首先,我们需要创建一个字典列表,该字典将键作为从 0 开始的可迭代对象,相应的值对将是分类列名。我们使用枚举轻松完成此操作。
cat_cols_enum = list(enumerate(X.select_dtypes(include = ['O']).columns))
-
然后,我们的想法是创建一个标签编码器列表,其维度等于数据帧 X 中存在的定性(分类)列数。
le = [LabelEncoder() for i in range(len(cat_cols_enum))]
-
接下来和最后一部分将分别将编码器列表中的每个标签编码器与字典列表中存在的每个分类列的唯一值相匹配。
for i in cat_cols_enum: le[i[0]].fit(X[i[1]].value_counts().index)
现在,我们可以使用以下方法将标签转换为各自的编码
for i in cat_cols_enum:
X[i[1]] = le[i[0]].transform(X[i[1]])
当transform
函数获取LabelEncoder
尝试编码的任何新值时,并且因为在训练样本中,当您使用fit_transform
时,该特定值不会出现在语料库中时,就会出现此错误。所以有一个技巧,如果您确定没有新值会更进一步,是否将所有唯一值与fit_transform
函数一起使用,或者尝试一些适合问题陈述的不同编码方法,如HashingEncoder
。
这是测试中没有进一步新值的示例
le_id.fit_transform(list(set(df['ID'].unique()).union(set(new_df['ID'].unique()))))
new_df['ID'] = le_id.transform(new_df.ID)
我希望这对某人有所帮助,因为它是最近的。
SKLEARN使用fit_transform来执行拟合函数和转换函数,以指示标签编码。 要解决看不见的值的 Y 标签抛出错误问题,请使用:
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le.fit_transform(Col)
这就解决了!
我用
le.fit_transform(Col)
我能够解决问题。它确实适合并改变了两者。我们不需要担心测试拆分中的未知值