【问题标题】:How to run model on new data that requires pd.get_dummies如何在需要 pd.get_dummies 的新数据上运行模型
【发布时间】:2026-01-31 19:40:01
【问题描述】:

我有一个运行以下内容的模型:

import pandas as pd
import numpy as np

# initialize list of lists 
data = [['tom', 10,1,'a'], ['tom', 15,5,'a'], ['tom', 14,1,'a'], ['tom', 15,4,'b'], ['tom', 18,1,'b'], ['tom', 15,6,'a'], ['tom', 17,3,'a']
       , ['tom', 14,7,'b'], ['tom',16 ,6,'a'], ['tom', 22,2,'a'],['matt', 10,1,'c'], ['matt', 15,5,'b'], ['matt', 14,1,'b'], ['matt', 15,4,'a'], ['matt', 18,1,'a'], ['matt', 15,6,'a'], ['matt', 17,3,'a']
       , ['matt', 14,7,'c'], ['matt',16 ,6,'b'], ['matt', 10,2,'b']]

# Create the pandas DataFrame 
df = pd.DataFrame(data, columns = ['Name', 'Attempts','Score','Category']) 

print(df.head(2))
  Name  Attempts  Score Category
0  tom        10      1        a
1  tom        15      5        a

然后我使用以下代码创建了一个虚拟 df 以在模型中使用:

from sklearn.linear_model import LogisticRegression

df_dum = pd.get_dummies(df)
print(df_dum.head(2))
  Attempts  Score  Name_matt  Name_tom  Category_a  Category_b  Category_c
0        10      1          0         1           1           0           0
1        15      5          0         1           1           0           0

然后我创建了以下模型:

#Model

X = df_dum.drop(('Score'),axis=1)
y = df_dum['Score'].values

#Training Size
train_size = int(X.shape[0]*.7)
X_train = X[:train_size]
X_test = X[train_size:]
y_train = y[:train_size]
y_test = y[train_size:]


#Fit Model
model = LogisticRegression(max_iter=1000)
model.fit(X_train,y_train)


#Send predictions back to dataframe
Z = model.predict(X_test)
zz = model.predict_proba(X_test)

df.loc[train_size:,'predictions']=Z
dfpredictions = df.dropna(subset=['predictions'])

print(dfpredictions)
    Name  Attempts  Score Category  predictions
14  matt        18      1        a          1.0
15  matt        15      6        a          1.0
16  matt        17      3        a          1.0
17  matt        14      7        c          1.0
18  matt        16      6        b          1.0
19  matt        10      2        b          1.0

现在我有了想要预测的新数据:

newdata = [['tom', 10,'a'], ['tom', 15,'a'], ['tom', 14,'a']]

newdf = pd.DataFrame(newdata, columns = ['Name', 'Attempts','Category']) 

print(newdf)

 Name  Attempts Category
0  tom        10        a
1  tom        15        a
2  tom        14        a

然后创建假人并运行预测

newpredict = pd.get_dummies(newdf)

predict = model.predict(newpredict)

输出:

ValueError: X has 3 features per sample; expecting 6

这是有道理的,因为没有类别 bc,也没有名为 matt 的名称。

我的问题是,鉴于我的新数据并不总是包含原始数据中使用的完整列集,如何设置此模型的最佳方式是什么。每天我都有新数据,所以我不太确定最有效和无错误的方法。

这是一个示例数据 - 我的数据集在运行 pd.get_dummies 时有 2000 列。非常感谢!

【问题讨论】:

  • 我记得在这个网站上进行了长时间的讨论,其中人们得出的结论是,出于这个确切原因,最好使用sklearn one-hot 编码器。
  • @NicolasGervais 明白了。如果你使用同一个 OneHotEncoder 对象,它会记住你第一次适应它时总共有多少列。

标签: python scikit-learn


【解决方案1】:

让我更详细地解释一下 Nicolas 和 BlueSkyz 的建议。

