简体   繁体   中英

Dplyr reshape activity date data to monthly level

I have a df that contains user ID, subscription start and current month, activity date, and activity number. Users can appear more than once if they have multiple activities. Below is a short toy example:

USER_ID      SUB_START     CURRENT_MONTH     ACTIVITY_DATE    ACTIVITY_NUMBER
  0102       2020-04-01     2020-08-01        2020-02-05            1
  0102       2020-04-01     2020-08-01        2020-03-10            2
  0102       2020-04-01     2020-08-01        2020-07-01            3
  2190       2019-05-10     2020-08-01        2017-01-02            1
  2190       2019-05-10     2020-08-01        2017-10-02            2
  0121       2020-07-13     2020-08-01        2018-01-04            1
  0121       2020-07-13     2020-08-01        2019-02-10            2
  0121       2020-07-13     2020-08-01        2020-01-02            3
  0121       2020-07-13     2020-08-01        2020-04-10            4

What I would like to accomplish is group by month and then show the number of unique ID's that had an active subscription that month and the number of unique id's that had an activity date within the previous 13 months of that month. So the output for this toy data set would look like:

  MONTH       ACTIVE_COUNT    ACTIVITY_COUNT
2019-05-01         1               0          *user 2190 active with no activity within past 13 mo
2019-06-01         1               0          *user 2190 active with no activity within past 13 mo
2019-07-01         1               0          *user 2190 active with no activity within past 13 mo
2019-08-01         1               0          *user 2190 active with no activity within past 13 mo
2019-09-01         1               0          *user 2190 active with no activity within past 13 mo
2019-10-01         1               0          *user 2190 active with no activity within past 13 mo
2019-11-01         1               0          *user 2190 active with no activity within past 13 mo
2019-12-01         1               0          *user 2190 active with no activity within past 13 mo
2020-01-01         1               0          *user 2190 active with no activity within past 13 mo
2020-02-01         1               0          *user 2190 active with no activity within past 13 mo
2020-03-01         1               0          *user 2190 active with no activity within past 13 mo
2020-04-01         2               1          *user 2190 and 0102 active and 0102 has a qualifying activity
2020-05-01         2               1          *user 2190 and 0102 active and 0102 has a qualifying activity
2020-06-01         2               1          *user 2190 and 0102 active and 0102 has a qualifying activity
2020-07-01         3               2          *user 2190,0102,0121 all active and 0102 and 0121 have qualifying activities

So far I have put together the following code based on a previous project that gives me each user and a row for every month between their SUB_START and CURRENT_MONTH. The problem is it repeats that process for every ACTIVITY_DATE so each USER_ID has multiple groups of their active months. I am looking to try and have one row for every month each user is active and then add a column for if that user had an ACTIVITY_DATE within 13 months of that month.

df_monthly <- df %>%
                  select(USER_ID,SUB_START, CURRENT_MONTH, ACTIVITY_DATE) %>%
                  mutate(across(where(is.character), ~ floor_date(as.Date(.x) - 1, "months") + 1)) %>%
                  rowwise() %>%
                  mutate(MONTH = list(seq(SUB_START,CURRENT_MONTH, by = "+1 month"))) %>%
                  unnest(MONTH) %>%
                  mutate(MONTH2 = floor_date(MONTH, unit="month"))

You need to loop on the "current month", then for each of these, compute the last activity of each user, and finally count the number of users with subscription and recent activity.

This should do approximately what you want:

library(tidyverse)
library(lubridate)
# recreate your dataframe
df <- "USER_ID,      SUB_START,     CURRENT_MONTH ,   ACTIVITY_DATE ,   ACTIVITY_NUMBER
0102,       2020-04-01,     2020-08-01,        2020-02-05            ,1
0102,       2020-04-01,     2020-08-01 ,       2020-03-10            ,2
0102,       2020-04-01,     2020-08-01,      2020-07-01            ,3
2190,       2019-05-10,     2020-08-01,        2017-01-02            ,1
2190,       2019-05-10,     2020-08-01,        2017-10-02            ,2
0121,       2020-07-13,     2020-08-01,        2018-01-04            ,1
0121,       2020-07-13,     2020-08-01,        2019-02-10            ,2
0121,       2020-07-13,     2020-08-01,        2020-01-02            ,3
0121,       2020-07-13,     2020-08-01,        2020-04-10            ,4" %>%
  str_remove_all(" ") %>%
  read_csv()

seq(min(df$SUB_START), max(df$CURRENT_MONTH), by = "+1 month") %>%
  map_dfr(~ df %>%
            group_by(USER_ID, SUB_START) %>%
            summarize(LAST_ACTIVITY = max(ACTIVITY_DATE), .groups="drop") %>%
            mutate(TIME_SINCE_LAST = .x - LAST_ACTIVITY) %>%
            summarize(n_users_subscribed = sum(SUB_START <= .x),
                      n_recently_active = sum(TIME_SINCE_LAST < dmonths(13) &
                                                TIME_SINCE_LAST >= 0)) %>%
            add_column(month = .x)
  )

One difference with your example data is that I don't count user 0121 on 2020-07-01 as they joined on 13th, you might need to work on the roundings (apply your floor_date before processing perhaps?).

Note: your approach with nesting should work too, I couldn't try it (probably because you have characters when reading the dataframe), but you probably just need to preprocess the nested dataframe before unnesting to keep only the last activity date per user.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM