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