2014-10-10 53 views
2

使用熊貓tshift非常棒。這很快!大熊貓在羣組中慢轉

df = pd.DataFrame(index=pd.date_range(pd.datetime(1970,1,1),pd.datetime(1970,2,1))) 
df['data']=.5 
%timeit df.sum() 
#10000 loops, best of 3: 162 µs per loop 
%timeit df.tshift(-1) 
#1000 loops, best of 3: 307 µs per loop #x2 slower 

但是,當我一個groupby後做tshift它會減慢了很多:

df = pd.DataFrame(index=pd.date_range(pd.datetime(1970,1,1),pd.datetime(1970,2,1))) 
df['data']=.5 
df['A'] = randint(0,2,len(df.index)) 
%timeit df.groupby('A').sum() 
#100 loops, best of 3: 2.72 ms per loop 
%timeit df.groupby('A').tshift(-1) 
#10 loops, best of 3: 16 ms per loop #x6 slower! 

爲什麼tshift這麼多幹什麼分組時慢?有沒有辦法變得更快?

更新:

我的實際使用案例更接近下面的代碼。我看到減速乘數的大小取決於組的數量。

n_A = 50 
n_B = 5 
index = pd.MultiIndex.from_product([arange(n_A), 
            arange(n_B), 
            pd.date_range(pd.datetime(1975,1,1), 
                pd.datetime(2010,1,1), 
                freq='5AS')], 
            names=['A', 'B', 'Year']) 

df = pd.DataFrame(index=index) 
df['data']=.5 

%timeit df.reset_index(['A','B']).groupby(['A','B']).sum() 
#100 loops, best of 3: 4.34 ms per loop 
%timeit df.reset_index(['A','B']).groupby(['A','B']).tshift(-1, freq='5AS') 
#10 loops, best of 3: 198 ms per loop # X44 slowdown. 

而如果我們增加A和B組數:

n_A = 500 
n_B = 50 
... 
%timeit df.reset_index(['A','B']).groupby(['A','B']).sum() 
#10 loops, best of 3: 35.8 ms per loop 
%timeit df.reset_index(['A','B']).groupby(['A','B']).tshift(-1, freq='5AS') 
#1 loops, best of 3: 20.3 s per loop # X567 slowdown 

我很驚訝的是,放緩的增長與組的號碼!有沒有更聰明的方法來做到這一點?

回答

5

tshift需要freq參數用於此用法(因爲freq一般可能並且通常不是常規組),因此df.groupby('A').tshift(-1)會返回一個空幀(它正在提高每個組的速度,並減慢它)。

In [44]: %timeit df.groupby('A').tshift(-1,'D') 
100 loops, best of 3: 3.57 ms per loop 

In [45]: %timeit df.groupby('A').sum() 
1000 loops, best of 3: 1.02 ms per loop 
從這個

除此之外,這個問題here正在等待cythonized實現移(和tshift)過的。這將使得這與cython化的總和相當。貢獻值得歡迎!

使用您的第二個數據集(大組),你可以這樣做:

In [59]: def f(df): 
    ....:  x = df.reset_index() 
    ....:  x['Year_ts'] = pd.DatetimeIndex(x['Year'])-pd.offsets.YearBegin(5) 
    ....:  return x.drop(['Year'],axis=1).rename(columns={'Year_ts' : 'Year'}).set_index(['A','B','Year']) 
    ....: 

In [60]: result = df.reset_index(['A','B']).groupby(['A','B']).tshift(-1,'5AS') 

In [61]: %timeit df.reset_index(['A','B']).groupby(['A','B']).tshift(-1,'5AS') 
1 loops, best of 3: 10.8 s per loop 

In [62]: result2 = f(df) 

In [63]: %timeit f(df) 
1 loops, best of 3: 2.51 s per loop 

In [64]: result.equals(result2) 
Out[64]: True 

這樣做GROUPBY之外的日期減法使得這大約快4倍。而這(和高速緩存)是探索加快分組速度的第一步。

+0

謝謝,傑夫!在下面查看我的更新。我的實際代碼有freq指示;我只是在示例中錯誤地放棄了它。看起來我的實際問題是組數? n_A和n_B實際上分別是2k-500k和121。 – jeffalstott 2014-10-11 04:44:50

+0

好的,這是緩慢的原因是,一個單獨的操作結束了「時間戳+偏移量」(例如添加一個特定的時間戳和偏移量(這裏是負值))。這是具有非常具體的語義時,增加幾個月/年,使東西適當土地等。在其本身中,這種操作很好。然而,它多次重複多次。所以這*可以被緩存以大大加快速度。將創建一個問題來跟蹤這一點。 – Jeff 2014-10-11 14:44:42

+0

此外,你真的正在做一個完全不同的操作類型''.sum()'',這是一種減少。你在這裏基本上正在進行一次轉變,正在重複。 – Jeff 2014-10-11 15:59:27