#r #plyr #dplyr
#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-дневное отставание судьи и коэффициент завершения были в день вынесения решения по делу. Я выяснил, как создать интервал дат (окно) и определить количество обращений, которые начались или закончились в этом окне. И я могу применять ее на постоянной основе к набору данных judge, используя неуклюжий 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
Это работает с небольшим набором выборочных данных, но данные моего проекта содержат более 6 миллионов наблюдений (случаев). Я знаю, что есть способ сделать это с помощью ddply
or dplyr
, но это просто выше моих сил. Может ли кто-нибудь мне помочь?
Большое спасибо. Кен
Некоторые последующие вопросы:
Спасибо @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 sapply
применяет function(i)
к каждому элементу end.date
и возвращает упрощенный результат.
3 function(i)
принимает в качестве входных данных первый элемент end.date
, sum
возвращает сумму логических истинных значений логического вектора between(etc.)
.
Я в порядке, я думаю до этого, но после этого я запутался в том, как between
работает и какие именно значения включаются и оцениваются. Так что же именно as.numeric(i)-as.numeric(end.date)
делается? Я получаю as.numeric
часть — это просто извлечение целого числа, которое представляет количество дней после контрольной даты.
Итак, ‘as.numeric(i)’ извлекает целочисленное значение i-th
элемента end.date
?
Тогда что - as.numeric(end.date)
делается?
Ответ №1:
Я бы не стал слишком расстраиваться, эти проблемы с перемещением окон немного сложны.
Учитывая объем ваших данных, я мог бы предложить использовать 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
это раньше. Здесь, поскольку значения даты хранятся как количество дней, прошедших с контрольной даты, я просто преобразую в числовое значение и делаю вычитание самостоятельно. Затем я переупорядочиваю и вычисляю количество новых обращений. Я делаю один дополнительный шаг, чтобы вычислить соотношение. Затем я конвертирую данные обратно в 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
значение генерирует один из приведенных выше столбцов. Теперь я могу добавить значение between, чтобы увидеть, есть ли различия, скажем, между 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()
значение между значениями, которое превратит все ИСТИННЫЕ значения в 1, а ЛОЖНЫЕ в 0
sapply(x, function(i) sum(between(i-x, 0, 10)))
# [1] 1 2 3 4 5 4
Комментарии:
1. Спасибо за щедрый ответ. Сейчас я разбираюсь с этим. И освоение нового набора навыков.
2. Я в порядке, я думаю, до определенного момента (смотрите Мои последующие вопросы в моем первоначальном вопросе). Но после этого я запутался в том, как
between
работает и какие именно значения включаются и оцениваются. Так что же именноas.numeric(i)-as.numeric(end.date)
делается? Я получаюas.numeric
часть — это просто извлечение целого числа, которое представляет количество дней после контрольной даты. Итак, ‘as.numeric(i)’ извлекает целочисленное значение первого элементаend.date
? Тогда что- as.numeric(end.date)
делает?3. @kmayeaux Я добавил несколько дополнительных объяснений и простой пример, чтобы более наглядно показать, как это работает.
4. большое спасибо за урок. Таким образом, повышение скорости заключается в выполнении этого в виде матрицы, а не в сканировании каждого элемента
end.date
, чтобы увидеть, находится ли он в интервале, а затем в суммировании T — а затем повторении этого для каждого значенияend.date
?5. На самом деле, повышение скорости происходит за счет использования
data.table
, которое способно выполнять эту операцию быстрее за счет индексации данных. Приведенное выше описание на самом деле о том, почему вы ожидаете, что эти функции будут работать. Фактическая реализация того, как они выполняются, может отличаться вdata.table
самих вызовах.