pd.get_dummies 在您确定生产/新数据集中的特定分类变量不会有任何新类别时很有用,例如基于贵公司或数据库的内部数据分类/一致性规则的性别、产品等。

但是,对于大多数机器学习任务,您可以预期未来会有新的类别在模型训练中未使用,sklearn.OneHotEncoder 应该是标准选择。可以将sklearn.OneHotEncoderhandle_unknown 参数设置为'ignore' 以做到这一点:在将来应用编码器时忽略新类别。来自documentation

如果在转换期间存在未知的分类特征,是否引发错误或忽略(默认为引发)。当此参数设置为“忽略”并且在转换过程中遇到未知类别时,此功能的生成的 one-hot 编码列将全为零。在逆变换中,未知类别将表示为None

您的示例基于 LabelEncoding 和 OneHotEncoding 的完整流程如下:

# Create a categorical boolean mask
categorical_feature_mask = df.dtypes == object
# Filter out the categorical columns into a list for easy reference later on in case you have more than a couple categorical columns
categorical_cols = df.columns[categorical_feature_mask].tolist()

# Instantiate the OneHotEncoder Object
from sklearn.preprocessing import OneHotEncoder
ohe = OneHotEncoder(handle_unknown='ignore', sparse = False)
# Apply ohe on data
ohe.fit(df[categorical_cols])
cat_ohe = ohe.transform(df[categorical_cols])

#Create a Pandas DataFrame of the hot encoded column
ohe_df = pd.DataFrame(cat_ohe, columns = ohe.get_feature_names(input_features = categorical_cols))
#concat with original data and drop original columns
df_ohe = pd.concat([df, ohe_df], axis=1).drop(columns = categorical_cols, axis=1)

# The following code is for your newdf after training and testing on original df
# Apply ohe on newdf
cat_ohe_new = ohe.transform(newdf[categorical_cols])
#Create a Pandas DataFrame of the hot encoded column
ohe_df_new = pd.DataFrame(cat_ohe_new, columns = ohe.get_feature_names(input_features = categorical_cols))
#concat with original data and drop original columns
df_ohe_new = pd.concat([newdf, ohe_df_new], axis=1).drop(columns = categorical_cols, axis=1)

# predict on df_ohe_new
predict = model.predict(df_ohe_new)

输出(您可以分配回 newdf):

array([1, 1, 1])

但是,如果您真的只想使用pd.get_dummies,那么以下方法也可以:

newpredict = newpredict.reindex(labels = df_dum.columns, axis = 1, fill_value = 0).drop(columns = ['Score'])
predict = model.predict(newpredict)

上面的代码 sn-p 将确保您在新虚拟对象 df (newpredict) 中具有与原始 df_dum 相同的列(具有 0 值)并删除 'Score' 列。这里的输出和上面一样。此代码将确保新数据集中存在但现在在原始训练数据中的任何分类值都将被删除,同时保持与原始 df 中的列顺序相同。

编辑: 我忘记补充的一件事是,pd.get_dummies 的执行速度通常比 sklearn.OneHotEncoder 快得多

【讨论】:

  • 很高兴能帮助到@SOK。如果您在实施过程中遇到任何问题,请告诉我。
  • 感谢@finlytics-hub。我目前唯一遇到的问题是TypeError: ('argument must be a string or number',当我处于ohe.fit(dfmodel[categorical_cols]) 阶段时。有什么要调试的想法吗?
  • @SOK 你能检查categorical_cols 的输出吗?应该是这样的['Name', 'Category']。我假设您在ohe.fit 中引用的dfmodel 与您在原始帖子中定义的df 完全相同?
  • 是的,抱歉 dfmodeldf 相同。当我调试它时,它列出了categorical_cols,正如你所说,在我的例子中它有大约 7 列(都是分类的)所以我不太清楚为什么
  • (我正在申请我更大的模型)它的列比上述问题更多