R 为面板中的组创建顺序计数器,从事件开始,在事件之前为零

R 为面板中的组创建顺序计数器,从事件开始,在事件之前为零,r,dplyr,counter,panel,timedelta,R,Dplyr,Counter,Panel,Timedelta,对于一个面板数据集(GSOEP),我需要创建一个时间计数器,该计数器在一个事件之后给出delta t,该事件对于每个个体的特定年份被伪编码为1。例如,在1990-2006年等随机年份内,对个人进行了观察,其中一个单独的变量表示1996年某一事件的1。计数器需要在下一年开始,应该以下一个个体(id)结束,并且需要在该个体的事件发生之前为零 当前数据如下所示: df <- data.frame(id= rep(c("1","2","3"), each=6), year=rep(1998:200

对于一个面板数据集(GSOEP),我需要创建一个时间计数器,该计数器在一个事件之后给出delta t,该事件对于每个个体的特定年份被伪编码为1。例如,在1990-2006年等随机年份内,对个人进行了观察,其中一个单独的变量表示1996年某一事件的1。计数器需要在下一年开始,应该以下一个个体(id)结束,并且需要在该个体的事件发生之前为零

当前数据如下所示:

df <- data.frame(id= rep(c("1","2","3"), each=6), year=rep(1998:2003, times=3), event=c(0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0), stringsAsFactors=FALSE)

   id year event
1   1 1998     0
2   1 1999     0
3   1 2000     1
4   1 2001     0
5   1 2002     0
6   1 2003     0
7   2 1998     0
8   2 1999     0
9   2 2000     0
10  2 2001     0
11  2 2002     1
12  2 2003     0
13  3 1998     0
14  3 1999     1
15  3 2000     0
16  3 2001     0
17  3 2002     0
18  3 2003     0

df可能不是最优雅的版本,但是如果您的数据集不是太大,下面几行可能是一个开始

library(data.table)
df <- data.frame(id= rep(c("1","2","3"), each=6), year=rep(1998:2003, times=3), event=c(0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0), stringsAsFactors=FALSE)
DT <- as.data.table(df)

get_delta <- function(x) {
  if (all(x == 0)) {
    return(x)
  } else {
    event_position <- which(x == 1)
    x[event_position] <- 0
    if (event_position == length(x)) {
     return(x) 
    } else {
     x[(event_position+1):length(x)] <- seq(length(x)-event_position)
     return(x)
    }
  }
}


DT[, delta:= get_delta(event), by = c("id")]
DT
# id year event delta
# 1:  1 1998     0     0
# 2:  1 1999     0     0
# 3:  1 2000     1     0
# 4:  1 2001     0     1
# 5:  1 2002     0     2
# 6:  1 2003     0     3
# 7:  2 1998     0     0
# 8:  2 1999     0     0
# 9:  2 2000     0     0
# 10:  2 2001     0     0
# 11:  2 2002     1     0
# 12:  2 2003     0     1
# 13:  3 1998     0     0
# 14:  3 1999     1     0
# 15:  3 2000     0     1
# 16:  3 2001     0     2
# 17:  3 2002     0     3
# 18:  3 2003     0     4

n_rows <- 1e6
DT_large <- data.table(id= as.character(rep(c(1:n_rows), each=6))
                       ,year=rep(1998:2003, n_rows), 
                       event = as.vector(sapply(1:n_rows, function(x) {
                         x <- rep(0, 6)
                         x[sample(6, 1)] <- 1  
                         x
                       }))
                       ,stringsAsFactors=FALSE)

system.time(DT_large[, delta:= get_delta(event), by = c("id")])
# User      System     elapsed 
# 9.30        0.02        9.35

#some benchmarking...
library(tidyverse)
library(data.table)
library(microbenchmark)

df <- data.frame(id= rep(c("1","2","3"), each=6), year=rep(1998:2003, times=3), event=c(0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0), stringsAsFactors=FALSE)

CPak_approach <- function() {
  df %>%
    group_by(id) %>%
    mutate(delta = ifelse(cumsum(cummax(event)) > 0, cumsum(cummax(event)) - 1, 0)) %>%
    ungroup()  
}

manuelbickel_approach <- function(x) {
  DT <- as.data.table(df)
  get_delta <- function(x) {
    if (all(x == 0)) {
      return(x)
    } else {
      event_position <- which(x == 1)
      x[event_position] <- 0
      if (event_position == length(x)) {
        return(x) 
      } else {
        x[(event_position+1):length(x)] <- seq(length(x)-event_position)
        return(x)
      }
    }
  }
  DT[, delta:= get_delta(event), by = c("id")]
}


microbenchmark(
  (dplyr_approach()),
  (manuelbickel_approach())
)

# Unit: microseconds
#       expr                      min        lq     mean   median       uq       max neval
# (dplyr_approach())         3731.146 3872.6625 4098.923 3985.363 4194.183  6441.475   100
# (manuelbickel_approach())   803.705  829.5605 1148.891 1014.105 1049.829 13993.372   100
库(data.table)

df可能不是最优雅的版本,但是如果您的数据集不是太大,下面几行可能是一个开始

library(data.table)
df <- data.frame(id= rep(c("1","2","3"), each=6), year=rep(1998:2003, times=3), event=c(0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0), stringsAsFactors=FALSE)
DT <- as.data.table(df)

get_delta <- function(x) {
  if (all(x == 0)) {
    return(x)
  } else {
    event_position <- which(x == 1)
    x[event_position] <- 0
    if (event_position == length(x)) {
     return(x) 
    } else {
     x[(event_position+1):length(x)] <- seq(length(x)-event_position)
     return(x)
    }
  }
}


DT[, delta:= get_delta(event), by = c("id")]
DT
# id year event delta
# 1:  1 1998     0     0
# 2:  1 1999     0     0
# 3:  1 2000     1     0
# 4:  1 2001     0     1
# 5:  1 2002     0     2
# 6:  1 2003     0     3
# 7:  2 1998     0     0
# 8:  2 1999     0     0
# 9:  2 2000     0     0
# 10:  2 2001     0     0
# 11:  2 2002     1     0
# 12:  2 2003     0     1
# 13:  3 1998     0     0
# 14:  3 1999     1     0
# 15:  3 2000     0     1
# 16:  3 2001     0     2
# 17:  3 2002     0     3
# 18:  3 2003     0     4

n_rows <- 1e6
DT_large <- data.table(id= as.character(rep(c(1:n_rows), each=6))
                       ,year=rep(1998:2003, n_rows), 
                       event = as.vector(sapply(1:n_rows, function(x) {
                         x <- rep(0, 6)
                         x[sample(6, 1)] <- 1  
                         x
                       }))
                       ,stringsAsFactors=FALSE)

system.time(DT_large[, delta:= get_delta(event), by = c("id")])
# User      System     elapsed 
# 9.30        0.02        9.35

#some benchmarking...
library(tidyverse)
library(data.table)
library(microbenchmark)

df <- data.frame(id= rep(c("1","2","3"), each=6), year=rep(1998:2003, times=3), event=c(0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0), stringsAsFactors=FALSE)

CPak_approach <- function() {
  df %>%
    group_by(id) %>%
    mutate(delta = ifelse(cumsum(cummax(event)) > 0, cumsum(cummax(event)) - 1, 0)) %>%
    ungroup()  
}

manuelbickel_approach <- function(x) {
  DT <- as.data.table(df)
  get_delta <- function(x) {
    if (all(x == 0)) {
      return(x)
    } else {
      event_position <- which(x == 1)
      x[event_position] <- 0
      if (event_position == length(x)) {
        return(x) 
      } else {
        x[(event_position+1):length(x)] <- seq(length(x)-event_position)
        return(x)
      }
    }
  }
  DT[, delta:= get_delta(event), by = c("id")]
}


microbenchmark(
  (dplyr_approach()),
  (manuelbickel_approach())
)

# Unit: microseconds
#       expr                      min        lq     mean   median       uq       max neval
# (dplyr_approach())         3731.146 3872.6625 4098.923 3985.363 4194.183  6441.475   100
# (manuelbickel_approach())   803.705  829.5605 1148.891 1014.105 1049.829 13993.372   100
库(data.table)
df您可以使用
groupby(id)
cumsum(cumax(event))
来接近-从
event==1开始生成
1…N
。我用
ifelse(…)
包装它,从
>0
的值中减去1

library(tidyverse)
df %>%
  group_by(id) %>%
  mutate(delta = ifelse(cumsum(cummax(event)) > 0, cumsum(cummax(event)) - 1, 0)) %>%
  ungroup()

# A tibble: 18 x 4
   # id     year event delta
   # <chr> <int> <dbl> <dbl>
 # 1 1      1998    0.    0.
 # 2 1      1999    0.    0.
 # 3 1      2000    1.    0.
 # 4 1      2001    0.    1.
 # 5 1      2002    0.    2.
 # 6 1      2003    0.    3.
 # 7 2      1998    0.    0.
 # 8 2      1999    0.    0.
 # 9 2      2000    0.    0.
# 10 2      2001    0.    0.
# 11 2      2002    1.    0.
# 12 2      2003    0.    1.
# 13 3      1998    0.    0.
# 14 3      1999    1.    0.
# 15 3      2000    0.    1.
# 16 3      2001    0.    2.
# 17 3      2002    0.    3.
# 18 3      2003    0.    4.
库(tidyverse)
df%>%
分组依据(id)%>%
突变(delta=ifelse(cumsum(cummax(event))>0,cumsum(cummax(event))-1,0))%>%
解组()
#一个tibble:18x4
#id年事件增量
#    
# 1 1      1998    0.    0
# 2 1      1999    0.    0
# 3 1      2000    1.    0
# 4 1      2001    0.    1.
# 5 1      2002    0.    2.
# 6 1      2003    0.    3.
# 7 2      1998    0.    0
# 8 2      1999    0.    0
# 9 2      2000    0.    0
# 10 2      2001    0.    0
# 11 2      2002    1.    0
# 12 2      2003    0.    1.
# 13 3      1998    0.    0
# 14 3      1999    1.    0
# 15 3      2000    0.    1.
# 16 3      2001    0.    2.
# 17 3      2002    0.    3.
# 18 3      2003    0.    4.
您可以使用
分组依据(id)
cumsum(cumax(event))
来接近-从
事件==1开始生成
1…N
。我用
ifelse(…)
包装它,从
>0
的值中减去1

library(tidyverse)
df %>%
  group_by(id) %>%
  mutate(delta = ifelse(cumsum(cummax(event)) > 0, cumsum(cummax(event)) - 1, 0)) %>%
  ungroup()

# A tibble: 18 x 4
   # id     year event delta
   # <chr> <int> <dbl> <dbl>
 # 1 1      1998    0.    0.
 # 2 1      1999    0.    0.
 # 3 1      2000    1.    0.
 # 4 1      2001    0.    1.
 # 5 1      2002    0.    2.
 # 6 1      2003    0.    3.
 # 7 2      1998    0.    0.
 # 8 2      1999    0.    0.
 # 9 2      2000    0.    0.
# 10 2      2001    0.    0.
# 11 2      2002    1.    0.
# 12 2      2003    0.    1.
# 13 3      1998    0.    0.
# 14 3      1999    1.    0.
# 15 3      2000    0.    1.
# 16 3      2001    0.    2.
# 17 3      2002    0.    3.
# 18 3      2003    0.    4.
库(tidyverse)
df%>%
分组依据(id)%>%
突变(delta=ifelse(cumsum(cummax(event))>0,cumsum(cummax(event))-1,0))%>%
解组()
#一个tibble:18x4
#id年事件增量
#    
# 1 1      1998    0.    0
# 2 1      1999    0.    0
# 3 1      2000    1.    0
# 4 1      2001    0.    1.
# 5 1      2002    0.    2.
# 6 1      2003    0.    3.
# 7 2      1998    0.    0
# 8 2      1999    0.    0
# 9 2      2000    0.    0
# 10 2      2001    0.    0
# 11 2      2002    1.    0
# 12 2      2003    0.    1.
# 13 3      1998    0.    0
# 14 3      1999    1.    0
# 15 3      2000    0.    1.
# 16 3      2001    0.    2.
# 17 3      2002    0.    3.
# 18 3      2003    0.    4.

感谢您的快速建议!遗憾的是,当向量变得太大时,它会破坏我的ram,我将进一步尝试使用dplyr找到解决方案。您的数据实际有多大。我已经更新了这个示例,并使用了一个带有10e6行的DT。这对我有用。(我已进一步稍微更新了该功能)。另一件事是,您的编程问题主要与
dplyr
data.table
之间的竞争无关。感谢您的快速建议!遗憾的是,当向量变得太大时,它会破坏我的ram,我将进一步尝试使用dplyr找到解决方案。您的数据实际有多大。我已经更新了这个示例,并使用了一个带有10e6行的DT。这对我有用。(我已进一步稍微更新了该功能)。另一件事是,您的编程问题主要不是关于
dplyr
data.table
之间的竞争。谢谢您的回答,这是我想要的方向。然而,当我在我的数据中运行它时,向量delta是空的,并且似乎没有被创建,但是没有错误,即使我已经首先删除了所有NA。你知道可能是什么问题吗?没有分配数据帧,我的错。再次感谢你的回答!我认为这个答案非常好,因为它非常简洁,所以没有对CPak的批评,很好的方法(+1)!我只是想提醒@Julius,我的答案产生了相同的结果,速度快了4倍,请参见我在下面的答案中添加的基准测试。我恳请您在今后的问题中详细说明您的要求。您没有指定您只接受
dplyr
/
tidyverse
解决方案,这同样是完全正确的,请更明确地指定此类要求,以腾出其他人提出不同方法的时间。谢谢。亲爱的Manuel,问题是我无法让你的解决方案工作,因为它产生了一个计数器,无法重新启动新ID,并且对我来说不像一个明显的R初学者那么容易处理(我的错)。因此,我完全相信你的答案至少同样好,但我选择了CPak,因为从我的角度来看,它是最方便的,很抱歉,我不能同时选择两个答案。再次感谢@朱利叶斯:好的,谢谢你的反馈,很抱歉这么挑剔。我同意,对于初学者来说,像dplyr这样的软件包通常包含非常好的现成的、简洁的和可理解的解决方案,只有对于高级速度和功能需求,像我这样的自制解决方案可能会感兴趣。祝你在学习R方面取得成功!谢谢你的回复,这就是我想要走的方向。然而,当我在我的数据中运行它时,向量delta是空的,并且似乎没有被创建,但是没有错误,即使我已经首先删除了所有NA。你知道可能是什么问题吗?没有分配数据帧,我的错。再次感谢你的回答!我认为这个答案非常好,因为它非常简洁,所以没有对CPak的批评,很好的方法(+1)!我只是想注意到我的回答产生的@Julius