Python 如何在具有受限前瞻性的组内计算cummin?
旧的 给出以下示例:Python 如何在具有受限前瞻性的组内计算cummin?,python,pandas,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
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
我如何用熊猫来计算呢
新的
我目前的方法如下(基于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
我的建议是迭代df,如果新的max确实大于旧的max,则更新新的max dict: 创建一个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
此解决方案首先对
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
方法允许根据日期范围确定窗口大小。默认的窗口是一个常量,对于这个问题不起作用。但是,如果您将索引转换为datetimelike索引,那么您可以利用滚动方法并按日期进行切片
令人烦恼的是,rolling
方法没有前瞻性选项,因此必须首先反转数据帧
首先反转并创建一个新索引,以天为单位
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内置的滚动方法生成与上面完全相同的数据帧
df.groupby('group')\
.rolling('3D', min_periods=0)['value']\
.max()\
.reset_index(0)\
.reindex(df.index)[::-1]\
.reset_index(drop=True)
我在numpy重新实现了你的方法(见问题)。你有什么进一步改进的建议吗?