【问题标题】:find most recent date when value was higher/lower than current in group查找值高于/低于组中当前值的最近日期
【发布时间】: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


    【解决方案1】:

    您可以在每个组内进行笛卡尔积,然后过滤掉右侧 c 值不高于左侧的那些行(c < c_:例如,我们只想比较 d3 和 @987654324 @)

    剩下的就是找到最低的c_,其中左侧v 的值低于/高于右侧v_ 的值。

    类似这样的:

    z = df0.merge(df0, on='gr', suffixes=['', '_']).query('c < c_')
    
    df0.set_index(['gr', 'c']).assign(
        dateLower=z[z['v'].gt(z['v_'])].groupby(['gr', 'c'])['c_'].min(),
        dateHigher=z[z['v'].lt(z['v_'])].groupby(['gr', 'c'])['c_'].min()
    ).reset_index()
    

    输出:

      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
    

    【讨论】:

    • 真正鼓舞人心的方法。我能看到的唯一问题 - 大数据帧的笛卡尔积的内存消耗。
    • @QuantChristo 确实如此,但好处是您只需要在组内获取笛卡尔积,这样您就可以将其拆分为更小的数据帧,然后为这些数据运行它块,然后连接结果(例如,在提供的样本上,我会分别为 gr 'a' 和 'b' 运行它得到相同的结果,然后连接结果)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多