为什么pandas中的groupby().transform()需要唯一索引?

12
我希望使用groupby().transform()方法对(排序后的)数据集中每个记录块进行自定义(累积)转换。除非我确保拥有唯一的键,否则它无法正常工作。为什么?
这里有一个玩具示例:
df = pd.DataFrame([[1,1],
                  [1,2],
                  [2,3],
                  [3,4],
                  [3,5]], 
                  columns='a b'.split())
df['partials'] = df.groupby('a')['b'].transform(np.cumsum)
df

给出了预期的结果:

     a   b   partials
0    1   1   1
1    1   2   3
2    2   3   3
3    3   4   4
4    3   5   9

但如果'a'是一个键,则会出现问题:

df = df.set_index('a')
df['partials'] = df.groupby(level=0)['b'].transform(np.cumsum)
df

---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-146-d0c35a4ba053> in <module>()
      3 
      4 df = df.set_index('a')
----> 5 df.groupby(level=0)['b'].transform(np.cumsum)

/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pandas/core/groupby.pyc in transform(self, func, *args, **kwargs)
   1542             res = wrapper(group)
   1543             # result[group.index] = res
-> 1544             indexer = self.obj.index.get_indexer(group.index)
   1545             np.put(result, indexer, res)
   1546 

/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/pandas/core/index.pyc in get_indexer(self, target, method, limit)
    847 
    848         if not self.is_unique:
--> 849             raise Exception('Reindexing only valid with uniquely valued Index '
    850                             'objects')
    851 

Exception: Reindexing only valid with uniquely valued Index objects

如果在分组之前选择列“b”,同样会出现错误,即:
df['b'].groupby(level=0).transform(np.cumsum)

但是如果您转换整个数据框,就可以使其正常工作,例如:

df.groupby(level=0).transform(np.cumsum)

甚至可以是一个单列的数据框(而不是系列):
df.groupby(level=0)[['b']].transform(np.cumsum)

我感觉我还没有完全掌握GroupBy-fu的精髓。 有人能帮我理清思路吗?


是的,这就是我想要的——在'a'组中'b'的部分和。我在上面澄清了说明。在我的实际示例中,'a'是一个时间戳,而b是一些其他键,因此我的数据集实际上是不同长度的时间序列集合(它们在时间上重叠,并且在组内和跨组包含重复的时间戳)。我使用transform()对每个时间序列段执行累积操作,如移动平均等。 - patricksurry
1个回答

6

这是一个bug,在pandas中已经修复(在0.15.2版本中肯定已经修复,我记得在0.14版本中也修复了),所以您不应该再看到这个异常。


作为解决方法,在早期的pandas中,您可以使用apply

In [10]: g = df.groupby(level=0)['b']

In [11]: g.apply(np.cumsum)
Out[11]:
a
1    1
1    3
2    3
3    4
3    9
dtype: int64

你可以将其分配给数据框中的列。
In [12]: df['partial'] = g.apply(np.cumsum)

好的,谢谢-我猜我不理解apply()和transform()之间的区别。 那么transform更加严格吗?? - patricksurry
@patricksurry 我在想这是否是个 bug,看起来它应该属于转换类别... - Andy Hayden
2
@patricksurry 的意思是 tranform 函数期望将一个结果应用到组中的所有元素,而 apply 函数期望为组中的每一行提供一个值。虽然两者都作用于组(子数据帧),但这有点令人困惑。 - Andy Hayden
1
听起来很有道理,但似乎文档写得不太清楚。比如这里,它一开始把transform描述为apply的一种形式,然后又几乎把它们说得差不多等价:“...对于这些情况,请使用apply函数,可以替代标准用例中的aggregate和transform。然而,apply可以处理一些特殊情况,例如…” - patricksurry

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