【问题标题】:Carry forward a specific value if observations stop before a certain time如果观察在特定时间之前停止,则结转特定值
【发布时间】:2021-04-08 15:21:23
【问题描述】:

对于纵向数据集,我想将在第 7 天之前终止且 y=3 的观察结果进行结转,完成连续天数的记录,直到第 7 天 y=3。一个相关的问题是How to make continuous time sequences within groups in data.table?。以下解决方案有效,但我还希望有一个解决方案,(1) 将之前的观察结果子集化(见下文)或 (2) 通过一步连接进行结转。

d <- data.table(t =c(1, 2, 1, 2, 3, 1, 2, 1, 2, 3, 5, 6, 7, 1, 2, 3, 5, 6),
                id=c(1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5),
                y =c(1, 2, 1, 2, 3, 1, 1, 1, 2, 2, 3, 3, 3, 1, 2, 2, 2, 3),
                x =c(0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0),
                key=c('id', 't'))
d

    t id y x
 1: 1  1 1 0
 2: 2  1 2 0
 3: 1  2 1 1
 4: 2  2 2 1
 5: 3  2 3 1
 6: 1  3 1 0
 7: 2  3 1 0
 8: 1  4 1 1
 9: 2  4 2 1
10: 3  4 2 1
11: 5  4 3 1
12: 6  4 3 1
13: 7  4 3 1
14: 1  5 1 0
15: 2  5 2 0
16: 3  5 2 0
17: 5  5 2 0
18: 6  5 3 0

w <- d[, .(tlast=t, last3 = t == max(t) & y == 3 & t < 7, x=x), by=id]
w <- w[last3 == TRUE, .(t = (tlast + 1) : 7, y=rep(3, 7 - tlast), x=x), by=id]
d <- rbind(d, w)
setkey(d, id, t)
d

   t id y x
 1: 1  1 1 0
 2: 2  1 2 0
 3: 1  2 1 1
 4: 2  2 2 1
 5: 3  2 3 1
 6: 4  2 3 1
 7: 5  2 3 1
 8: 6  2 3 1
 9: 7  2 3 1
10: 1  3 1 0
11: 2  3 1 0
12: 1  4 1 1
13: 2  4 2 1
14: 3  4 2 1
15: 5  4 3 1
16: 6  4 3 1
17: 7  4 3 1
18: 1  5 1 0
19: 2  5 2 0
20: 3  5 2 0
21: 5  5 2 0
22: 6  5 3 0
23: 7  5 3 0
    t id y x

以下不起作用(导致 data.table 有 0 行和 4 列)

w <- d[(t == max(t) & y == 3 & t < 7) == TRUE, .SD, by=id]

【问题讨论】:

  • 嗨教授,在data.table中,操作顺序是i,然后对于每个by,计算j,在每个by中,.SD包含当前子集数据(请参阅获取 DT,使用i 对行进行子集/重新排序,然后计算j,在cran.r-project.org/web/packages/data.table/vignettes/… 中按by 分组)。在示例数据中,由于max(t) 为 7,所以没有行有 t == max(t) &amp; y == 3 &amp; t &lt; 7
  • 谢谢 - 这是我忘记的操作顺序。

标签: r data.table


【解决方案1】:

数据表

cols <- c("x", "y")
merge(d[, .(t = if (3 %in% y && max(t) < 7) as.numeric(c(t, (1+max(t)):7)) else t),
      by = .(id)], d, by = c("id", "t"), all.x = TRUE
  )[, (cols) := lapply(.SD, nafill, type = "locf"), by = .(id), .SDcols = cols][]
