Python 使用groupby后计算Pandas中的np.diff会导致意外结果

Python 使用groupby后计算Pandas中的np.diff会导致意外结果,python,pandas,diff,Python,Pandas,Diff,我有一个数据帧,我正试图向它添加一列顺序差异。我发现了一种我非常喜欢的方法(并且对我的用例进行了很好的概括)。但我注意到一件奇怪的事。你能帮我弄明白吗 以下是一些具有正确结构的数据(以答案为模型的代码): 好的,太好了。这给了我一个这样的框架: date ticker value 0 2013-10-03 ticker_2 0.435995 1 2013-10-04 ticker_2 0.025926 2 2013-10-02

我有一个数据帧,我正试图向它添加一列顺序差异。我发现了一种我非常喜欢的方法(并且对我的用例进行了很好的概括)。但我注意到一件奇怪的事。你能帮我弄明白吗

以下是一些具有正确结构的数据(以答案为模型的代码):

好的,太好了。这给了我一个这样的框架:

     date        ticker      value
0    2013-10-03  ticker_2    0.435995
1    2013-10-04  ticker_2    0.025926
2    2013-10-02  ticker_1    0.549662
3    2013-10-01  ticker_0    0.435322
4    2013-10-02  ticker_2    0.420368
5    2013-10-03  ticker_0    0.330335
6    2013-10-04  ticker_1    0.204649
7    2013-10-02  ticker_0    0.619271
8    2013-10-01  ticker_2    0.299655
我的目标是向这个数据框架添加一个新列,其中包含顺序更改。为了做到这一点,数据需要是完整的,但排序和差异需要“按股票代码”进行,以便另一个股票代码中的缺口不会导致给定股票代码的NA。我希望在不以任何其他方式干扰数据帧的情况下执行此操作(即,我不希望根据执行差分所需的内容对结果数据帧进行重新排序)。以下代码起作用:

data1 = data.copy() #let's leave the original data alone for later experiments
data1.sort(['ticker', 'date'], inplace=True)
data1['diffs'] = data1.groupby(['ticker'])['value'].transform(lambda x: x.diff())
data1.sort_index(inplace=True)
data1
并返回:

     date        ticker      value       diffs
0    2013-10-03  ticker_2    0.435995    0.015627
1    2013-10-04  ticker_2    0.025926   -0.410069
2    2013-10-02  ticker_1    0.549662    NaN
3    2013-10-01  ticker_0    0.435322    NaN
4    2013-10-02  ticker_2    0.420368    0.120713
5    2013-10-03  ticker_0    0.330335   -0.288936
6    2013-10-04  ticker_1    0.204649   -0.345014
7    2013-10-02  ticker_0    0.619271    0.183949
8    2013-10-01  ticker_2    0.299655    NaN
到目前为止,一切顺利。如果我用这里显示的更简洁的代码替换上面的中间行,那么一切仍然有效:

data2 = data.copy()
data2.sort(['ticker', 'date'], inplace=True)
data2['diffs'] = data2.groupby('ticker')['value'].diff()
data2.sort_index(inplace=True)
data2
快速检查表明,实际上,
data1
等于
data2
。但是,如果我这样做:

data3 = data.copy()
data3.sort(['ticker', 'date'], inplace=True)
data3['diffs'] = data3.groupby('ticker')['value'].transform(np.diff)
data3.sort_index(inplace=True)
data3
我得到了一个奇怪的结果:

     date        ticker     value       diffs
0    2013-10-03  ticker_2    0.435995    0
1    2013-10-04  ticker_2    0.025926   NaN
2    2013-10-02  ticker_1    0.549662   NaN
3    2013-10-01  ticker_0    0.435322   NaN
4    2013-10-02  ticker_2    0.420368   NaN
5    2013-10-03  ticker_0    0.330335    0
6    2013-10-04  ticker_1    0.204649   NaN
7    2013-10-02  ticker_0    0.619271   NaN
8    2013-10-01  ticker_2    0.299655    0

这是怎么回事?当您在Pandas对象上调用
.diff
方法时,它不仅仅是在调用
np.diff
?我知道在
DataFrame
类上有一个
diff
方法,但是如果没有
lambda
函数语法,我就不知道如何将它传递到
transform
,我用它使
data1
工作。我错过什么了吗?为什么
data3
中的
diff
列有问题?我怎样才能在
transform
中调用Pandas
diff
方法,而不需要编写
lambda
来实现它呢?

很好,很容易复制示例!!更多的问题应该是这样的

