旧
给出以下示例:
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
答案 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)