在观察前{n}天向数据框添加一列

时间:2018-04-21 15:09:03

标签: r date lubridate

我需要一种更有效的方法来添加一个标记,该标记显示在特定日期前3天注册观察。问题是这些日期不一定是连续的,即它们可能会丢失,但我需要标记来忽略丢失的日期。以下示例说明了问题以及我需要更清楚的内容:

library(tidyverse)
library(lubridate)

df <- data.frame("Date" = c(as_date(0:9)), ID = rep(paste0("ID", 1:3), each = 10))
df <- df[-c(5, 13, 24),]

date_before <- "1970-01-07"

df[, "three_days_before"] <- 0

for(i in df$ID){

  cond <- df[, "ID"] == i & 
    df[, "Date"] == date_before

  before_n <- (which(cond)-3):(which(cond)-1)

  df[before_n, "three_days_before"] <- 1

}

df

循环为我提供了我需要的东西(每次都标记三天,无论它们是否包含在data.frame中),但是在更大的数据集上计算需要相当长的时间。有人可以推荐更好的方法吗?

2 个答案:

答案 0 :(得分:1)

以下是使用tidyversedifftime的<{1}}解决方案:

cumsum

说明:我们按library(tidyverse); df %>% group_by(ID) %>% mutate( is_before = difftime(as_date(date_before), Date) >= 0, three_days_before = as.numeric((max(cumsum(is_before)) - cumsum(is_before)) %in% 1:3)) %>% select(-is_before) %>% as.data.frame() # Date ID three_days_before #1 1970-01-01 ID1 0 #2 1970-01-02 ID1 0 #3 1970-01-03 ID1 1 #4 1970-01-04 ID1 1 #5 1970-01-06 ID1 1 #6 1970-01-07 ID1 0 #7 1970-01-08 ID1 0 #8 1970-01-09 ID1 0 #9 1970-01-10 ID1 0 #10 1970-01-01 ID2 0 #11 1970-01-02 ID2 0 #12 1970-01-04 ID2 1 #13 1970-01-05 ID2 1 #14 1970-01-06 ID2 1 #15 1970-01-07 ID2 0 #16 1970-01-08 ID2 0 #17 1970-01-09 ID2 0 #18 1970-01-10 ID2 0 #19 1970-01-01 ID3 0 #20 1970-01-02 ID3 0 #21 1970-01-03 ID3 1 #22 1970-01-05 ID3 1 #23 1970-01-06 ID3 1 #24 1970-01-07 ID3 0 #25 1970-01-08 ID3 0 #26 1970-01-09 ID3 0 #27 1970-01-10 ID3 0 对条目进行分组; IDis_before或之前标记条目;然后,我们使用date_before标记 date_before之前的前三行

样本数据

(max(cumsum(is_before)) - cumsum(is_before)) %in% 1:3)

答案 1 :(得分:1)

1)为每个ID分别应用滚动窗口。滚动窗口函数检查Date的下三个元素中的任何一个是否等于date_before。 (指定list(1:3)的宽度表示使用偏移1,2和3,这意味着前面的下一个3。)请注意,最后一个值没有接下来的3个元素,因此我们使用fill来填充它in。我们添加0以从逻辑转换为数字。这个解决方案只涉及两行代码,没有明确的循环。

library(zoo)

roll <- function(x) rollapply(x, list(1:3), FUN = any, partial = TRUE, fill = FALSE)
transform(df, before = ave(Date == date_before, ID, FUN = roll) + 0)

,并提供:

         Date  ID before
1  1970-01-01 ID1      0
2  1970-01-02 ID1      0
3  1970-01-03 ID1      1
4  1970-01-04 ID1      1
6  1970-01-06 ID1      1
7  1970-01-07 ID1      0
8  1970-01-08 ID1      0
9  1970-01-09 ID1      0
10 1970-01-10 ID1      0
11 1970-01-01 ID2      0
12 1970-01-02 ID2      0
14 1970-01-04 ID2      1
15 1970-01-05 ID2      1
16 1970-01-06 ID2      1
17 1970-01-07 ID2      0
18 1970-01-08 ID2      0
19 1970-01-09 ID2      0
20 1970-01-10 ID2      0
21 1970-01-01 ID3      0
22 1970-01-02 ID3      0
23 1970-01-03 ID3      1
25 1970-01-05 ID3      1
26 1970-01-06 ID3      1
27 1970-01-07 ID3      0
28 1970-01-08 ID3      0
29 1970-01-09 ID3      0
30 1970-01-10 ID3      0

2)这也可以表示为roll来自上方的管道:

library(dplyr)
library(zoo)

df %>%
   group_by(ID) %>%
   mutate(before = roll(Date == date_before)) %>%
   ungroup