#        id     t     y     x
#     <num> <num> <num> <num>
#  1:     1     1     1     0
#  2:     1     2     2     0
#  3:     2     1     1     1
#  4:     2     2     2     1
#  5:     2     3     3     1
#  6:     2     4     3     1
#  7:     2     5     3     1
#  8:     2     6     3     1
#  9:     2     7     3     1
# 10:     3     1     1     0
# 11:     3     2     1     0
# 12:     4     1     1     1
# 13:     4     2     2     1
# 14:     4     3     2     1
# 15:     4     5     3     1
# 16:     4     6     3     1
# 17:     4     7     3     1
# 18:     5     1     1     0
# 19:     5     2     2     0
# 20:     5     3     2     0
# 21:     5     5     2     0
# 22:     5     6     3     0
# 23:     5     7     3     0
#        id     t     y     x

演练:

  • 我们首先需要生成一个列表,其中包含每个id 所需的t 值,所以

    d[, .(t = if (3 %in% y && max(t) < 7) as.numeric(c(t, (1+max(t)):7)) else t), by = .(id)]
    #        id     t
    #     <num> <num>
    #  1:     1     1
    #  2:     1     2
    #  3:     2     1
    #  4:     2     2
    #  5:     2     3
    #  6:     2     4
    #  7:     2     5
    #  8:     2     6
    #  9:     2     7
    # 10:     3     1
    # 11:     3     2
    # 12:     4     1
    # 13:     4     2
    # 14:     4     3
    # 15:     4     5
    # 16:     4     6
    # 17:     4     7
    # 18:     5     1
    # 19:     5     2
    # 20:     5     3
    # 21:     5     5
    # 22:     5     6
    # 23:     5     7
    #        id     t
    

    这不会填充缺少的步骤(ids 3 和 4 中缺少4)。如果y 包含3,那么我们将t 填写到7,否则我们什么都不做。

    注意t 这里是numeric,这需要与 (integer) 序列一起跳舞,因此as.numeric 可以消除 data.table 对匹配列类型的抱怨。

  • 与原始d 的简单合并将在数据中留下一些NA 漏洞,这是故意的:

    merge(d[, .(t = if (3 %in% y && max(t) < 7) as.numeric(c(t, (1+max(t)):7)) else t), by = .(id)], d, by = c("id", "t"), all.x = TRUE)
    #        id     t     y     x
    #     <num> <num> <num> <num>
    #  1:     1     1     1     0
    #  2:     1     2     2     0
    #  3:     2     1     1     1
    #  4:     2     2     2     1
    #  5:     2     3     3     1
    #  6:     2     4    NA    NA
    #  7:     2     5    NA    NA
    #  8:     2     6    NA    NA
    #  9:     2     7    NA    NA
    # 10:     3     1     1     0
    # 11:     3     2     1     0
    # 12:     4     1     1     1
    # 13:     4     2     2     1
    # 14:     4     3     2     1
    # 15:     4     5     3     1
    # 16:     4     6     3     1
    # 17:     4     7     3     1
    # 18:     5     1     1     0
    # 19:     5     2     2     0
    # 20:     5     3     2     0
    # 21:     5     5     2     0
    # 22:     5     6     3     0
    # 23:     5     7    NA    NA
    #        id     t     y     x
    
  • 从这里开始,就像nafill(., type="locf") 一样简单,使用.SDcols 来提高效率(和通用性,所以我们不关心还有哪些其他列,只要cols 列出它们)。


原因

d[(t == max(t) & y == 3 & t < 7) == TRUE, .SD, by=id]

返回 0 行是 i 条件是 first 而不是 .SD。因此,它是一个全局条件,而不是每个组的条件。条件不考虑by=,所以表达式等价于

d[(t == max(t) & y == 3 & t < 7),] # no .SD, no by=

这也是 0 行。但是这样看,意识到只有一行t == max(t),第 13 行,其中t 是 7。在那一行,y 是 3(到目前为止很好),但 t&lt;7 是不是真的。

将其更改为 .SD 中的每组事物会返回数据:

d[, .SD[(t == max(t) & y == 3 & t < 7),], by=id]
#       id     t     y     x
#    <num> <num> <num> <num>
# 1:     2     3     3     1
# 2:     5     6     3     0

