【发布时间】:2021-04-05 21:01:52
【问题描述】:
我有一个数据框,其中包含分组列 (gr)、日期 (c)(d1 - 表示当天,d6 - 六天前)和值列 (v)。对于每个组,我想以扩展方式找到值低于(或高于)当前值的最近日期。
这是带有解决方案的玩具示例:
import pandas as pd
import operator
from functools import partial
df0 = pd.DataFrame({
'gr': ['a', 'a', 'a', 'a', 'b', 'b'],
'c': ['d1', 'd2', 'd3', 'd4', 'd5', 'd6'],
'v': [30, 10, 20, 5, 35, 5]
})
def last_time_op(df, col, t, op):
#col - column with values
#t - column with date
#op eg. operator.gt for lower, lt for higher
value = df[col]
series = [op(value.loc[x], value.loc[x+1:]) for x in value.index]
seriesIndex = [x.where(x==True).first_valid_index() for x in series]
r = df[t].reindex(seriesIndex)
return r
df0['dateLower'] = df0.groupby('gr').apply(partial(last_time_op, col='v', t='c', op=operator.gt)).reset_index(drop=True)
df0['dateHigher'] = df0.groupby('gr').apply(partial(last_time_op, col='v', t='c', op=operator.lt)).reset_index(drop=True)
结果是:
gr c v dateLower dateHigher
0 a d1 30 d2 NaN
1 a d2 10 d4 d3
2 a d3 20 d4 NaN
3 a d4 5 NaN NaN
4 b d5 35 d6 NaN
5 b d6 5 NaN NaN
例如:10(第 1 行,c:d2)dateHigher 是 d3。
对于更高的你需要给operator.lt而不是operator.gt。函数last_time_op 在没有分组时也可以正常工作,但是当没有真实分组时,例如
df1 = pd.DataFrame({
'gr': ['a', 'a', 'a', 'a', 'a', 'a'], #pseudo-grouping
'c': ['d1', 'd2', 'd3', 'd4', 'd5', 'd6'], #d1 now #d6 - six days ago
'v': [30, 10, 20, 5, 35, 5]
})
那么你需要另外unstack() 来“强制形状”:
df1['dateLower'] = df1.groupby('gr').apply(partial(last_time_op, col='v', t='c', op=operator.gt)).unstack().reset_index(drop=True)
当然,我可以在分组中找到多个唯一值,并且 if 提供了可用于伪分组的实现,但对我来说看起来很难看。
另外我的函数last_time_op也不是那么简单...
我想知道是否存在使用纯 pandas 或一些 pandas 扩展的更简洁、更简洁和更惯用的方法?
解决方案应该是c 中的分组和日期时间中的多列。
【问题讨论】:
标签: pandas pandas-groupby