只需传递一个lambda进行转换(这相当于直接传递一个uncton对象,例如np.diff(或Series.diff),因此这相当于data1/data2

In [32]: data3['diffs'] = data3.groupby('ticker')['value'].transform(Series.diff)

In [34]: data3.sort_index(inplace=True)

In [25]: data3
Out[25]: 
         date    ticker     value     diffs
0  2013-10-03  ticker_2  0.435995  0.015627
1  2013-10-04  ticker_2  0.025926 -0.410069
2  2013-10-02  ticker_1  0.549662       NaN
3  2013-10-01  ticker_0  0.435322       NaN
4  2013-10-02  ticker_2  0.420368  0.120713
5  2013-10-03  ticker_0  0.330335 -0.288936
6  2013-10-04  ticker_1  0.204649 -0.345014
7  2013-10-02  ticker_0  0.619271  0.183949
8  2013-10-01  ticker_2  0.299655       NaN

[9 rows x 4 columns]
我相信
np.diff
并没有遵循numpy自己的unfunc指南来处理数组输入(它尝试各种方法来强制输入并发送输出,例如输入上的
\uuuuuuuu数组\uuuuuuuuu
,输出上的
\uuuuu数组\uuuuuuuuuuuuuuuuuu
)。我不太清楚原因,请参阅更多信息。因此底线是,
np.diff
没有正确处理索引并进行自己的计算(在这种情况下是错误的)


Pandas有很多方法,它们不只是调用numpy函数,主要是因为它们处理不同的数据类型,处理NaN,在这种情况下,处理“特殊”差异。例如,您可以将时间频率传递给datelike索引,它计算实际差异的n数。

您可以看到序列
.diff()
方法不同于
np.diff()


这是一种奇怪的行为!是的……这可能是一个bug,也可能只是一种怪癖(我想这可能是他们这样做的原因)。谢谢你的补充。我是个新手,我想让像你这样的人更容易回答我那些不太深奥的问题!你跳过了我的
排序(['ticker',date',inplace=True)了吗
step?结果似乎你的答案与我的不同。没什么大不了的,我想我明白你所说的要点。对风格/稳定性/可扩展性最佳实践有什么感觉会让你更喜欢
转换(lambda x:x.diff())
方法或
blahblah.diff()
完成此任务的方法?@DJ_8one6 hah,我已经更改了它;我意识到您已经有了lambda解决方案。像您这样的groupby正在这样做(例如,使用
data3.groupby('ticker')['value']
将向应用函数传递一个序列;如果您这样做了
data3.groupby('ticker')。apply(…)
您将获得一个数据帧。这就是为什么lambda很好,您不必将函数作为其匿名引用。当我创建groupby时,我会这样做:
data3.groupby(…).apply(f)
其中
f
是:
def(x):print x;return x
。以准确查看传入的内容。明白了。您的示例非常清楚。在我最初的帖子中,我展示了两种有效的方法(1:涉及
lambda
和2:直接在熊猫
系列
上使用
diff
方法)。你认为其中哪一个更合适/最佳实践?一般来说,你是尽可能少地使用
lambda
,还是发现它们更容易看到正在发生的事情的结构?@DJ_8one6事实上,我发现lambdas在熊猫方面出奇地高效(尝试一下计时,它可以忽略不计,尽管我认为pd.Series.diff读起来更好)!Atm groupby.diff()实际上是在做groupby.apply(pd.Series.diff),这通常看起来比较慢。可能在0.14中。diff可以做得更快,这是许多低挂果/groupby方法之一。将
sort=False
标记添加到.groupby()拜托。你让我很紧张。出于某种原因。使用.diff的groupby使用了大量内存,而且效率很低,在其中添加不必要的排序只会让事情变得更糟。你的问题是关于
np.diff
而不是
pandas.diff
。为你编辑了你的标题。
In [32]: data3['diffs'] = data3.groupby('ticker')['value'].transform(Series.diff)

In [34]: data3.sort_index(inplace=True)

In [25]: data3
Out[25]: 
         date    ticker     value     diffs
0  2013-10-03  ticker_2  0.435995  0.015627
1  2013-10-04  ticker_2  0.025926 -0.410069
2  2013-10-02  ticker_1  0.549662       NaN
3  2013-10-01  ticker_0  0.435322       NaN
4  2013-10-02  ticker_2  0.420368  0.120713
5  2013-10-03  ticker_0  0.330335 -0.288936
6  2013-10-04  ticker_1  0.204649 -0.345014
7  2013-10-02  ticker_0  0.619271  0.183949
8  2013-10-01  ticker_2  0.299655       NaN

[9 rows x 4 columns]
In [11]: data.value.diff()  # Note the NaN
Out[11]: 
0         NaN
1   -0.410069
2    0.523736
3   -0.114340
4   -0.014955
5   -0.090033
6   -0.125686
7    0.414622
8   -0.319616
Name: value, dtype: float64

In [12]: np.diff(data.value.values)  # the values array of the column
Out[12]: 
array([-0.41006867,  0.52373625, -0.11434009, -0.01495459, -0.09003298,
       -0.12568619,  0.41462233, -0.31961629])

In [13]: np.diff(data.value) # on the column (Series)
Out[13]: 
0   NaN
1     0
2     0
3     0
4     0
5     0
6     0
7     0
8   NaN
Name: value, dtype: float64

In [14]: np.diff(data.value.index)  # er... on the index
Out[14]: Int64Index([8], dtype=int64)

In [15]: np.diff(data.value.index.values)
Out[15]: array([1, 1, 1, 1, 1, 1, 1, 1])