在 R 中:如何在两个日期之间按组对变量求和

Posted

技术标签:

【中文标题】在 R 中:如何在两个日期之间按组对变量求和【英文标题】:In R: how to sum a variable by group between two dates 【发布时间】:2017-11-04 20:27:08 【问题描述】:

我有两个数据框(DF1 和 DF2):

(1) DF1 包含关于个人级别的信息,即 11 年(2000-2011 年)中嵌套在 30 个单元中的 10.000 个人的信息。它包含四个变量:

    “individual”(每个人的数字 id;范围为 1-10.000) “unit”(每个单元的数字 id;范围为 1-30) "date1"(日期格式的日期,即2000-01-01等;范围从2000-01-01到2010-12-31) “日期 2”(“日期 1”+ 1 年)

(2) DF2 包含关于unit-level 的信息,即在同一时间段(2000-2011 年)与 DF1 中相同的 30 个单位,并进一步包含一个数字变量(“x” ):

    “unit”(每个单元的数字 id;范围为 1-30) “日期”(日期格式的日期,即2000-01-01等;范围从2000-01-01到2011-12-31) “x”(数值型变量,范围从0到200)

我想创建一个新变量(“newvar”),它为每个“单位”的每个“个人”提供从“date1”(DF1)到“date2”(DF2)的“x”(DF2)总和)。这意味着我想将这个新变量添加到 DF1。

例如,如果 "unit"=1 中的 "individual"=1 有 "date1"=2000-01-01 和 "date2"=2001-01-01,并且在 DF2 中 "unit"=1 有三个观察值在时间段“date1”到“date2”(即2000-01-01到2001-01-01),“x”=1,“x”=2和“x”=3,那么我想添加一个在 "unit"=1 "newvar"=6 中给出 "individual"=1 的新变量。

我假设我需要在 R 中使用 for 循环并且一直在使用以下代码:

for(i in length(DF1))

DF1$newvar[i] <-sum(DF2$x[which(DF1$date == DF1$date1[i] &
                     DF1$date == DF1P$date1[i] &
                     DF2$unit == DF1P$unit[i]),])

但得到错误信息:

Error in DF2$x[which(DF2$date ==  : incorrect number of dimensions 

任何关于如何创建此变量的想法都将不胜感激!

这里是一个小例子以及预期的输出,为了简单起见,使用一个单位:

假设 DF1 如下所示:

individual  unit  date1        date2   
1           1     2000-01-01   2001-01-01
2           1     2000-02-02   2001-02-02
3           1     2000-03-03   2000-03-03
4           1     2000-04-04   2000-04-04
5           1     2000-12-31   2001-12-31 
(...)
996         1     2010-01-01   2011-01-01
997         1     2010-02-15   2011-02-15
998         1     2010-03-05   2011-03-05
999         1     2010-04-10   2011-04-10
1000        1     2010-12-27  2011-12-27
1001        2     2000-01-01   2001-01-01
1002        2     2000-02-02   2001-02-02
1003        2     2000-03-03   2000-03-03
1004        2     2000-04-04   2000-04-04
1005        2     2000-12-31   2001-12-31 
(...)
1996        2     2010-01-01   2011-01-01
1997        2     2010-02-15   2011-02-15
1998        2     2010-03-05   2011-03-05
1999        2     2010-04-10   2011-04-10
2000        2     2010-12-027  2011-12-27
(...)
3000        34    2000-02-02   2002-02-02
3001        34    2000-05-05   2001-05-05
3002        34    2000-06-06   2001-06-06
3003        34    2000-07-07   2001-07-07
3004        34    2000-11-11   2001-11-11
(...)
9996        34    2010-02-06   2011-02-06
9997        34    2010-05-05   2011-05-05
9998        34    2010-09-09   2011-09-09 
9999        34    2010-09-25   2011-09-25
10000       34    2010-10-15   2011-10-15

假设 DF2 如下所示:

unit      date         x
1         2000-01-01   1
1         2000-05-01   2
1         2000-12-01   3
1         2001-01-02   10
1         2001-07-05   20
1         2001-12-31   30
(...) 
2         2010-05-05   1 
2         2010-07-01   1
2         2010-08-09   1
3         (...)

这是我希望 DF1 在运行代码后的样子:

individual  unit      date1        date2        newvar  
    1           1     2000-01-01   2001-01-01   6
    2           1     2000-02-02   2001-02-02   16
    3           1     2000-03-03   2001-03-03   15
    4           1     2000-04-04   2001-04-04   15
    5           1     2000-12-31   2001-12-31   60
    (...)
    996         1     2010-01-01   2011-01-01    3
    997         1     2010-02-15   2011-02-15    2
    998         1     2010-03-05   2011-03-05    2
    999         1     2010-04-10   2011-04-10    2
    1000        1     2010-12-27  2011-12-27     0
    (...)

但是,我不能简单地汇总:想象一下,在 DF1 中,每个“单元”在 2000 年至 2011 年期间每年都有数百人。而 DF2 对 2000 年至 2011 年期间的每个单元都有许多观察结果。

【问题讨论】:

请展示一个可重现的小例子和预期的输出 我没有检查代码,但是您收到的错误消息是因为您将向量 (DF2$x) 视为二维,而它只有一维。您需要删除最后一个括号之前的逗号:,]) 谢谢@Bea!删除逗号时错误消息消失,这很好。不幸的是,该代码没有产生正确的总和,即它为 DF1 中“newvar”中的所有观察值提供了值 0。 谢谢你,@akrun!我添加了一个带有预期输出的小例子 - 我希望这就是你的想法?我是新手,所以我尽力提供所需的信息...... 【参考方案1】:

