【问题标题】:Count rows with consecutive dates within PANDAS groupby计算PANDAS groupby中具有连续日期的行
【发布时间】:2016-11-17 23:03:21
【问题描述】:

This is the closest to what i'm looking for that I've found

假设我的数据框看起来像这样:

d = {'item_number':['K208UL','AKD098008','DF900A','K208UL','AKD098008']
     'Comp_ID':['998798098','988797387','12398787','998798098','988797387']
     'date':['2016-11-12','2016-11-13','2016-11-17','2016-11-13','2016-11-14']}

df = pd.DataFrame(data=d)

我想计算连续几天观察到相同item_numberComp_ID 的次数。

我想这看起来会是这样的:

g = df.groupby(['Comp_ID','item_number'])
g.apply(lambda x: x.loc[x.iloc[i,'date'].shift(-1) - x.iloc[i,'date'] == 1].count())

但是,在比较之前,我需要从每个日期中提取日期作为 int,我也遇到了麻烦。

for i in df.index:
    wbc_seven.iloc[i, 'day_column'] = datetime.datetime.strptime(df.iloc[i,'date'],'%Y-%m-%d').day

显然基于位置的索引只允许整数?我该如何解决这个问题?

【问题讨论】:

    标签: python pandas dataframe


    【解决方案1】:

    但是,我需要从每个日期中提取日期作为 int 在比较之前,我也遇到了麻烦。

    为什么?

    要修复您的代码,您需要:

    consecutive['date'] = pd.to_datetime(consecutive['date'])
    g = consecutive.groupby(['Comp_ID','item_number'])
    g['date'].apply(lambda x: sum(abs((x.shift(-1) - x)) == pd.to_timedelta(1, unit='D')))
    

    注意以下几点:

    1. 上面的代码避免了重复。这是一个基本的编程原则:Don't Repeat Yourself
    2. 它将 1 转换为 timedelta 以进行正确比较。
    3. 需要绝对差。

    提示,为您的工作编写一个顶级函数,而不是 lambda,因为它具有更好的可读性、简洁性和美观性:

    def differencer(grp, day_dif):
        """Counts rows in grp separated by day_dif day(s)"""
        d = abs(grp.shift(-1) - grp)
        return sum(d == pd.to_timedelta(day_dif, unit='D'))
    g['date'].apply(differencer, day_dif=1)
    

    解释:

    这很简单。日期是converted to Timestamp type,然后减去。差异将导致timedelta,它还需要与timedelta 对象进行比较,因此将1(或day_dif)转换为timedelta。该转换的结果将是一个布尔系列。布尔值用 0 表示 False,用 1 表示 True。布尔系列的总和将返回系列中 True 值的总数。

    【讨论】:

    • 我收到此错误:TypeError: unsupported operand type(s) for -: 'str' and 'str'
    • 好吧,显然,您需要将“日期”列转换为Timestamp 类型。在分组之前使用这个consecutive['date'] = pd.to_datetime(consecutive['date'])。我也会编辑答案。
    • 太棒了!从我的第一轮 QA 来看,这看起来效果很好。我并没有想象的那么远。如果您能对代码中发生的事情添加一些解释,将不胜感激。 @Kartik
    • 我添加了一个解释。此外,稍微简化一下,您不需要再次使用比较返回的布尔系列进行索引。一个简单的sum 就可以满足您的需求。而且你肯定离得不远,做得很好:-)。请采纳答案,如果您觉得有用,请点赞。这些控件位于答案的左侧。
    • 我还没有代表点来投票,但确实是我想要的!
    【解决方案2】:

    一种解决方案是使用数据透视表来计算连续几天观察到Comp_IDitem_number 的次数。

    import pandas as pd
    
    d = {'item_number':['K208UL','AKD098008','DF900A','K208UL','AKD098008'],'Comp_ID':['998798098','988797387','12398787','998798098','988797387'],'date':['2016-11-12','2016-11-13','2016-11-17','2016-11-13','2016-11-14']}
    
    df = pd.DataFrame(data=d).sort_values(['item_number','Comp_ID'])
    df['date'] = pd.to_datetime(df['date'])
    df['delta'] = (df['date'] - df['date'].shift(1))
    df = df[(df['delta']=='1 days 00:00:00.000000000') & (df['Comp_ID'] == df['Comp_ID'].shift(1)) & 
            (df['item_number'] == df['item_number'].shift(1))].pivot_table( index=['item_number','Comp_ID'],
               values=['date'],aggfunc='count').reset_index()
    df.rename(columns={'date':'consecutive_days'},inplace =True)
    

    结果

      item_number    Comp_ID  consecutive_days
    0   AKD098008  988797387                 1
    1      K208UL  998798098                 1 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-20
      • 2019-03-16
      • 1970-01-01
      • 2015-08-23
      • 1970-01-01
      相关资源
      最近更新 更多