R—在数据帧中创建条件和作为新列的更快方法

R—在数据帧中创建条件和作为新列的更快方法,r,dplyr,R,Dplyr,我最初的问题 我有一个医院就诊的数据框架,如下所示: df = data.frame(PNUM = c(1,1,1,1,2,2,2,2), indate=as.Date(c("2016-01-03","2016-05-05","2017-02-03", "2017-06-07","2016-01-03","2016-05-05", "

我最初的问题

我有一个医院就诊的数据框架,如下所示:

df = data.frame(PNUM = c(1,1,1,1,2,2,2,2),
                indate=as.Date(c("2016-01-03","2016-05-05","2017-02-03",
                                 "2017-06-07","2016-01-03","2016-05-05",
                                 "2017-02-03","2017-06-07")),
                Inpatient=c(0,1,0,1,1,1,1,0),
                AnE=c(1,0,1,0,0,0,0,1))
  PNUM     indate Inpatient AnE sum_365_Inpatient sum_365_AnE
1    1 2016-01-03         0   1                 0           0
2    1 2016-05-05         1   0                 0           1
3    1 2017-02-03         0   1                 1           0
4    1 2017-06-07         1   0                 0           1
5    2 2016-01-03         1   0                 0           0
6    2 2016-05-05         1   0                 1           0
7    2 2017-02-03         1   0                 1           0
8    2 2017-06-07         0   1                 1           0
输出:

  PNUM     indate Inpatient AnE
1    1 2016-01-03         0   1
2    1 2016-05-05         1   0
3    1 2017-02-03         0   1
4    1 2017-06-07         1   0
5    2 2016-01-03         1   0
6    2 2016-05-05         1   0
7    2 2017-02-03         1   0
8    2 2017-06-07         0   1
现在,我想添加列,以反映当前“indate”之前365天的“住院”和“电子”就诊次数。预期结果如下所示:

df = data.frame(PNUM = c(1,1,1,1,2,2,2,2),
                indate=as.Date(c("2016-01-03","2016-05-05","2017-02-03",
                                 "2017-06-07","2016-01-03","2016-05-05",
                                 "2017-02-03","2017-06-07")),
                Inpatient=c(0,1,0,1,1,1,1,0),
                AnE=c(1,0,1,0,0,0,0,1))
  PNUM     indate Inpatient AnE sum_365_Inpatient sum_365_AnE
1    1 2016-01-03         0   1                 0           0
2    1 2016-05-05         1   0                 0           1
3    1 2017-02-03         0   1                 1           0
4    1 2017-06-07         1   0                 0           1
5    2 2016-01-03         1   0                 0           0
6    2 2016-05-05         1   0                 1           0
7    2 2017-02-03         1   0                 1           0
8    2 2017-06-07         0   1                 1           0
我已经找到了一种方法来实现这一点(见下文),但速度非常慢(对于一个包含10000行的新列,大约需要4分钟)。我的原始数据帧有200万行和>100列,我想为它们创建这些总和。我对R比较陌生,通过将几个类似问题的内容组合在一起,创建了以下解决方案。我想这不是很有效。我将非常感谢任何关于如何改进我的代码的建议

这是我非常低效的解决方案

我首先定义了一个函数,该函数计算某个特定列回顾X天的总和(另外受到ID的限制,因为我只想要来自同一个人的事件)


为此设计了一个非等联接

对于互斥伪列的情况…

首先,一些设置

# go to long form

library(data.table)
DT = melt(setDT(df), id=c("PNUM", "indate"), variable.name = "status")[value == 1, !"value"]
setorder(DT, PNUM, indate)

# use integer dates

DT[, indate := as.IDate(indate)]


   PNUM     indate    status
1:    1 2016-01-03       AnE
2:    1 2016-05-05 Inpatient
3:    1 2017-02-03       AnE
4:    1 2017-06-07 Inpatient
5:    2 2016-01-03 Inpatient
6:    2 2016-05-05 Inpatient
7:    2 2017-02-03 Inpatient
8:    2 2017-06-07       AnE
数一数

for (s in unique(DT$status)){
  DT[, paste0("n365_", s) := 
    .SD[status == s][.SD[, .(PNUM, d_dn = indate - 365L, d_up = indate)], 
      on=.(PNUM, indate >= d_dn, indate < d_up),
      .N, by=.EACHI]$N
 ][]
}

   PNUM     indate    status n365_AnE n365_Inpatient
1:    1 2016-01-03       AnE        0              0
2:    1 2016-05-05 Inpatient        1              0
3:    1 2017-02-03       AnE        0              1
4:    1 2017-06-07 Inpatient        1              0
5:    2 2016-01-03 Inpatient        0              0
6:    2 2016-05-05 Inpatient        0              1
7:    2 2017-02-03 Inpatient        0              1
8:    2 2017-06-07       AnE        0              1

通常,对整数的联接/查找比对浮点数的联接/查找更快,这就是为什么在这里进行转换。
lappy
for
循环方式是等效的,尽管
lappy
方式只涉及构建
查找这些
一次,因此可能会更快。

非等联接就是为了实现这一点而设计的

对于互斥伪列的情况…

首先,一些设置

# go to long form

library(data.table)
DT = melt(setDT(df), id=c("PNUM", "indate"), variable.name = "status")[value == 1, !"value"]
setorder(DT, PNUM, indate)

# use integer dates

DT[, indate := as.IDate(indate)]


   PNUM     indate    status
1:    1 2016-01-03       AnE
2:    1 2016-05-05 Inpatient
3:    1 2017-02-03       AnE
4:    1 2017-06-07 Inpatient
5:    2 2016-01-03 Inpatient
6:    2 2016-05-05 Inpatient
7:    2 2017-02-03 Inpatient
8:    2 2017-06-07       AnE
数一数

for (s in unique(DT$status)){
  DT[, paste0("n365_", s) := 
    .SD[status == s][.SD[, .(PNUM, d_dn = indate - 365L, d_up = indate)], 
      on=.(PNUM, indate >= d_dn, indate < d_up),
      .N, by=.EACHI]$N
 ][]
}

   PNUM     indate    status n365_AnE n365_Inpatient
1:    1 2016-01-03       AnE        0              0
2:    1 2016-05-05 Inpatient        1              0
3:    1 2017-02-03       AnE        0              1
4:    1 2017-06-07 Inpatient        1              0
5:    2 2016-01-03 Inpatient        0              0
6:    2 2016-05-05 Inpatient        0              1
7:    2 2017-02-03 Inpatient        0              1
8:    2 2017-06-07       AnE        0              1

通常,对整数的联接/查找比对浮点数的联接/查找更快,这就是为什么在这里进行转换。
lappy
for
循环方式是等效的,尽管
lappy
方式只涉及构建
查找这些
一次,因此可能更快。

Wow。看起来不错。但是,是否可以使用我的原始虚拟变量而不是状态来执行此操作?正如我在文章中提到的,我有100个变量,其中大多数变量并不相互排斥(例如,也可能有一行是“AnE”和“住院患者”)。很抱歉,我对data.table语法没有任何经验,无法理解;我来编辑。我没有注意到它们可能不是相互排斥的。非常感谢。第二个版本非常好用。哇。看起来不错。但是,是否可以使用我的原始虚拟变量而不是状态来执行此操作?正如我在文章中提到的,我有100个变量,其中大多数变量并不相互排斥(例如,也可能有一行是“AnE”和“住院患者”)。很抱歉,我对data.table语法没有任何经验,无法理解;我来编辑。我没有注意到它们可能不是相互排斥的。非常感谢。第二个版本非常好用。