在观察前 n 天向数据框添加一列
Add a column to data frame n days before an observation
我需要一种更有效的方法来添加一个标记,以显示在特定日期前 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 中),但是在更大的数据集上计算需要相当长的时间。有人可以推荐更好的方法吗?
这是使用 difftime
和 cumsum
的 tidyverse
解决方案:
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
说明:我们按 ID
对条目进行分组; is_before
标记 在 date_before
之前或之前的条目;然后我们用 (max(cumsum(is_before)) - cumsum(is_before)) %in% 1:3)
.
标记前三行 before date_before
示例数据
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"
1) 分别为每个 ID
应用滚动 window。 rolling window 函数检查 Date 接下来的 3 个元素是否等于 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
我需要一种更有效的方法来添加一个标记,以显示在特定日期前 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 中),但是在更大的数据集上计算需要相当长的时间。有人可以推荐更好的方法吗?
这是使用 difftime
和 cumsum
的 tidyverse
解决方案:
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
说明:我们按 ID
对条目进行分组; is_before
标记 在 date_before
之前或之前的条目;然后我们用 (max(cumsum(is_before)) - cumsum(is_before)) %in% 1:3)
.
date_before
示例数据
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"
1) 分别为每个 ID
应用滚动 window。 rolling window 函数检查 Date 接下来的 3 个元素是否等于 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