【讨论】:

  • 非常好!如果您退后一步评估您的解决方案与我原来的rbind 解决方案的可理解性/优雅性,我会很感激您的看法。还有谁能解释为什么w &lt;- d[(t == max(t) &amp; y == 3 &amp; t &lt; 7) == TRUE, .SD, by=id] 不起作用,以及如何使它起作用?
  • 我认为这样的情况需要继续.SD,而不是在全球范围内。也许d[,.SD[(t == max(t) &amp; y == 3 &amp; t &lt; 7),], by=id] 更接近?
  • 对我来说,data.table 的学习曲线的一部分是发现(通常)当有 by= 时,i 中的任何内容都应该在 origdata[,.SD[i,],by=] 中,而不是在origdata[i,.SD,by=]。我相信在某些情况下,这种经验法则可能不是最好的,但这是一个开始。
【解决方案2】:

我不确定这是否更有效,但这是一种dplyr 方法(我的data.table 知识对于这个方法来说太有限了)。如果您觉得它有用,希望您可以调整它的 data.table 版本。

library(dplyr)

crossing(id=unique(d$id), t=1:max(d$t)) %>% 
  full_join(d) %>% 
  group_by(id) %>% 
  filter(!(max(y, na.rm=TRUE) < 3 & is.na(y)) &
           !(is.na(y) & !is.na(lead(y)))) %>% 
  mutate(across(c(y,x), zoo::na.locf)) 
      id     t     y     x
 1     1     1     1     0
 2     1     2     2     0
 3     2     1     1     1
 4     2     2     2     1
 5     2     3     3     1
 6     2     4     3     1
 7     2     5     3     1
 8     2     6     3     1
 9     2     7     3     1
10     3     1     1     0
11     3     2     1     0
12     4     1     1     1
13     4     2     2     1
14     4     3     2     1
15     4     5     3     1
16     4     6     3     1
17     4     7     3     1
18     5     1     1     0
19     5     2     2     0
20     5     3     2     0
21     5     5     2     0
22     5     6     3     0
23     5     7     3     0

【讨论】:

  • 谢谢。我正在寻找data.table 的方法。
【解决方案3】:

如果您不介意使用dplyr,这可能会满足您的需求。将数据集分成两组,然后处理需要重复第三行的情况。 dplyr::summarize 通常用于将多行折叠成一列,但也可以将一列展开成多行。

library(dplyr)

# original data
d <- tibble(t =c(1, 2, 1, 2, 3, 1, 2, 1, 2, 3, 5, 6, 7, 1, 2, 3, 5, 6),
            id=c(1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5),
            y =c(1, 2, 1, 2, 3, 1, 1, 1, 2, 2, 3, 3, 3, 1, 2, 2, 2, 3),
            x =c(0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0)) %>%
        arrange(id, t)


bind_rows(
    
    
    d %>%
        # subset for cases to leave alone
        group_by(id) %>%
        filter( max(t) < 3 | 7 <= max(t))  %>%
        ungroup(),
    
    d %>%
        # subset for cases to fill in by carrying values forward
        group_by(id) %>%
        filter(3 <= max(t) & max(t) < 7) %>%

        # get the 3rd row
        filter(t == 3) %>%

        # repeat 3rd row until there are 7 rows
        summarize(t = seq(t, 7), x = x, y = y)  %>%
        ungroup()
    
) %>% arrange(id, t)

输出

# A tibble: 20 x 4
       t    id     y     x
   <dbl> <dbl> <dbl> <dbl>
 1     1     1     1     0
 2     2     1     2     0
 3     3     2     3     1
 4     4     2     3     1
 5     5     2     3     1
 6     6     2     3     1
 7     7     2     3     1
 8     1     3     1     0
 9     2     3     1     0
10     1     4     1     1
11     2     4     2     1
12     3     4     2     1
13     5     4     3     1
14     6     4     3     1
15     7     4     3     1
16     3     5     2     0
17     4     5     2     0
18     5     5     2     0
19     6     5     2     0
20     7     5     2     0

【讨论】:

  • 谢谢。我想要data.table 方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-17
  • 2022-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多