根据行索引在 R 中创建索引变量

Creating an index variable in R based on row index

我想是时候寻求帮助了。假设我有这个 data.frame 或 data.table

State   Date   Event 
CA      Oct27    1      
CA      Oct28    0      
CA      Oct29    0      
CA      Oct30    0      
CA      Oct31    1      
TX      Oct27    0      
TX      Oct28    1      
TX      Oct29    1      
TX      Oct30    0      
TX      Oct31    0      
TX      Nov1     0      

我想创建一个新的二进制变量 "active",它指示在特定日期和状态是否有活动事件(假设所有事件都持续三天)。 "Event" 列中的值“1”表示事件开始的时间。所以,我的数据看起来像这样:

State   Date   Event Active
CA      Oct27    1      1
CA      Oct28    0      1
CA      Oct29    0      1
CA      Oct30    0      0
CA      Oct31    1      1
TX      Oct27    0      0
TX      Oct28    1      1
TX      Oct29    1      1
TX      Oct30    0      1
TX      Oct31    0      1
TX      Nov1     0      0

如果有任何建议,我将不胜感激。

考虑到您的 table 已排序并且您不关心非相邻日期,您可以尝试:

library(data.table)
setDT(df)[, Active:=Event|c(0, head(Event,-1))|c(0,0,head(Event,-2)), State][
          , Active:=Active+0]

#    State  Date Event Active
# 1:    CA Oct27     1      1
# 2:    CA Oct28     0      1
# 3:    CA Oct29     0      1
# 4:    CA Oct30     0      0
# 5:    CA Oct31     1      1
# 6:    TX Oct27     0      0
# 7:    TX Oct28     1      1
# 8:    TX Oct29     1      1
# 9:    TX Oct30     0      1
#10:    TX Oct31     0      1
#11:    TX  Nov1     0      0

伙计,这是一个非常具有挑战性的问题。我想我得到它使用 by()StateReduce() 分组重复应用向量化逻辑 OR |Active 向量来解释过去的任何一天在事件开始的指定范围内 (3)。

df <- data.frame(State=c('CA','CA','CA','CA','CA','TX','TX','TX','TX','TX','TX'), Date=c('Oct27','Oct28','Oct29','Oct30','Oct31','Oct27','Oct28','Oct29','Oct30','Oct31','Nov1'), Event=c(1,0,0,0,1,0,1,1,0,0,0) );
E <- 3;
do.call(rbind,by(df,df$State,function(x) { s <- x$Event==1; x$Active <- Reduce(function(a,b) a|c(rep(F,b),s[-seq(length(s)-b+1,len=b)]),c(list(s),1:(E-1))); x; }));
##       State  Date Event Active
## CA.1     CA Oct27     1   TRUE
## CA.2     CA Oct28     0   TRUE
## CA.3     CA Oct29     0   TRUE
## CA.4     CA Oct30     0  FALSE
## CA.5     CA Oct31     1   TRUE
## TX.6     TX Oct27     0  FALSE
## TX.7     TX Oct28     1   TRUE
## TX.8     TX Oct29     1   TRUE
## TX.9     TX Oct30     0   TRUE
## TX.10    TX Oct31     0   TRUE
## TX.11    TX  Nov1     0  FALSE

此解决方案的一个优点是它参数化了事件持续时间,这意味着您可以在将来轻松更改它:

E <- 2;
do.call(rbind,by(df,df$State,function(x) { s <- x$Event==1; x$Active <- Reduce(function(a,b) a|c(rep(F,b),s[-seq(length(s)-b+1,len=b)]),c(list(s),1:(E-1))); x; }));
##       State  Date Event Active
## CA.1     CA Oct27     1   TRUE
## CA.2     CA Oct28     0   TRUE
## CA.3     CA Oct29     0  FALSE
## CA.4     CA Oct30     0  FALSE
## CA.5     CA Oct31     1   TRUE
## TX.6     TX Oct27     0  FALSE
## TX.7     TX Oct28     1   TRUE
## TX.8     TX Oct29     1   TRUE
## TX.9     TX Oct30     0   TRUE
## TX.10    TX Oct31     0  FALSE
## TX.11    TX  Nov1     0  FALSE

这个解决方案的正确性取决于两个假设,独立于每个唯一的 State:(1) Date 序列中没有间隙,以及 (2) data.frame 按 Date.

排序

这是一个不同的解决方案,再次使用 by(),但现在使用 seq() 生成事件涵盖的所有日期,并使用 merge() 将这些日期合并回 data.frame 特定 State 的子集以将 Active 设置为 true。该解决方案放宽了我上面提到的两个假设;输入 data.frame 现在不再必须是无间隙或有序的。但是,您现在必须将 Date 列强制转换为 class Date(正如我在下面的演示中所做的那样),尽管我认为这是您在使用日期。

df2 <- transform(df,Date=as.Date(Date,'%b%d'));
E <- 3;
transform(do.call(rbind,by(df2,df2$State,function(x) merge(x,data.frame(Date=unique(do.call(c,lapply(x$Date[x$Event==1],seq,by=1,len=E))),Active=T),all.x=T))),Active=replace(Active,is.na(Active),F));
##            Date State Event Active
## CA.1 2015-10-27    CA     1   TRUE
## CA.2 2015-10-28    CA     0   TRUE
## CA.3 2015-10-29    CA     0   TRUE
## CA.4 2015-10-30    CA     0  FALSE
## CA.5 2015-10-31    CA     1   TRUE
## TX.1 2015-10-27    TX     0  FALSE
## TX.2 2015-10-28    TX     1   TRUE
## TX.3 2015-10-29    TX     1   TRUE
## TX.4 2015-10-30    TX     0   TRUE
## TX.5 2015-10-31    TX     0   TRUE
## TX.6 2015-11-01    TX     0  FALSE
E <- 2;
transform(do.call(rbind,by(df2,df2$State,function(x) merge(x,data.frame(Date=unique(do.call(c,lapply(x$Date[x$Event==1],seq,by=1,len=E))),Active=T),all.x=T))),Active=replace(Active,is.na(Active),F));
##            Date State Event Active
## CA.1 2015-10-27    CA     1   TRUE
## CA.2 2015-10-28    CA     0   TRUE
## CA.3 2015-10-29    CA     0  FALSE
## CA.4 2015-10-30    CA     0  FALSE
## CA.5 2015-10-31    CA     1   TRUE
## TX.1 2015-10-27    TX     0  FALSE
## TX.2 2015-10-28    TX     1   TRUE
## TX.3 2015-10-29    TX     1   TRUE
## TX.4 2015-10-30    TX     0   TRUE
## TX.5 2015-10-31    TX     0  FALSE
## TX.6 2015-11-01    TX     0  FALSE

我喜欢 data.table 解决方案。这是我认为更干净的基础 R 解决方案。

s <- split(df, df$State)

newlist <- lapply(s, function(x) {
  days <- c(which(x$Event==1)+1, which(x$Event==1)+2)
  x$Event[seq_along(x$Event) %in% days] <- 1
  x
  }
)

do.call(rbind, newlist)

首先,按州拆分数据框。对于每个状态,确定事件开始后的两天。如果那些日子在列表中,则将 1 分配给它们。最后,把状态放在一起。

它输出:

      State  Date Event
CA.1     CA Oct27     1
CA.2     CA Oct28     1
CA.3     CA Oct29     1
CA.4     CA Oct30     0
CA.5     CA Oct31     1
TX.6     TX Oct27     0
TX.7     TX Oct28     1
TX.8     TX Oct29     1
TX.9     TX Oct30     1
TX.10    TX Oct31     1
TX.11    TX  Nov1     0