并非所有类别都存在时的虚拟变量
Posted
技术标签:
【中文标题】并非所有类别都存在时的虚拟变量【英文标题】:Dummy variables when not all categories are present 【发布时间】:2016-09-22 09:33:29 【问题描述】:我有一组数据框,其中一个列包含一个分类变量。我想将其转换为几个虚拟变量,在这种情况下,我通常会使用get_dummies
。
发生的情况是get_dummies
查看每个数据帧中可用的数据以找出有多少类别,从而创建适当数量的虚拟变量。但是,在我现在正在处理的问题中,我实际上提前知道可能的类别是什么。但是,当单独查看每个数据框时,不一定会出现所有类别。
我的问题是:有没有办法将类别的名称传递给get_dummies
(或等效函数),这样,对于未出现在给定数据框中的类别,它只会创建一列 0?
可以做到这一点的东西:
categories = ['a', 'b', 'c']
cat
1 a
2 b
3 a
变成这样:
cat_a cat_b cat_c
1 1 0 0
2 0 1 0
3 1 0 0
【问题讨论】:
您正在寻找sklearn.OneHotEncoder
。看这里:scikit-learn.org/stable/modules/generated/…
@ssm: get_dummies
实现了与OneHotEncoder
相同的功能,另外一个好处是输出是一个易于理解的带有标记列的pandas 数据框,而不是普通的ndarray
。
我误解了这个问题。谢谢!
我认为对于模型训练,没有必要执行这一步。如果训练数据中缺少一个类别,无论您是否提供全为零的列,您的模型都不会学到任何有助于预测包含该变量的测试实例的内容。
@Quickbeam2k1 不需要这样做的情况很少。对于初始原型设计,它可能不是,但对于任何生产代码,您都希望确保所有模型输入具有相同的列。
【参考方案1】:
TL;DR:
pd.get_dummies(cat.astype(pd.CategoricalDtype(categories=categories)))
大熊猫:pd.get_dummies(cat.astype('category', categories=categories))
有没有办法将类别的名称传递给 get_dummies(或等效函数),这样,对于未出现在给定数据框中的类别,它只会创建一列 0?
是的,有! Pandas 有一个特殊类型的 Series 仅用于 categorical data。本系列的属性之一是get_dummies
考虑的可能类别。这是一个例子:
In [1]: import pandas as pd
In [2]: possible_categories = list('abc')
In [3]: cat = pd.Series(list('aba'))
In [4]: cat = cat.astype(pd.CategoricalDtype(categories=possible_categories))
In [5]: cat
Out[5]:
0 a
1 b
2 a
dtype: category
Categories (3, object): [a, b, c]
那么,get_dummies
会做你想做的事!
In [6]: pd.get_dummies(cat)
Out[6]:
a b c
0 1 0 0
1 0 1 0
2 1 0 0
还有很多其他方法可以创建分类Series
或DataFrame
,这只是我觉得最方便的一种。您可以在the pandas documentation 阅读所有这些内容。
编辑:
我没有遵循确切的版本控制,但至少在 0.17.0 版之前,pandas 如何处理稀疏矩阵有一个bug。版本 0.18.1(2016 年 5 月发布)已对其进行了更正。
对于 0.17.0 版,如果您尝试使用带有 DataFrame
的 sparse=True
选项执行此操作,则缺少的虚拟变量的零列将是 NaN
列,并且它将被转换到稠密。
看起来 pandas 0.21.0 添加了一个CategoricalDType
,并且创建明确包含原始答案中的类别的分类已被弃用,我不太确定何时。
【讨论】:
很好,我不知道 Pandas 中的数据类型,谢谢! 嗯,我选择了 piRSquared 的答案,因为它清晰、简洁,并且适合我已有的代码。另外,它是我最终在我所做的事情中使用的那个,所以在某种程度上它解决了我的问题。你的整体信息量更大,当然,但这不是我最终使用的那个,这就是为什么我没有把它改成你的,对不起......如果我给它加分不过可以。 好像 astype 不再接受类别,您需要通过 CategoricalDtype(categories=[...]) 代替 (***.com/questions/37952128/…) 我刚刚尝试使用 pandas (1.0.1),pd.Series.astype('category') 确实有效,但无法识别附加参数 categories=[...]。 @NicoLi,啊,这似乎遵循文档。我相应地更新了答案。【参考方案2】:使用转置和重新索引
import pandas as pd
cats = ['a', 'b', 'c']
df = pd.DataFrame('cat': ['a', 'b', 'a'])
dummies = pd.get_dummies(df, prefix='', prefix_sep='')
dummies = dummies.T.reindex(cats).T.fillna(0)
print dummies
a b c
0 1.0 0.0 0.0
1 0.0 1.0 0.0
2 1.0 0.0 0.0
【讨论】:
通过使用reindex
的columns
关键字(即dummies.reindex(columns=cats)
),你不需要做双重转置。
另外reindex
有一个fill_value
参数,它可以完成您对fillna
所做的事情。因此,打印结果之前的行可以使用:dummies = dummies.reindex(columns=cats, fill_value=0)
.
不知道 'cats' 的列数怎么办?
@datascana df['cat'].unique()
将为您提供数据中实际存在的所有值的列表。
一个最近被标记为欺骗的问题被允许发现这个答案的宝石。【参考方案3】:
试试这个:
In[1]: import pandas as pd
cats = ["a", "b", "c"]
In[2]: df = pd.DataFrame("cat": ["a", "b", "a"])
In[3]: pd.concat((pd.get_dummies(df.cat, columns=cats), pd.DataFrame(columns=cats))).fillna(0)
Out[3]:
a b c
0 1.0 0.0 0
1 0.0 1.0 0
2 1.0 0.0 0
【讨论】:
这里的get_dummies
中的columns=cats
实际上并没有做任何事情。 columns
选项用于选择要使用虚拟变量编码的原始数据帧的子集。如果请求的列没有出现在数据框中,它似乎会忽略它。它似乎应该产生一个错误,但它没有【参考方案4】:
我确实在 pandas github 上问过这个问题。事实证明,当您将列定义为 Categorical
并在其中定义所有可能的类别时,很容易绕过它。
df['col'] = pd.Categorical(df['col'], categories=['a', 'b', 'c', 'd'])
get_dummies()
将按预期完成剩下的工作。
【讨论】:
【参考方案5】:我不认为get_dummies
提供了开箱即用的功能,它只允许创建一个额外的column
来突出NaN
值。
要自己添加缺少的columns
,您可以使用pd.concat
和axis=0
垂直“堆叠”DataFrames
(虚拟列加上DataFrame
id
)并自动创建任何缺少的列,用fillna(0)
替换缺失值,再用.groupby('id')
分隔各个DataFrame
。
【讨论】:
是的,这是我想过的替代方案,但我希望可能已经实现了一些更易于使用的东西(不一定是get_dummies
,但我发现的唯一其他替代方案是sklearn
的OneHotEncoder
似乎也没有多大帮助......)
您也可以跳过get_dummies
,直接根据类别列本身创建所有0
-1
列。我想这取决于你的问题的大小。【参考方案6】:
在测试集中添加缺失的类别:
# Get missing columns in the training test
missing_cols = set( train.columns ) - set( test.columns )
# Add a missing column in test set with default value equal to 0
for c in missing_cols:
test[c] = 0
# Ensure the order of column in the test set is in the same order than in train set
test = test[train.columns]
请注意,此代码还删除了测试数据集中类别产生的列,但训练数据集中不存在
【讨论】:
【参考方案7】:根据其他人的建议 - 将您的分类特征转换为“类别”数据类型应该可以使用“get_dummies”解决看不见的标签问题。
# Your Data frame(df)
from sklearn.model_selection import train_test_split
X = df.loc[:,df.columns !='label']
Y = df.loc[:,df.columns =='label']
# Split the data into 70% training and 30% test
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.3)
# Convert Categorical Columns in your data frame to type 'category'
for col in df.select_dtypes(include=[np.object]).columns:
X_train[col] = X_train[col].astype('category', categories = df[col].unique())
X_test[col] = X_test[col].astype('category', categories = df[col].unique())
# Now, use get_dummies on training, test data and we will get same set of columns
X_train = pd.get_dummies(X_train,columns = ["Categorical_Columns"])
X_test = pd.get_dummies(X_test,columns = ["Categorical_Columns"])
【讨论】:
【参考方案8】:越短越好:
import pandas as pd
cats = pd.Index(['a', 'b', 'c'])
df = pd.DataFrame('cat': ['a', 'b', 'a'])
pd.get_dummies(df, prefix='', prefix_sep='').reindex(columns = cats, fill_value=0)
结果:
a b c
0 1 0 0
1 0 1 0
2 1 0 0
注意事项:
cats
需要是熊猫索引
需要设置prefix=''
和prefix_sep=''
才能使用您首先定义的猫类别。否则,get_dummies
将转换为:cats_a
、cats_b
和 cats_c
)。对我来说这更好,因为它是明确的。
使用fill_value=0 将NaN
从列c
转换。或者,您可以在句末使用fillna(0)
。 (我不知道哪个更快)。
这是一个更短的版本(更改了索引值):
import pandas as pd
cats = pd.Index(['cat_a', 'cat_b', 'cat_c'])
df = pd.DataFrame('cat': ['a', 'b', 'a'])
pd.get_dummies(df).reindex(columns = cats, fill_value=0)
结果:
cat_a cat_b cat_c
0 1 0 0
1 0 1 0
2 1 0 0
奖励曲目!
我想您之所以拥有这些类别,是因为您之前使用训练数据做了一个虚拟/热门。您可以保存原始编码(.columns
),然后在生产期间应用:
cats = pd.Index(['cat_a', 'cat_b', 'cat_c']) # it might come from the original onehot encoding (df_ohe.columns)
import pickle
with open('cats.pickle', 'wb') as handle:
pickle.dump(cats, handle, protocol=pickle.HIGHEST_PROTOCOL)
with open('cats.pickle', 'rb') as handle:
saved_cats = pickle.load(handle)
df = pd.DataFrame('cat': ['a', 'b', 'a'])
pd.get_dummies(df).reindex(columns = saved_cats, fill_value=0)
结果:
cat_a cat_b cat_c
0 1 0 0
1 0 1 0
2 1 0 0
【讨论】:
【参考方案9】:如果您知道您的类别,您可以按照您的建议先申请pd.get_dummies()
,然后添加缺少的类别列。
这将使用缺少的cat_c
创建您的示例:
import pandas as pd
categories = ['a', 'b', 'c']
df = pd.DataFrame(list('aba'), columns=['cat'])
df = pd.get_dummies(df)
print(df)
cat_a cat_b
0 1 0
1 0 1
2 1 0
现在只需使用联合操作添加缺少的类别列(如suggested here)。
possible_categories = ['cat_' + cat for cat in categories]
df = df.reindex(df.columns.union(possible_categories, sort=False), axis=1, fill_value=0)
print(df)
cat_a cat_b cat_c
0 1 0 0
1 0 1 0
2 1 0 0
【讨论】:
【参考方案10】:我最近想解决同样的问题,但使用的是多列数据框和两个数据集(用于机器学习任务的训练集和测试集)。测试数据帧与训练数据帧具有相同的分类列,但其中一些列缺少训练数据帧中存在的类别。
我不想为每一列手动定义所有可能的类别。相反,我将训练和测试数据帧合并为一个,称为 get_dummies,然后将其拆分回两个。
# train_cat, test_cat are dataframes instantiated elsewhere
train_test_cat = pd.concat([train_cat, test_cat]
tran_test_cat = pd.get_dummies(train_test_cat, axis=0))
train_cat = train_test_cat.iloc[:train_cat.shape[0], :]
test_cat = train_test_cat.iloc[train_cat.shape[0]:, :]
【讨论】:
你不应该混合训练和测试。它可能适用于您的示例,但我们应该将测试视为我们以前从未见过的数据。如果我们真的像你说的那样,我们可能会在训练集中添加一个不在训练集上的标签。以上是关于并非所有类别都存在时的虚拟变量的主要内容,如果未能解决你的问题,请参考以下文章