从熊猫中的假人重建一个分类变量

问题描述 投票:23回答:5

pd.get_dummies允许将分类变量转换为虚拟变量。除了重建分类变量是微不足道的事实之外,还有一种首选/快速的方法吗?

python pandas
5个回答
16
投票
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


31
投票

已经有几年了,所以在最初提出这个问题时,这可能不会出现在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,因此除非您实际拥有NaNs,否则您可能不希望设置此列。一个很好的方法可能是设置dummies = pd.get_dummies(series, dummy_na=series.isnull().any())。同样好的是,idxmax解决方案将正确地重新生成你的NaNs(不仅仅是一个说“nan”的字符串)。

还值得一提的是,设置drop_first=Truedummy_na=False意味着NaNs与第一个变量的实例无法区分,因此如果您的数据集可能包含任何NaN值,则强烈建议不要这样做。


8
投票

这是一个相当晚的答案,但由于你要求快速做到这一点,我认为你正在寻找最高效的策略。在大型数据帧(例如10000行)上,使用np.where而不是idxmaxget_level_values可以获得非常显着的速度提升,并获得相同的结果。我们的想法是索引虚拟数据帧不为0的列名:

Method:

使用与@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

Benchmark:

在一个小的虚拟数据帧上,您将看不到性能上的太大差异。但是,测试不同的策略来解决大型系列中的这个问题:

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

3
投票

建立

使用@ 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

0
投票

将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()
© www.soinside.com 2019 - 2024. All rights reserved.