我有一个包含三列的数据帧Data1
:NoContract
、IniDate
、FinDate
,分别表示合同开始和结束时的标识符。另一方面,我有一个分析期:2012年1月1日到2014年12月31日。我想知道在分析期的每个月内有多少合约处于有效状态,我所说的有效是指一个合约在一个月的分析期内至少有一天的日期在IniDate
和FinDate
之间。
我试着在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中这样做是有效的?或者任何关于如何改进代码的建议。谢谢你的支持。
发布于 2015-06-10 05:22:41
这里有一个使用data.table foverlaps
的选项。
foverlaps
是使用interval的合并。您应该具有相同的列名才能进行合并。您还应该设置第二个表的键。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
https://stackoverflow.com/questions/30742609
复制相似问题