Search code examples
rgroup-bydplyrdata.tablelag

R: Group-by lag variable generating different within-group lag values


I have data grouped by an id variable with multiple, unique observations per quarter and with different group sizes per id:

    library(dplyr)
    library(data.table)
    library(lubridate)

v2 <- sample(1:100, 15)
df <- data.frame(qy = c(rep('2016-01-01', 5), rep('2016-04-01', 5), rep('2016-10-01', 5)),
                 id = c(rep(c('a','a','b','b','c'), 3)),
                 value_t = c(0,0,1,1,0,1,1,0,0,0,0,0,1,1,1),
                 value2_t = c(v2))
df$qy <- ymd(df$qy)
df <- df %>% arrange(id, qy)
> df
   qy          id      value_t value2_t
1  2016-01-01  a       0       49
2  2016-01-01  a       0        4
3  2016-01-01  b       1        5
4  2016-01-01  b       1       48
5  2016-01-01  c       0       32
6  2016-04-01  a       1       81
7  2016-04-01  a       1        6
8  2016-04-01  b       0       71
9  2016-04-01  b       0       47
10 2016-04-01  c       0       78
11 2016-10-01  a       0       31
12 2016-10-01  a       0       10
13 2016-10-01  b       1       37
14 2016-10-01  b       1       63
15 2016-10-01  c       1       36

I attempt to create two lag variables grouped by id with lags of t-1 and t-2, respectively:

setDT(df)[order(qy), paste0('value_t', 1:2) := shift(value_t, 1:2) , by = id]

Although I've grouped by id, the lags don't follow the grouping assignment - the lag variables are just rolling lags within the group:

> df
   qy          id      value_t value2_t value_t1 value_t2
1: 2016-01-01  a       0       49       NA       NA
2: 2016-01-01  a       0        4        0       NA
3: 2016-04-01  a       1       81        0        0
4: 2016-04-01  a       1        6        1        0
5: 2016-10-01  a       0       31        1        1
6: 2016-10-01  a       0       10        0        1
7: 2016-01-01  b       1        5       NA       NA
8: 2016-01-01  b       1       48        1       NA
9: 2016-04-01  b       0       71        1        1
10: 2016-04-01  b       0       47        0        1
11: 2016-10-01  b       1       37        0        0
12: 2016-10-01  b       1       63        1        0
13: 2016-01-01  c       0       32       NA       NA
14: 2016-04-01  c       0       78        0       NA
15: 2016-10-01  c       1       36        0        0

I would like the lag variables to respect the grouping despite there being multiple observations per quarter as follows:

> df
   qy          id      value_t value2_t value_t1 value_t2
1  2016-01-01  a       0       49       NA       NA
2  2016-01-01  a       0        4       NA       NA
3  2016-04-01  a       1       81        0       NA
4  2016-04-01  a       1        6        0       NA
5  2016-10-01  a       0       31        1        0
6  2016-10-01  a       0       10        1        0
7  2016-01-01  b       1        5       NA       NA
8  2016-01-01  b       1       48       NA       NA
9  2016-04-01  b       0       71        1       NA
10 2016-04-01  b       0       47        1       NA
11 2016-10-01  b       1       37        0        1
12 2016-10-01  b       1       63        0        1
13 2016-01-01  c       0       32       NA       NA
14 2016-04-01  c       0       78        0       NA
15 2016-10-01  c       1       36        0        0

Any suggestions in data.table or dplyr in particular would be greatly appreciated!

Update: Thanks all for your comments. I believe David A. is correct in that the main issue is the varied id group size, and I've updated the question to highlight this.


Solution

  • We can create a subset of data frame based on unique qy and id, create the lag columns value_t1 and value_t2, and then merge back to the original data frame.

    library(dplyr)
    library(data.table)
    library(lubridate)
    
    # Create example data frame
    set.seed(123)
    
    v2 <- sample(1:100, 15)
    df <- data.frame(qy = c(rep('2016-01-01', 5), rep('2016-04-01', 5), rep('2016-10-01', 5)),
                     id = c(rep(c('a','a','b','b','c'), 3)),
                     value_t = c(0,0,1,1,0,1,1,0,0,0,0,0,1,1,1),
                     value2_t = c(v2))
    df$qy <- ymd(df$qy)
    df <- df %>% arrange(id, qy)
    
    # Process the data
    df2 <- df %>%
      distinct(id, qy, .keep_all = TRUE) %>%
      group_by(id) %>%
      mutate(value_t1 = lag(value_t, n = 1L),
             value_t2 = lag(value_t, n = 2L)) %>%
      select(-value_t, -value2_t) %>%
      ungroup() %>%
      left_join(df, ., by = c("qy", "id")) 
    
    df2
    #            qy id value_t value2_t value_t1 value_t2
    # 1  2016-01-01  a       0       29       NA       NA
    # 2  2016-01-01  a       0       79       NA       NA
    # 3  2016-04-01  a       1        5        0       NA
    # 4  2016-04-01  a       1       50        0       NA
    # 5  2016-10-01  a       0       87        1        0
    # 6  2016-10-01  a       0       98        1        0
    # 7  2016-01-01  b       1       41       NA       NA
    # 8  2016-01-01  b       1       86       NA       NA
    # 9  2016-04-01  b       0       83        1       NA
    # 10 2016-04-01  b       0       51        1       NA
    # 11 2016-10-01  b       1       60        0        1
    # 12 2016-10-01  b       1       94        0        1
    # 13 2016-01-01  c       0       91       NA       NA
    # 14 2016-04-01  c       0       42        0       NA
    # 15 2016-10-01  c       1        9        0        0