熊猫高效的团队每年都有季节

时间:2014-09-03 14:55:12

标签: python pandas group-by time-series

我有一个多年的时间序列,想要我95%的数据之间的界限。 我希望按照一年中的季节(' DJF',' MAM',' JJA',' SON')来看待这个。 / p>

我试过以下:

import pandas as pd
import numpy as np
FRAC_2_TAIL = 0.025
yr_idx = pd.date_range(start='2005-01-30', 
                       end='2008-02-02', freq='D')
data = np.random.rand(len(yr_idx))
df = pd.DataFrame(index=yr_idx, data=data, columns=['a'])
month_num_to_season =   { 1:'DJF',  2:'DJF', 
                          3:'MAM',  4:'MAM',  5:'MAM', 
                          6:'JJA',  7:'JJA',  8:'JJA',
                          9:'SON', 10:'SON', 11:'SON',
                         12:'DJF'}
grouped =  df.groupby(lambda x: month_num_to_season.get(x.month))                      
low_bounds = grouped.quantile(FRAC_2_TAIL)
high_bounds = grouped.quantile(1 - FRAC_2_TAIL) 

它的作用是给予:

DJF   0.021284
JJA   0.024769
MAM   0.030149
SON   0.041784

但是我需要花费很长时间才能完成数十年的数据集。

我可以使用TimeGrouper来获取几乎我想要的内容:

gp_time = df.groupby(pd.TimeGrouper('QS-DEC'))
low_bounds = gp_time.agg(lambda x: x.quantile(FRAC_2_TAIL)) 

但我们每年都有单独的输出(多年来没有明显的方法来结合分位数限制)。

2004-12-01  0.036755
2005-03-01  0.034271
         ...
2007-09-01  0.098833
2007-12-01  0.068948

我还试过制作freq='QS-DEC'时间序列' DJF' MAM'等等以最小化字典查找,然后上采样到df.index.freq并对其进行分组。它也很慢而且记忆力很重。

似乎我错过了一些明显的东西。

修改

根据@ JohnE的评论

groupby中的dict查找需要时间。使用5年的精确数据:

%%timeit
grouped =  df.groupby(lambda x: month_num_to_season.get(x.month)) 
> 13.3 s per loop

分位数计算速度很快:

%%timeit
low_bounds = grouped.quantile(FRAC_2_TAIL)
> 2.94 ms per loop

添加季节列并对其进行分组在整体时间上类似。再次由dict lookup`主导:

SEAS = 'season'
%%timeit
df[SEAS] = [month_num_to_season.get(t_stamp.month) for t_stamp in df.index]
> 13.1 s per loop

%%timeit
gp_on_col = df.groupby(SEAS)
> 10000 loops, best of 3: 62.7 µs per loop

%%timeit
gp_on_col.quantile(FRAC_2_TAIL)
> 753 ms per loop

我重新实施了制作季度季节数据帧的方法,以最小化dict查找,然后对其进行上采样。这种方法现在看起来像是一个重大改进:我不知道我之前是如何做到这么慢:

SEASON_HALO = pd.datetools.relativedelta(months=4)
start_with_halo = df.index.min() - SEASON_HALO
end_with_halo = df.index.max() + SEASON_HALO
> 84.1 µs per loop

seasonal_idx = pd.DatetimeIndex(start=start_with_halo, end=end_with_halo, freq='QS-DEC')
seasonal_ts = pd.DataFrame(index=seasonal_idx)
> 440 µs per loop

seasonal_ts[SEAS] = [month_num_to_season.get(t_stamp.month) for t_stamp in seasonal_ts.index]
> 1.25 s per loop

seasonal_minutely_ts = seasonal_ts.resample(df.index.freq, fill_method='ffill')
> 5.12 ms per loop

df_via_resample = df.join(seasonal_minutely_ts)
> 47 ms per loop

gp_up_sample = df_via_resample.groupby(SEAS)
> 63.4 µs per loop

gp_up_sample.quantile(FRAC_2_TAIL)
> 834 ms per loop

对于其他方法,这就像2秒对13秒。

3 个答案:

答案 0 :(得分:3)

如果它有帮助,我会建议替换你认为缓慢的以下列表理解和字典查找:

month_to_season_dct = {
    1: 'DJF', 2: 'DJF',
    3: 'MAM', 4: 'MAM', 5: 'MAM',
    6: 'JJA', 7: 'JJA', 8: 'JJA',
    9: 'SON', 10: 'SON', 11: 'SON',
    12: 'DJF'
}
grp_ary = [month_to_season_dct.get(t_stamp.month) for t_stamp in df.index]

使用numpy数组作为查找表。

month_to_season_lu = np.array([
    None,
    'DJF', 'DJF',
    'MAM', 'MAM', 'MAM',
    'JJA', 'JJA', 'JJA',
    'SON', 'SON', 'SON',
    'DJF'
])
grp_ary = month_to_season_lu[df.index.month]

这是对〜3年微小数据的两种方法的时间比较:

In [16]: timeit [month_to_season_dct.get(t_stamp.month) for t_stamp in df.index]
1 loops, best of 3: 12.3 s per loop

In [17]: timeit month_to_season_lu[df.index.month]
1 loops, best of 3: 549 ms per loop

答案 1 :(得分:1)

目前为止最快的是创建用于季节查找的低频时间序列和@ Garrett使用numpy.array索引查找而不是dict的方法的组合。

season_lookup = np.array([
    None,
    'DJF', 'DJF',
    'MAM', 'MAM', 'MAM',
    'JJA', 'JJA', 'JJA',
    'SON', 'SON', 'SON',
    'DJF'])
SEASON_HALO = pd.datetools.relativedelta(months=4)
start_with_halo = df.index.min() - SEASON_HALO
end_with_halo = df.index.max() + SEASON_HALO
seasonal_idx = pd.DatetimeIndex(start=start_with_halo, end=end_with_halo, freq='QS-DEC')
seasonal_ts = pd.DataFrame(index=seasonal_idx)
seasonal_ts[SEAS] = season_lookup[seasonal_ts.index.month]
seasonal_minutely_ts = seasonal_ts.resample(df.index.freq, fill_method='ffill')
df_via_resample = df.join(seasonal_minutely_ts)
gp_up_sample = df_via_resample.groupby(SEAS)
gp_up_sample.quantile(FRAC_2_TAIL)

我的机器上有10年的分钟数据:这是关于:

  • 比低频dict查找然后上采样
  • 快2%
  • 比正常频率np.array查找快<7%
  • &gt;我的原始方法改善了400%

YMMV

答案 2 :(得分:0)

这可能会有所帮助

$(CXX) $(CXXPRE) [somehow specify the .meta file here] $(CXXPRE2) $< $(CXXPOST) > $@