代码之家  ›  专栏  ›  技术社区  ›  Jake Morris

确定GroupBy之后的两种类型是否出现在时间范围内

  •  1
  • Jake Morris  · 技术社区  · 7 年前

    我有一个数据集,其中每一行代表一个事件,包含一个日期和一些关于事件的信息。我想知道在按一列分组之后( a )多个类型的另一列( b )在短时间内出现(比如, +/- 60 days ,并将行保留在这种情况下的位置。

    使用熊猫数据框架,如下所示:

         a      b          date
        foo    blue     2018-02-17
        foo    blue     2018-02-22
        foo    red      2018-04-28
        foo    blue     2018-04-29
        foo    blue     2018-05-02
        foo    red      2018-08-01
        bar    yellow   2018-01-25
        bar    red      2018-04-07
        bar    yellow   2018-07-11
        bar    yellow   2018-07-14
        baz    red      2018-03-11
        baz    blue     2018-04-14
        baz    red      2018-07-05
        baz    blue     2018-10-01
    

    我想选择以下行:

         a      b          date
        foo    red      2018-04-28
        foo    blue     2018-04-29
        foo    blue     2018-05-02
        baz    red      2018-03-11
        baz    blue     2018-04-14
    
    1 回复  |  直到 7 年前
        1
  •  0
  •   James Dellinger    7 年前

    从粘贴的示例开始:

    df = pd.DataFrame(columns=['a','b','date'],
                      data=[['foo','blue','2018-02-17'],['foo','blue','2018-02-22'],['foo','red','2018-04-28'],['foo','blue','2018-04-29'],['foo','blue','2018-05-02'],
                            ['foo','red','2018-08-01'],['bar','yellow','2018-01-25'],['bar','red','2018-04-07'],['bar','yellow','2018-07-11'],
                            ['bar','yellow','2018-07-14'],['baz','red','2018-03-11'],['baz','blue','2018-04-14'],['baz','red','2018-07-05'],['baz','blue','2018-10-01']])
    
    df['date'] = pd.to_datetime(df['date'])
    df
    
        a    b       date
    0   foo  blue    2018-02-17
    1   foo  blue    2018-02-22
    2   foo  red     2018-04-28
    3   foo  blue    2018-04-29
    4   foo  blue    2018-05-02
    5   foo  red     2018-08-01
    6   bar  yellow  2018-01-25
    7   bar  red     2018-04-07
    8   bar  yellow  2018-07-11
    9   bar  yellow  2018-07-14
    10  baz  red     2018-03-11
    11  baz  blue    2018-04-14
    12  baz  red     2018-07-05
    13  baz  blue    2018-10-01
    

    我确定了在给定时间范围的情况下,df中需要搜索的行的子集。我使用与您的示例相同的60天(+/-30天)时间框架。

    win_sz = pd.Timedelta(days=60)
    start = df['date'].min() + win_sz/2
    end = df['date'].max() - win_sz/2
    to_search_over = df[(df['date'] > start) & (df['date'] <= end)]['date']
    

    下一步,

    • 对于中的每一行 to_search_over 上面的列表中,我抓取了df的一个子集,其中包含日期在与日期窗口大小(在本例中为60天)相对应的日期范围内的所有行。
    • 我用这个窗户 groupby() nunique() 检查列中的给定元素 a 列中有多个值 b 与之相关。
    • 最后,我将满足此条件的行的任何索引添加到列表中( res )
    res = []
    
    for d in to_search_over:
        mask = (df['date'] > d-(win_sz/2)) & (df['date'] <= d+(win_sz/2))
        window = df.loc[mask]
        a = window.groupby('a')['b'].nunique()
        a = a[a>1].index.values
        if a.any():
            res += list(window[window['a'].isin(a)].index)
    

    我将这个索引列表转换为一个集合,然后再转换回一个列表,以便只保留唯一的行值。然后,我可以对df进行切片,以返回所有符合我们标准的行:

    df.iloc[list(set(res))]
    
        a    b      date
    2   foo  red    2018-04-28
    3   foo  blue   2018-04-29
    4   foo  blue   2018-05-02
    10  baz  red    2018-03-11
    11  baz  blue   2018-04-14
    

    我很高兴看到是否有人有更优雅的方法来实现这一点(这种方法不需要显式地逐行迭代df的一个子集的行)。我花了一段时间试图找到一种方法 pd.rolling 但无济于事。