Pandas:计算数据框中唯一值的数量

8
我们有一个如下所示的DataFrame:
> df.ix[:2,:10]
    0   1   2   3   4   5   6   7   8   9   10
0   NaN NaN NaN NaN  6   5  NaN NaN  4  NaN  5
1   NaN NaN NaN NaN  8  NaN NaN  7  NaN NaN  5
2   NaN NaN NaN NaN NaN  1  NaN NaN NaN NaN NaN

我们只需对DataFrame中所有唯一值进行计数。一个简单的解决方案是:
df.stack().value_counts() 

然而: 1. 看起来stack返回的是一个副本而不是视图,在这种情况下会占用过多的内存。这个说法正确吗? 2. 我想按行对数据框进行分组,然后获取每个分组的不同直方图。如果我们忽略stack的内存问题并现在使用它,如何正确地完成分组?

d = pd.DataFrame([[nan, 1, nan, 2, 3],
              [nan, 1, 1, 1, 3],
              [nan, 1, nan, 2, 3],
              [nan,2,2,2, 3]])

len(d.stack()) #14
d.stack().groupby(arange(4))
AssertionError: Grouper and axis must be same length

堆叠的DataFrame具有MultiIndex,其长度小于n_rows*n_columns的一些数字,因为nan已被删除。

0  1    1
   3    2
   4    3
1  0    1
   1    1
   2    1
   3    1
   4    3
    ....

这意味着我们不太知道如何构建我们的分组。只操作第一层会更好,但那样我就无法应用我真正想要的分组。

d.stack().groupby(level=0).groupby(list('aabb'))
KeyError: 'a'

编辑:一种不使用堆叠的解决方案:

f = lambda x: pd.value_counts(x.values.ravel())
d.groupby(list('aabb')).apply(f)
a  1    4
   3    2
   2    1
b  2    4
   3    2
   1    1
dtype: int64

看起来有些笨重。如果有更好的选择,我很乐意听取建议。

编辑:丹的评论揭示了我的一个拼写错误,不过即使更正了这个错误,我们还是没有达到目标。


df.stack().groupby(list('aaaabbbb')).value_counts() 对我来说可行。 - Dan Allan
@DanAllan 嗯,value_counts的快捷方式在0.13+版本中已被关闭(我认为它已经被列入白名单了,但可能不是...) - Andy Hayden
2个回答

7

我认为你正在进行行/列操作,因此可以使用 apply

In [11]: d.apply(pd.Series.value_counts, axis=1).fillna(0)
Out[11]: 
   1  2  3
0  1  1  1
1  4  0  1
2  1  1  1
3  0  4  1

注意:0.14版本中将推出一个名为value_counts的DataFrame方法,这将使操作更加高效和简洁。
值得注意的是,pandas的value_counts函数也适用于numpy数组,因此您可以将DataFrame的值作为1维数组视图传递给该函数(使用np.ravel函数)。
In [21]: pd.value_counts(d.values.ravel())
Out[21]: 
2    6
1    6
3    4
dtype: int64

另外,您接近正确答案了,但需要堆叠和取消堆叠:

In [22]: d.stack().groupby(level=0).apply(pd.Series.value_counts).unstack().fillna(0)
Out[22]: 
   1  2  3
0  1  1  1
1  4  0  1
2  1  1  1
3  0  4  1

这个错误似乎有点自我解释(4 != 16):
len(d.stack()) #16
d.stack().groupby(arange(4))
AssertionError: Grouper and axis must be same length

也许您想传递:
In [23]: np.repeat(np.arange(4), 4)
Out[23]: array([0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3])

谢谢,安迪。这个接近了,但是分组还不太对。请看我即将放入原问题中的示例。另外,stack返回的是副本而不是视图吗? - jeffalstott
问题在于nans使得行数变量,因此我们无法事先知道如何构建适当的分组器以用于d.stack()。最好不要使用stack进行操作,就像你所做的那样;我只是看不到在其中获得适当分组的方法。 - jeffalstott
@jeffalstott,你按级别分组,堆栈是一个副本。我不确定你实际想要的最终结果是什么... - Andy Hayden
有一个解决方案。很高兴听到更好的方法。在此期间,我将接受您的答案。 - jeffalstott

1

评论需要足够的声望,但是安迪的回答:

pd.value_counts(d.values.ravel()) 

这是我个人使用过的,而且对我来说似乎是最多才多艺和易于阅读的解决方案。另一个优点是可以轻松使用列的子集:

pd.value_counts(d[[1,3,4,6,7]].values.ravel()) 

或者

pd.value_counts(d[["col_title1","col_title2"]].values.ravel()) 

使用这种方法有什么缺点吗?或者你想使用堆栈和分组的特定原因是什么?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接