web-dev-qa-db-ja.com

Pandasでnp.diffを計算すると、groupbyの使用後に予期しない結果が発生する

私はデータフレームを持っていて、それに逐次差分の列を追加しようとしています。私はとても気に入っているメソッドを見つけました(そして私のユースケースでは一般化しています)。しかし、私は途中で1つの奇妙なことに気づきました。それを理解するのを手伝ってくれませんか?

これは正しい構造を持ついくつかのデータです(回答をモデル化したコード here ):

import pandas as pd
import numpy as np
import random
from itertools import product

random.seed(1)       # so you can play along at home
np.random.seed(2)    # ditto

# make a list of dates for a few periods
dates = pd.date_range(start='2013-10-01', periods=4).to_native_types()
# make a list of tickers
tickers = ['ticker_%d' % i for i in range(3)]
# make a list of all the possible (date, ticker) tuples
pairs = list(product(dates, tickers))
# put them in a random order
random.shuffle(pairs)
# exclude a few possible pairs
pairs = pairs[:-3]
# make some data for all of our selected (date, ticker) tuples
values = np.random.Rand(len(pairs))

mydates, mytickers = Zip(*pairs)
data = pd.DataFrame({'date': mydates, 'ticker': mytickers, 'value':values})

わかりました。これは私にそのようなフレームを与えます:

     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を引き起こさないようにします。他の方法でデータフレームを乱すことなくこれを実行したい(つまり、結果のDataFrameを、差分を行うために必要なものに基づいて並べ替えたくない)。次のコードは機能します。

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

簡単に確認すると、実際にはdata1data2と等しいことがわかります。ただし、これを行うと:

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を呼び出すだけではありませんか?diffクラスにDataFrameメソッドがあることを知っていますが、それを渡す方法を理解できませんでしたtransform関数の構文なしでlambdaを使用しましたが、data1を機能させるために使用しました。何かが不足していますか?data3 screwyのdiffs列はなぜですか?diff内でPandas transformメソッドを__some_variable内で呼び出すにはどうすればよいですか?それを行うには、lambdaを書き込みますか?

33
8one6

再現しやすい例!!より多くの質問はこのようにする必要があります!

ラムダを変換に渡すだけです(これは、np.diff(またはSeries.diff)などのafunctonオブジェクトを直接渡すことと同じです。したがって、これは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ガイドラインに準拠していません(これにより、入力を強制して出力を送信するためのさまざまなメソッドを試みます。たとえば、__array__入力時__array_wrap__出力時)。なぜなのかよくわからないので、もう少し詳しく こちら をご覧ください。つまり、一番下の行はnp.diffはインデックスを適切に処理しておらず、独自の計算を行っています(この場合は間違っています)。

Pandasには、さまざまなdtypeを処理し、nansを処理し、この場合は「特別な」diffを処理するため、numpy関数を呼び出すだけではない多くのメソッドがあります。例えば時間の頻度をdatelike-indexに渡し、実際に比較するnの数を計算することができます。

28
Jeff

Series .diff()メソッドがnp.diff()と異なることがわかります。

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])
5
Andy Hayden