我们可以使用data.table

library(data.table)
setDT(DF1)
setDT(DF2)
DF1[DF2[, .(newvar = sum(x)), .(unit, individual = cumsum(date %in% DF1$date1))],
             newvar := newvar, on = .(individual, unit)]
DF1
#    individual unit      date1      date2 newvar
#1:          1    1 2000-01-01 2001-01-01      6
#2:          2    1 2001-01-02 2002-01-02     60

或者我们可以使用非等连接

DF1[DF2[DF1, sum(x), on = .(unit, date >= date1, date <= date2),
        by = .EACHI], newvar := V1, on = .(unit, date1=date)]

DF1
#   individual unit      date1      date2 newvar
#1:          1    1 2000-01-01 2001-01-01      6
#2:          2    1 2001-01-02 2002-01-02     60

【讨论】:

非常感谢@akrun!第二个选项解决了这个问题! 我意识到代码并没有完全按照我的想法做。对于 DF1 中的每个“个人”,它将 DF2 中的“x”值从“date1”中的第一个日期到个人特定的“date2”(从 2000 年 1 月 1 日到“date2”的累积值)求和,而不是从个人特定的“date1”到个人特定的“date2”。 Exp: 如果一个人有 "date1"=2005-01-01 & "date2"=2006-01-01,输出给出从 "date1"=2000-01-01 到 "date2"= "x" 的总和2006-01-01,而不是正确地在 2005-01-01 开始求和。任何有关如何修改代码的帮助将不胜感激! 是的,我都试过了,不幸的是他们给了我同样的结果...... @Gret-D 我正在使用您的示例。也许你可以提供一个新的例子 tat 给出不同的输出 我发现了错误 - 您的版本和 Bea 的版本(在具有 2 个单位的数据子集上)现在都可以使用!问题是其中一个日期意外地是“mondate”格式。对于给您带来的困惑,我深表歉意,再次感谢您!【参考方案2】:

你快到了,我只是稍微修改了你的 for 循环,并确保日期变量被认为是这样的:

DF1$date1 = as.Date(DF1$date1,"%Y-%m-%d")
DF1$date2 = as.Date(DF1$date2,"%Y-%m-%d")
DF2$date = as.Date(DF2$date,"%Y-%m-%d")

for(i in 1:nrow(DF1))
  DF1$newvar[i] <-sum(DF2$x[which(DF2$unit == DF1$unit[i] & 
                                  DF2$date>= DF1$date1[i] &
                                  DF2$date<= DF1$date2[i])]) 

问题是,您要求 DF2$date 同时成为 == DF1$date1DF1$date2。 此外,length(DF1) 会为您提供列数。要获得行数,您可以使用nrow(DF1)dim(DF1)[1]

【讨论】:

非常感谢您的回答@Bea!不幸的是,for 循环需要很长时间才能运行,而且我没有耐心等待那么久(因为 akrun 的代码有效)。但是仅在没有 for 循环的情况下运行代码就可以正常工作(当然不能正确地对单位求和),所以我认为如果我的数据帧更小,这将是一个很好的解决方案...... 你是对的,for循环可能比data.table花费更长的时间。用 akrun 的代码解决了很好 :)

以上是关于在 R 中:如何在两个日期之间按组对变量求和的主要内容,如果未能解决你的问题,请参考以下文章

如何按组对变量求和

如何按组对变量求和

如何按组对变量求和

R按两组对变量求和[重复]

Spark:按组对记录进行排序?

r - 有效地创建变量,指示日期变量是否在事件之前(按组)