2015-08-21 56 views
1

我有以下dataframes之间:R:序列天的日期

AllDays 
2012-01-01 
2012-01-02 
2012-01-03 
... 
2015-08-18 

Leases 
StartDate EndDate 
2012-01-01 2013-01-01 
2012-05-07 2013-05-06 
2013-09-05 2013-12-01 

我想要做的是,在数据帧ALLDAYS每个日期,计算但在效果上的租约数量。例如如果有4个租赁开始日期< = 2015-01-01和结束日期> = 2015-01-01,那么我想在该数据框中放置一个4。

我有以下代码

for (i in 1:nrow(leases)) 
    { 
    occupied = seq(leases$StartDate[i],leases$EndDate[i],by="days") 
    occupied = occupied[occupied < dateOfInt] 
    matching = match(occupied,allDays$Date) 
    allDays$Occupancy[matching] = allDays$Occupancy[matching] + 1 
    } 

其工作,但因为我有大约5000租约,它需要大约1.1秒。有没有人有更高效的方法,需要更少的计算时间? 感兴趣的日期只是当前日期,并且仅用于确保它不会计入将来的租约日期。

+1

是存储为日期变量的所有日期吗?似乎是'foverlaps'的东西,但我仍然不知道如何使用该功能:\ – MichaelChirico

回答

4

使用seq几乎肯定是低效率的 - 想象一下,您的数据租约长达10000年。 seq将永远持续并返回10000 * 365-1天,这对我们无关紧要。然后,我们必须使用%in%,这也会产生相同数量的不必要的比较。

我不确定以下是最好的方法(我相信有一个完全矢量化的解决方案),但它更接近问题的核心。

数据

set.seed(102349) 
days<-data.frame(AllDays=seq(as.Date("2012-01-01"), 
          as.Date("2015-08-18"),"day")) 

leases<-data.frame(StartDate=sample(days$AllDays,5000L,T)) 
leases$EndDate<-leases$StartDate+round(rnorm(5000,mean=365,sd=100)) 

方法

使用data.tablesapply

library(data.table) 
setDT(leases); setDT(days) 

days[,lease_count:= 
     sapply(AllDays,function(x) 
     leases[StartDate<=x&EndDate>=x,.N])][] 
     AllDays lease_count 
    1: 2012-01-01   5 
    2: 2012-01-02   8 
    3: 2012-01-03   11 
    4: 2012-01-04   16 
    5: 2012-01-05   18 
    ---      
1322: 2015-08-14  1358 
1323: 2015-08-15  1358 
1324: 2015-08-16  1360 
1325: 2015-08-17  1363 
1326: 2015-08-18  1359 
+0

感谢您的回应。我不断收到一个错误,指出“较长的对象长度不是较短对象长度的倍数”。有任何想法吗?谢谢! – Mike

+0

对不起,我的错!有用!花费超过0.5秒,好得多!非常感谢! – Mike

+0

@Mike检查编辑一些解释 – MichaelChirico

1

的另一种方法,但我不知道它的速度更快。

library(lubridate) 
library(dplyr) 

AllDays = data.frame(dates = c("2012-02-01","2012-03-02","2012-04-03")) 

Lease = data.frame(start = c("2012-01-03","2012-03-01","2012-04-02"), 
        end = c("2012-02-05","2012-04-15","2012-07-11")) 

# transform to dates 
AllDays$dates = ymd(AllDays$dates) 
Lease$start = ymd(Lease$start) 
Lease$end = ymd(Lease$end) 

# create the range id 
Lease$id = 1:nrow(Lease) 

AllDays 

#  dates 
# 1 2012-02-01 
# 2 2012-03-02 
# 3 2012-04-03 

Lease 

#  start  end id 
# 1 2012-01-03 2012-02-05 1 
# 2 2012-03-01 2012-04-15 2 
# 3 2012-04-02 2012-07-11 3 


data.frame(expand.grid(AllDays$dates,Lease$id)) %>%  # create combinations of dates and ranges 
    select(dates=Var1, id=Var2) %>% 
    inner_join(Lease, by="id") %>%       # join information 
    rowwise %>% 
    do(data.frame(dates=.$dates, 
       flag = ifelse(.$dates %in% seq(.$start,.$end,by="1 day"),1,0))) %>%  # create ranges and check if the date is in there 
    ungroup %>% 
    group_by(dates) %>% 
    summarise(N=sum(flag)) 

#  dates N 
# 1 2012-02-01 1 
# 2 2012-03-02 1 
# 3 2012-04-03 2 
0

尝试使用lubridate包。为每个租约创建一个时间间隔。再算上每个日期在租赁的时间间隔。

# make some data 
AllDays <- data.frame("Days" = seq.Date(as.Date("2012-01-01"), as.Date("2012-02-01"), by = 1)) 
Leases <- data.frame("StartDate" = as.Date(c("2012-01-01", "2012-01-08")), 
       "EndDate" = as.Date(c("2012-01-10", "2012-01-21"))) 
library(lubridate) 

x <- new_interval(Leases$StartDate, Leases$EndDate, tzone = "UTC") 
AllDays$NumberInEffect <- sapply(AllDays$Days, function(a){sum(a %within% x)}) 

输出

head(AllDays) 
     Days NumberInEffect 
1 2012-01-01    1 
2 2012-01-02    1 
3 2012-01-03    1 
4 2012-01-04    1 
5 2012-01-05    1 
6 2012-01-06    1 
2

没有你的数据,我不能对此进行测试是否是速度更快,但它会与完成工作更少的代码:

for (i in 1:nrow(AllDays)) AllDays$tally[i] = sum(AllDays$AllDays[i] >= Leases$Start.Date & AllDays$AllDays[i] <= Leases$End.Date) 

我用以下来测试它;注意,在这两个数据帧相关的列被格式化为日期:

AllDays = data.frame(AllDays = seq(from=as.Date("2012-01-01"), to=as.Date("2015-08-18"), by=1)) 
Leases = data.frame(Start.Date = as.Date(c("2013-01-01", "2012-08-20", "2014-06-01")), End.Date = as.Date(c("2013-12-31", "2014-12-31", "2015-05-31"))) 
5

这正是foverlaps眼前一亮的问题:子集化(foverlaps基于另一个data.frame一个data.frame似乎为目的进行调整)。

基于@ MichaelChirico的数据。

setkey(days[, AllDays1:=AllDays,], AllDays, AllDays1) 
setkey(leases, StartDate, EndDate) 
foverlaps(leases, days)[, .(lease_count=.N), AllDays] 
# user system elapsed 
# 0.114 0.018 0.136 
# @MichaelChirico's approach 
# user system elapsed 
# 0.909 0.000 0.907 

Here是它如何工作的@Arun,这让我开始与data.table的简要说明。

+2

这应该是被接受的答案.. :-(。并且很自然地知道这个答案让你开始使用dt。 – Arun

+1

谢谢,@Arun,用来欺骗我使用data.table :) – Khashaa