我有一个看起来像这样的数据框(通常有很多用户):

userid  |  activityday
222        2015-01-09 12:00
222        2015-01-10 12:00
222        2015-01-11 12:00
222        2015-01-13 12:00
222        2015-01-14 12:00
222        2015-01-15 12:00
222        2015-01-17 12:00
222        2015-01-18 12:00
222        2015-01-19 12:00
222        2015-01-20 12:00
222        2015-01-20 12:00


我想获取到给定日期的连续活动天数和非活动天数的总数。例如,如果日期为2015-01-23,则:

userid | days_active_jb  | days_inactive_jb | ttl_days_active | ttl_days_inactive
222    | 3               | 2                | 10              | 2


或者,如果给定的日期是2015年1月15日,则:

userid | days_active_jb  | days_inactive_jb | ttl_days_active | ttl_days_inactive
222    | 2               | 0                | 5              | 1


我有大约300.000行要处理以获取此最终数据帧。我想知道什么是实现这一目标的有效方法。有任何想法吗?

以下是每列的说明:

days_active_jb:学生在给定日期之前连续进行活动的天数。

days_inactive_jb:学生在给定日期之前连续没有活动的天数。

ttl_days_active:学生在给定日期前一天进行活动的天数。

ttl_days_inactive:学生在给定日期前一天没有活动的天数。

最佳答案

设定:

df
Out[1714]:
    userid         activityday
0      222 2015-01-09 12:00:00
1      222 2015-01-10 12:00:00
2      222 2015-01-11 12:00:00
3      222 2015-01-13 12:00:00
4      222 2015-01-14 12:00:00
5      222 2015-01-15 12:00:00
6      222 2015-01-17 12:00:00
7      222 2015-01-18 12:00:00
8      222 2015-01-19 12:00:00
9      222 2015-01-20 12:00:00
11     322 2015-01-09 12:00:00
12     322 2015-01-10 12:00:00
13     322 2015-01-11 12:00:00
14     322 2015-01-13 12:00:00
15     322 2015-01-14 12:00:00
16     322 2015-01-15 12:00:00
17     322 2015-01-17 12:00:00
18     322 2015-01-18 12:00:00
19     322 2015-01-19 12:00:00
20     322 2015-01-20 12:00:00




def days_active_jb(x):
    x = x[x<pd.to_datetime(cut_off_days)]
    if len(x) == 0:
        return 0
    x = [e.date() for e in x.sort_values(ascending=False)]
    prev = x.pop(0)
    i = 1
    for e in x:
        if (prev-e).days == 1:
            i+=1
            prev = e
        else:
            break
    return i

def days_inactive_jb(x):
    diff = (pd.to_datetime(cut_off_days) -max(x)).days
    return 0 if diff<0 else diff

def ttl_days_active(x):
    x = x[x<pd.to_datetime(cut_off_days)]
    return len(x[x<pd.to_datetime(cut_off_days)])

def ttl_days_inactive(x):
    #counter the missing days between start and end dates
    x = x[x<pd.to_datetime(cut_off_days)]
    return len(pd.date_range(min(x),max(x))) - len(x)

#drop duplicate userid-activityday pairs
df = df.drop_duplicates(subset=['userid','activityday'])

cut_off_days = '2015-01-23'
df.sort_values(by=['userid','activityday'],ascending=False).\
              groupby('userid')['activityday'].\
              agg([days_active_jb,
                   days_inactive_jb,
                   ttl_days_active,
                   ttl_days_inactive]).\
              astype(np.int64)

Out[1856]:
        days_active_jb  days_inactive_jb  ttl_days_active  ttl_days_inactive
userid
222                  4                 2               10                  2
322                  4                 2               10                  2


cut_off_days = '2015-01-15'
df.sort_values(by=['userid','activityday'],ascending=False).\
              groupby('userid')['activityday'].\
              agg([days_active_jb,
                   days_inactive_jb,
                   ttl_days_active,
                   ttl_days_inactive]).\
              astype(np.int64)

Out[1863]:
        days_active_jb  days_inactive_jb  ttl_days_active  ttl_days_inactive
userid
222                  2                 0                5                  1
322                  2                 0                5                  1

10-07 19:11
查看更多