如何使用日期范围编写函数而不是for循环

时间:2014-07-06 01:02:24

标签: r plyr dplyr

我是新的实证研究法律教授和R.我正在研究法官的工作量(如他/她在30天内完成的案件数量)或他/她的案件积压(因为案件开放与案件在同一范围内关闭的比例)影响案件结果。一些样本数据:

# first generate a vector of dates and repeat it 4 times
beg.date <- rep(seq.Date(as.Date("2008-01-01"),as.Date("2013-12-31"),by="day"),4)
length(beg.date) # 8768
length(beg.date)/4 # 2192 dates (6 years)
# generate a vector of judges of same length
x <- factor(LETTERS[1:4]); judge <- rep(x, each=2192)
# cbind them as df
data <- cbind.data.frame(judge, beg.date)
# create end date exactly 30 days later for each case
data$end.date <- as.Date(data$beg.date + 30)
#sort by beg.date and add caseid variable
data  <- data[order(data$beg.date),]; data$caseid <- 1:8768
#reorder columns
data <- data[c(4,1,2,3)]
# reorder rows by judge and by end dates
data <- data[order(data$judge, data$end.date),]

这是数据的样子:

  caseid judge   beg.date   end.date
1      1     A 2008-01-01 2008-01-31
2      5     A 2008-01-02 2008-02-01
3      9     A 2008-01-03 2008-02-02
4     13     A 2008-01-04 2008-02-03
5     17     A 2008-01-05 2008-02-04
6     21     A 2008-01-06 2008-02-05

所以我想计算一下裁判判决当天法官的30天积压和完成率。我已经弄清楚如何创建日期间隔(窗口)并确定在该窗口内开始或结束的案例数。并且我能够在法官使用笨重for loop.

的数据集上滚动应用它
a <- data
comprate <- numeric()
ratio <- numeric()
for (j in c("A","B","C","D")){
  x=a[a$judge==j,]
for(i in 1:nrow(x)){
  y <- new_interval((x$end.date[i]-ddays(30)),x$end.date[i])
  x$comprate[i] <- length(x$end.date[x$end.date %within% y==T])
  x$ratio[i]  <- length(x$beg.date[x$beg.date %within% y==T])/x$comprate[i]
  }
comprate  <- append(comprate, x$comprate, after=length(comprate))
ratio  <- append(ratio, x$ratio, after=length(ratio))
}
a$comprate <- comprate
a$ratio <- ratio

这适用于小样本数据集,但我的项目数据有超过600万个观察(案例)。我知道有一种方法可以通过ddplydplyr执行此操作,但它只是超出我的范围。有人可以帮助我吗?

非常感谢。肯

一些跟进问题:


感谢@MrFlick提供了有用的答案。 让我看看我是否理解(或请帮助我理解)解决方案的工作原理:

dt[, comprate:=sapply(end.date, function(i) 
    sum(between(as.numeric(i)-as.numeric(end.date),0,30))), by=judge]

在此代码块中:
1 comprate变量是通过应用sapply(etc.)表达式by=judge创建的 2 sapplyfunction(i)应用于end.date的每个元素,并返回简化结果 3 function(i)end.date的第一个元素作为输入,sum返回逻辑向量between(etc.)的逻辑真值的总和。

我好,我想到这里,但在此之后,我对between如何运作以及确切地包含和评估哪些值感到困惑。那么as.numeric(i)-as.numeric(end.date)到底在做什么呢?我得到as.numeric部分 - 它只是提取表示哨兵日期之后天数的整数。

所以&#39; as.numeric(i)&#39;正在提取i-th的{​​{1}}元素的整数值?
那么end.date在做什么?

1 个答案:

答案 0 :(得分:4)

我不会感觉太糟糕,这些移动的窗口问题有点棘手。

考虑到数据的大小,我建议您使用data.table库。此库允许您索引数据,以便查找更快。我们在这里

library(data.table)
dt<-setDT(data)
setkey(dt, judge, end.date)
dt[, comprate:=sapply(end.date, function(i) 
    sum(between(as.numeric(i)-as.numeric(end.date),0,30))), by=judge]

setkey(dt, judge, beg.date)
dt[, newcase:=sapply(end.date, function(i) 
    sum(between(as.numeric(i)-as.numeric(beg.date),0,30))), by=judge]

dt[, ratio:= newcase/comprate]
a<-as.data.frame(dt)

因此我们使用setDT()data转换为data.table对象。然后我们设置键,为表添加索引。接下来,我们使用特殊的data.table语法添加新列。在这里,对于每个法官,我们计算过去30天内的结束日数。您似乎之前使用过lubridate。这里,由于Date值存储为自哨兵日期以来的天数,我只需转换为数字并自行进行减法。然后我重新排序并计算新案件的数量。我做了一个额外的步骤来计算比率。然后我将东西转换回data.frame(但您也可以将它们保存为data.table)。

因此,在此示例数据上,它运行得更快,并提供相同的结果。这确实意味着可能会为新包提供新的语法,但您应该获得更快的结果。


进一步解释

因此,让我们使用简单的向量

x<-c(1,3,6,9,10,15)

我们可以将这些作为数字形式的日期。当我们做的时候

sapply(x, function(i) i-x)

#      [,1] [,2] [,3] [,4] [,5] [,6]
# [1,]    0    2    5    8    9   14
# [2,]   -2    0    3    6    7   12
# [3,]   -5   -3    0    3    4    9
# [4,]   -8   -6   -3    0    1    6
# [5,]   -9   -7   -4   -1    0    5
# [6,]  -14  -12   -9   -6   -5    0

我们正在做的是一次获取x的每个值(i)并找到x中每个其他值的差异。每个x值都会生成上面的一列。现在我可以添加两者之间以查看差异是否介于0和10之间。

sapply(x, function(i) between(i-x, 1, 10))

#       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]
# [1,]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE
# [2,] FALSE  TRUE  TRUE  TRUE  TRUE FALSE
# [3,] FALSE FALSE  TRUE  TRUE  TRUE  TRUE
# [4,] FALSE FALSE FALSE  TRUE  TRUE  TRUE
# [5,] FALSE FALSE FALSE FALSE  TRUE  TRUE
# [6,] FALSE FALSE FALSE FALSE FALSE  TRUE

因此我们使用between(来自data.table包)将结果限制为过去的特定窗口。现在,我们不是为每个x值返回一列,而是取两个值之间的sum(),这会将所有TRUE值变为1,将FALSE变为0

sapply(x, function(i) sum(between(i-x, 0, 10)))
# [1] 1 2 3 4 5 4