Python Pandas中类似于R的tapply()函数的等效函数

7

我有一个数据集,包含3只动物的喂养数据,包括动物的标签id(1,2,3),每次“餐食”所给予的饲料类型(A,B)和数量(kg):

Animal   FeedType   Amount(kg)
Animal1     A         10
Animal2     B         7
Animal3     A         4
Animal2     A         2
Animal1     B         5
Animal2     B         6
Animal3     A         2

在R语言中,我可以轻松地使用tapply()函数输出以下矩阵,其中矩阵的行是unique('Animal'),列是unique('FeedType'),矩阵相应单元格内的累计Amount(kg)数据。请看下方示例代码:

out <- with(mydf, tapply(Amount, list(Animal, FeedType), sum))

         A  B
Animal1 10  5
Animal2  2 13
Animal3  6 NA

有没有Python Pandas数据帧的等效功能?在Pandas中实现这一点的最优雅和最快的方法是什么?

附注:我想能够指定在哪一列(在本例中为Amount)执行聚合。

提前感谢。

编辑:

我尝试了两个答案中的方法。在我的实际Pandas数据帧(216,347行,15列)中测试性能结果:

start_time1 = timeit.default_timer()
mydf.groupby(['Animal','FeedType'])['Amount'].sum()
elapsed_groupby = timeit.default_timer() - start_time1

start_time2 = timeit.default_timer()
mydf.pivot_table(rows='Animal', cols='FeedType',values='Amount',aggfunc='sum')
elapsed_pivot = timeit.default_timer() - start_time2

print ('elapsed_groupby: ' + str(elapsed_groupby))
print ('elapsed_pivot: ' + str(elapsed_pivot))

提供:

elapsed_groupby: 10.172213
elapsed_pivot: 8.465783

在我的情况下,使用pivot_table()更快。

2个回答

7

首先,我读取您的数据:

In [7]: df = pd.read_clipboard(sep="\s+", index_col=False)

In [8]: df
Out[8]:
    Animal FeedType  Amount(kg)
0  Animal1        A          10
1  Animal2        B           7
2  Animal3        A           4
3  Animal2        A           2
4  Animal1        B           5
5  Animal2        B           6
6  Animal3        A           2

然后我可以按照这两列进行分组并进行聚合:

In [9]: df.groupby(['Animal','FeedType']).sum()
Out[9]:
                  Amount(kg)
Animal  FeedType
Animal1 A                 10
        B                  5
Animal2 A                  2
        B                 13
Animal3 A                  6

为了使其格式相同,我可以使用unstack函数对dataframe进行重组:
In [10]: df.groupby(['Animal','FeedType']).sum().unstack()
Out[10]:
          Amount(kg)
FeedType           A   B
Animal
Animal1           10   5
Animal2            2  13
Animal3            6 NaN

谢谢您的回答!我该如何指定:df.groupby(['Animal','FeedType']).sum() 来特别求和 'Amount'?我在同一数据框中有其他浮点列,我只对 'Amount' 值感兴趣。 - Zhubarb
2
执行 df.groupby(['Animal','FeedType'])['Amount'].sum() - joris
是的,抱歉在我写评论后我就发现了这一点。但是这是否意味着我一开始就按['Animal', 'FeedType']以外的所有列进行分组? 我没有在问题中指定这一点,以使问题简洁明了,但实际上我有很多列,并且希望尽可能地限制操作仅适用于单个列。 - Zhubarb

6

@Zelazny7的方法使用groupbyunstack是完全可以的,但为了完整起见,您也可以直接使用pivot_table来实现(请参见文档)[版本0.13及以下]:

In [13]: df.pivot_table(rows='Animal', cols='FeedType', values='Amount(kg)', aggfunc='sum')
Out[13]:
FeedType   A   B
Animal
Animal1   10   5
Animal2    2  13
Animal3    6 NaN

在较新的Pandas版本(0.14及更高版本)中,pivot_table的参数已更改:
In [13]: df.pivot_table(index='Animal', columns='FeedType', values='Amount(kg)', aggfunc='sum')
Out[13]:
FeedType   A   B
Animal
Animal1   10   5
Animal2    2  13
Animal3    6 NaN

谢谢,这比 groupBy() 更快吗?我无法弄清如何在 groupBy() 方法中指定“Amount”列。我感到按整个数据框分组是不必要的,并且可能会带来性能问题。我想在聚合时特别使用 df$Amount - Zhubarb
一些快速的 timeit 结果显示,使用 groupby 方法稍微更快一些(100 次循环中为 2.44ms 对比 3.28ms)。 - Zelazny7
@Zelazny7,我按照你的建议使用timeit测试了两种方法。正如我在问题中所提到的那样,pivot_table()在我的情况下更快。但无论如何,我仍然感谢你的回答。 - Zhubarb
1
如果您正在使用IPython,对于计时,我发现使用%timeit更容易(例如%timeit mydf.groupby(['Animal','FeedType'])['Amount'].sum())@Zhubarb。 - joris

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