【问题标题】:Range of dates in a period of analysis in RR中分析期间的日期范围
【发布时间】:2015-08-24 21:28:34
【问题描述】:

我有一个包含三列的数据框Data1NoContractIniDateFinDate 分别代表合约的标识符,合约何时开始和何时结束。另一方面,我有一个分析期:2012 年 1 月 1 日至 2014 年 12 月 31 日。我想找出分析期内每个月有多少活跃的合约,活跃的意思是合约至少有一天IniDateFinDate 之间的日期在一个月的分析期间。

我尝试在 R 中做:

假设Data1 是:

Data1 <- data.frame(NoContract= 1:3, IniDate= as.Date(c("2011-05-03","2012-03-13","2014-03-26")),FinDate=as.Date(c("2015-01-05","2013-03-13","2015-08-19")))
Data1

  NoContract    IniDate    FinDate
1          1 2011-05-03 2015-01-05
2          2 2012-03-13 2013-03-13
3          3 2014-03-26 2015-08-19

我创建了另一个数据框 DatesCalc 为:

DatesCalc<-data.frame(monthI=seq(as.Date("2012-01-01"), as.Date("2014-12-31"), by="1 month"), monthF=(seq(as.Date("2012-02-01"), as.Date("2015-01-01"), by="1 month")-1))
head(DatesCalc)

      monthI     monthF
1 2012-01-01 2012-01-31
2 2012-02-01 2012-02-29
3 2012-03-01 2012-03-31
4 2012-04-01 2012-04-30
5 2012-05-01 2012-05-31
6 2012-06-01 2012-06-30

接下来,我写了一个函数

myfun<-function(X,Y){
  d1<-numeric()
  d2<-numeric()
  for (i in 1:36){ #36 num of rows on DatesCalc
    d1<-numeric()
    for (j in 1:3){ #3 num of rows of my Data1 (my actual case near 550K rows)
      d1<-c(d1,sum(seq(X[i,1],X[i,2],by=1)%in%seq(Y[j,2],Y[j,3],by=1),na.rm=TRUE)>0)
    }
d2<-cbind(d2,d1)
  }
  return(d2)
}

所以它的作用是,对于Data1 的每一行,创建DatesCalc 每一行的日期序列,并证明它是否在Data1 的当前行的日期序列内。此函数返回一个矩阵,其中行表示合约,列表示从 2012 年 1 月到 2014 年 12 月的月份,如果合约在一个月内处于活动状态,则每个单元格都有1,如果没有,则有0(请参阅Res)。最后我用 apply 按列求和,得到了我想要的。

Res<-myfun(DatesCalc,Data1)
Res
     d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1
[1,]  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
[2,]  0  0  1  1  1  1  1  1  1  1  1  1  1  1  1  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0
[3,]  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1  1  1

apply(Res,2,sum)
d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 d1 
 1  1  2  2  2  2  2  2  2  2  2  2  2  2  2  1  1  1  1  1  1  1  1  1  1  1  2  2  2  2  2  2  2  2  2  2

情况是我的实际Data1 中有数十万行(550K),并且在其上运行myfun 效率低下。我的问题是,也许是一种在 R 中有效的方法?或有关如何改进我的代码的任何建议。谢谢社区。​​p>

【问题讨论】:

  • 这似乎是foverlapsdata.table 包中的一个案例。您会在 SO 上找到一些非常好的问答,例如here
  • 谢谢@Henrik 我会试试foverlaps

标签: r date date-range


【解决方案1】:

这里有一个使用data.table foverlaps的选项。

  1. 首先,foverlaps 是使用区间的合并。您应该使用相同的列名来进行合并。您还应该设置第二个表的键。
  2. L 所需的输出是一个矩阵,其中行表示合同,列表示从 2012 年 1 月到 2014 年 12 月的月份,因此我创建了一个新的列期间,即合同的年月。李>
  3. 使用 dcast.data.table 以宽格式重新调整结果。

代码:

library(data.table)
setDT(Data1)
setDT(DatesCalc)
setkey(Data1, IniDate, FinDate)   ## Set keys for merge 
setnames(DatesCalc,names(DatesCalc),c('IniDate','FinDate')) ## rename for merge
dcast.data.table(        ## wide format
  foverlaps(DatesCalc, Data1, type="within")[,
        period := format(i.IniDate,'%Y-%m')], ## create a new variable here
  NoContract~period,fun=length) ## the aggregate function is the length (T/F)

  NoContract 2012-01 2012-02 2012-03 2012-04 2012-05 2012-06 2012-07 2012-08 2012-09 2012-10 2012-11 2012-12 2013-01 2013-02 2013-03 2013-04 2013-05 2013-06 2013-07
1:          1       1       1       1       1       1       1       1       1       1       1       1       1       1       1       1       1       1       1       1
2:          2       0       0       0       1       1       1       1       1       1       1       1       1       1       1       0       0       0       0       0
3:          3       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0
   2013-08 2013-09 2013-10 2013-11 2013-12 2014-01 2014-02 2014-03 2014-04 2014-05 2014-06 2014-07 2014-08 2014-09 2014-10 2014-11 2014-12
1:       1       1       1       1       1       1       1       1       1       1       1       1       1       1       1       1       1
2:       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0       0
3:       0       0       0       0       0       0       0       0       1       1       1       1       1       1       1       1       1

【讨论】:

  • 太棒了!它适用于我有 560K 行的大桌子。非常感谢。
  • 实际上,我们得出了两个不同的答案,我认为period := format(i.IniDate,'%Y-%m') 您只使用了构建矩阵的初始日期。如您所见,第二个合约可从2012-03-13 获得,在您的情况下:2012-03 的列,合约3 您得到0 并且必须是1。尽管如此,这是我需要的一种很好的方法,现在我正在尝试使用foverlaps 来获得我想要的,即分析期间每个月有多少合约处于活动状态。
  • @ChrissPaul 如果您想计算每个时期的合约数量,无需以宽格式重塑您的数据,只需按时期汇总即可,foverlaps(DatesCalc, Data1, type="within")[,period := format(i.IniDate,'%Y-%m')][,.N,period] 之类的内容将为您提供正确的答案.
  • 事实上,这正是我想要的。不过还是有一点bug的原因,如果是type="within"foverlaps 阅读了它的帮助文件后发现只考虑了区间的全部内容,所以我们应该使用type="any"以便重叠区间小于一个月(即在给定月份只有几天合同有效的情况)还是我误解了smthng?情况是我们在以下情况下得到相同的结果:foverlaps(DatesCalc, Data1, type="any")[,period := format(i.IniDate,'%Y-%m')][,.N,period]。再次感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2014-06-15
  • 1970-01-01
  • 1970-01-01
  • 2013-12-27
  • 2023-01-10
  • 1970-01-01
  • 2023-01-17
相关资源
最近更新 更多