pd.get_dummies
允许将分类变量转换为虚拟变量。除了重建分类变量是微不足道的事实之外,还有一种首选/快速的方法吗?
In [46]: s = Series(list('aaabbbccddefgh')).astype('category')
In [47]: s
Out[47]:
0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
dtype: category
Categories (8, object): [a < b < c < d < e < f < g < h]
In [48]: df = pd.get_dummies(s)
In [49]: df
Out[49]:
a b c d e f g h
0 1 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0
2 1 0 0 0 0 0 0 0
3 0 1 0 0 0 0 0 0
4 0 1 0 0 0 0 0 0
5 0 1 0 0 0 0 0 0
6 0 0 1 0 0 0 0 0
7 0 0 1 0 0 0 0 0
8 0 0 0 1 0 0 0 0
9 0 0 0 1 0 0 0 0
10 0 0 0 0 1 0 0 0
11 0 0 0 0 0 1 0 0
12 0 0 0 0 0 0 1 0
13 0 0 0 0 0 0 0 1
In [50]: x = df.stack()
# I don't think you actually need to specify ALL of the categories here, as by definition
# they are in the dummy matrix to start (and hence the column index)
In [51]: Series(pd.Categorical(x[x!=0].index.get_level_values(1)))
Out[51]:
0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
Name: level_1, dtype: category
Categories (8, object): [a < b < c < d < e < f < g < h]
所以我认为我们需要一个“做”这个功能,因为它似乎是一个自然的操作。也许get_categories()
,请参阅here
已经有几年了,所以在最初提出这个问题时,这可能不会出现在pandas
工具包中,但这种方法对我来说似乎有点容易。 idxmax
将返回对应于最大元素(即具有1
的元素)的索引。我们做axis=1
因为我们想要1
出现的列名。
编辑:我不打算将它分类而不仅仅是一个字符串,但你可以像@Jeff一样用pd.Categorical
(和pd.Series
,如果需要)包装它。
In [1]: import pandas as pd
In [2]: s = pd.Series(['a', 'b', 'a', 'c'])
In [3]: s
Out[3]:
0 a
1 b
2 a
3 c
dtype: object
In [4]: dummies = pd.get_dummies(s)
In [5]: dummies
Out[5]:
a b c
0 1 0 0
1 0 1 0
2 1 0 0
3 0 0 1
In [6]: s2 = dummies.idxmax(axis=1)
In [7]: s2
Out[7]:
0 a
1 b
2 a
3 c
dtype: object
In [8]: (s2 == s).all()
Out[8]: True
编辑以回应@ piRSquared的评论:这个解决方案确实假设每行有一个1
。我认为这通常是一种格式。如果你有pd.get_dummies
或者有drop_first=True
值和NaN
(默认)(任何我缺少的情况?),dummy_na=False
可以返回全0的行。一行全零将被视为它是第一列中命名的变量的实例(例如,上例中的a
)。
如果drop_first=True
,你无法从虚拟数据框中知道“第一”变量的名称是什么,所以除非你保留额外的信息,否则操作是不可逆的;我建议离开drop_first=False
(默认)。
由于dummy_na=False
是默认值,这肯定会导致问题。如果你想使用这个解决方案来反转“dummification”并且你的数据包含任何dummy_na=True
,请在调用pd.get_dummies
时设置NaNs
。设置dummy_na=True
将始终添加“nan”列,即使该列全部为0,因此除非您实际拥有NaN
s,否则您可能不希望设置此列。一个很好的方法可能是设置dummies = pd.get_dummies(series, dummy_na=series.isnull().any())
。同样好的是,idxmax
解决方案将正确地重新生成你的NaN
s(不仅仅是一个说“nan”的字符串)。
还值得一提的是,设置drop_first=True
和dummy_na=False
意味着NaN
s与第一个变量的实例无法区分,因此如果您的数据集可能包含任何NaN
值,则强烈建议不要这样做。
这是一个相当晚的答案,但由于你要求快速做到这一点,我认为你正在寻找最高效的策略。在大型数据帧(例如10000行)上,使用np.where
而不是idxmax
或get_level_values
可以获得非常显着的速度提升,并获得相同的结果。我们的想法是索引虚拟数据帧不为0的列名:
使用与@Nathan相同的样本数据:
>>> dummies
a b c
0 1 0 0
1 0 1 0
2 1 0 0
3 0 0 1
s2 = pd.Series(dummies.columns[np.where(dummies!=0)[1]])
>>> s2
0 a
1 b
2 a
3 c
dtype: object
在一个小的虚拟数据帧上,您将看不到性能上的太大差异。但是,测试不同的策略来解决大型系列中的这个问题:
s = pd.Series(np.random.choice(['a','b','c'], 10000))
dummies = pd.get_dummies(s)
def np_method(dummies=dummies):
return pd.Series(dummies.columns[np.where(dummies!=0)[1]])
def idx_max_method(dummies=dummies):
return dummies.idxmax(axis=1)
def get_level_values_method(dummies=dummies):
x = dummies.stack()
return pd.Series(pd.Categorical(x[x!=0].index.get_level_values(1)))
def dot_method(dummies=dummies):
return dummies.dot(dummies.columns)
import timeit
# Time each method, 1000 iterations each:
>>> timeit.timeit(np_method, number=1000)
1.0491090340074152
>>> timeit.timeit(idx_max_method, number=1000)
12.119140846014488
>>> timeit.timeit(get_level_values_method, number=1000)
4.109266621991992
>>> timeit.timeit(dot_method, number=1000)
1.6741622970002936
np.where
方法比get_level_values
方法快4倍,比idxmax
方法快11.5倍!它还击败(但只是一点点).dot()
中概述的this answer to a similar question方法
它们都返回相同的结果:
>>> (get_level_values_method() == np_method()).all()
True
>>> (idx_max_method() == np_method()).all()
True
使用@ Jeff的设置
s = Series(list('aaabbbccddefgh')).astype('category')
df = pd.get_dummies(s)
并且每行只有一个1
df.dot(df.columns)
0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
dtype: object
numpy.where
再次!假设每行只有一个1
i, j = np.where(df)
pd.Series(df.columns[j], i)
0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
dtype: category
Categories (8, object): [a, b, c, d, e, f, g, h]
numpy.where
不假设每行一个1
i, j = np.where(df)
pd.Series(dict(zip(zip(i, j), df.columns[j])))
0 0 a
1 0 a
2 0 a
3 1 b
4 1 b
5 1 b
6 2 c
7 2 c
8 3 d
9 3 d
10 4 e
11 5 f
12 6 g
13 7 h
dtype: object
numpy.where
我们不假设每行一个1
,我们放弃索引
i, j = np.where(df)
pd.Series(dict(zip(zip(i, j), df.columns[j]))).reset_index(-1, drop=True)
0 a
1 a
2 a
3 b
4 b
5 b
6 c
7 c
8 d
9 d
10 e
11 f
12 g
13 h
dtype: object
将dat [“分类”]转换为一个热编码并返回!!
import pandas as pd
from sklearn.preprocessing import LabelEncoder
dat["labels"]= le.fit_transform(dat["classification"])
Y= pd.get_dummies(dat["labels"])
tru=[]
for i in range(0, len(Y)):
tru.append(np.argmax(Y.iloc[i]))
tru= le.inverse_transform(tru)
##Identical check!
(tru==dat["classification"]).value_counts()