如何在限制前瞻的群组中计算cummin?

时间:2017-01-12 20:01:04

标签: python pandas

给出以下示例:

group value
 5     1
10     2
 5     3
10     4
10     5
 7     6
 7     7
 7     8
 7     9
 5    10

我想计算cummax,但是,我希望在分组之前先预测2。结果应该是

group value
 5     3
10     4
 5     3
10     5
10     5
 7     8
 7     9
 7     9
 7     9
 5    10

如何使用Pandas计算出来?

我目前的方法如下(基于Ezer K的建议)。您有什么建议可以提高可读性/性能吗?

def cum_func_lookahead(g, v, func1, func2, lookahead):
d = defaultdict(list)

result = [np.nan] * len(g)

def d_(g, v):
    d[g].append(v)
    if len(d[g]) > 1:
        d[g][-1] = func1(d[g][-2], d[g][-1])
    return d[g][-1]

for i in range(len(g)):
    lookahead_g = g[i:i+lookahead]
    lookahead_v = v[i:i+lookahead]

    mask = (lookahead_g == g[i])
    lookahead_v = lookahead_v[mask]

    max_v = func2(lookahead_v, axis=0)

    result[i] = d_(g[i], max_v)


result = np.asarray(result)
return result


result = np.asarray(result)
return result

表现:

LENGTH = 100000
g = np.random.randint(low=0, high=LENGTH/2, size=LENGTH)
v = np.random.rand(LENGTH, 40)
%timeit r1 = cum_func_lookahead(g, v, np.maximum, np.max, 3)
1 loop, best of 3: 2.18 s per loop

2 个答案:

答案 0 :(得分:1)

我的建议是迭代df并更新新的最大字典,如果确实新的最大值大于旧的最大值:

创建一个dict,其中键是组,值用零初始化:

max_dict = dict(zip(df.group.unique(),df.group.nunique()*[0]))

浏览df(原始数据框)的行,向前看,必要时更新dict并将当前最大值附加到列表中:

l = []
for t in df.iterrows():
    tmp_df = df.ix[t[0]:(t[0]+2)]
    tmp_df = tmp_df[tmp_df['group']==t[1]['group']]
    tmp_max =  max(tmp_df['value'].values)
    if tmp_max>max_dict[t[1]['group']]:
        max_dict[t[1]['group']] = tmp_max
    l.append( max_dict[t[1]['group']] )

df['com_max'] = l

    group   value   com_max
    0   5   1   3
    1   10  2   4
    2   5   3   3
    3   10  4   5
    4   10  5   5
    5   7   6   8
    6   7   7   9
    7   7   8   9
    8   7   9   9
    9   5   10  10

答案 1 :(得分:0)

此解决方案首先在group上分组并为apply创建一个自定义函数,该函数为每个组迭代选择最多2个索引,并找到该组的该子集的最大值,然后重新排序该帧回到原来的订单。

def max2(x):
    max_vals = [x.loc[idx:idx+2, 'value'].max() for idx in x.index]
    return pd.Series(max_vals, index=x.index, name='value')

df.groupby('group').apply(max2).reset_index('group').reindex(df.index)

输出

   group  value
0      5      3
1     10      4
2      5      3
3     10      5
4     10      5
5      7      8
6      7      9
7      7      9
8      7      9
9      5     10

内置熊猫解决方案

另一种解决方案依赖于rolling方法的其他功能。 rolling方法允许窗口大小由日期范围确定。默认窗口是一个常数,对此问题不起作用。但是,如果将索引转换为类似日期的索引,则可以利用rolling方法和按日期切片。

令人讨厌的是,rolling方法没有前瞻性选项,因此您必须首先反转DataFrame。

首先反转并使用as days创建一个新索引。

df = df[::-1].reset_index(drop=True)
df.index = pd.to_timedelta(df.index, 'D')

产生这个:

        group  value
0 days      5     10
1 days      7      9
2 days      7      8
3 days      7      7
4 days      7      6
5 days     10      5
6 days     10      4
7 days      5      3
8 days     10      2
9 days      5      1

然后使用Pandas内置的滚动方法,它产生与上面完全相同的DataFrame。

df.groupby('group')\
  .rolling('3D', min_periods=0)['value']\
  .max()\
  .reset_index(0)\
  .reindex(df.index)[::-1]\
  .reset_index(drop=True)