R избежать цикла для подсчета с условиями - PullRequest
1 голос
/ 08 июля 2019

Я использую R с пакетом data.table.У меня есть цикл, в котором рассчитывается количество, но, поскольку это цикл, он чрезвычайно медленный.Теперь я хочу как-то изменить его, чтобы вычисление не занимало дней.

У меня есть набор данных, и я хочу посчитать, как часто этот человек уже появляется в наборе данных.Когда имя, фамилия и дата рождения (день рождения, месяц рождения и год рождения) совпадают, это один и тот же «человек».Однако проблема в том, что дата тоже важна.Итак, если этот человек, на которого я смотрю, появляется в наборе данных, я должен проверить, не находится ли дата «того же человека» перед датой человека, на которого я смотрю.Следовательно, один и тот же человек должен быть известен раньше, чем тот, на кого я смотрю.

Затем я также хочу вычислить среднее время между этими записями.Вот мое текущее решение (которое работает, но очень медленно):

library(data.table)
data <- data[order(-persondatetime)]
vec_countperson <- numeric(nrow(data))
vec_time <- numeric(nrow(data))

for (i in 1:nrow(data)){
  vec_countperson[i] <- data[firstname == data[i, firstname] &
                                   surname == data[i, surname] &
                                   birthdate == data[i, birthdate] &
                                   persondatetime < data[i, persondatetime], .N]
       vec_time[i] <- data[firstname == data[i, firstname] &
                                   surname == data[i, surname] &
                                   birthdate == data[i, birthdate] &
                                   persondatetime < data[i, persondatetime], 
                                   mean(abs(diff(c(persondatetime, data[i, persondatetime]))))]
}


data[, countperson := vec_countperson]
data[, timebetweenentries := vec_time]

Пример data.table будет выглядеть следующим образом:

data <- data.table(
  firstname = c("Paul", "Jens", "Jens", "Jens","Paul", "Dieter"), 
  surname = c("Mueller", "Mustermann", "Mustermann", "Mustermann", "Mueller", "Brian"), 
  birthdate = as.Date(c("1960-05-08", "1960-05-08", "1960-05-08",
                        "1960-05-08", "1960-05-08", "1960-05-08")), 
  persondatetime = as.POSIXct(c("2018-05-01 23:18:38 CET", "2018-03-01 23:18:38 CET",
                                "2018-06-01 23:18:38 CET", "2018-04-01 23:18:38 CET", 
                                "2018-04-06 23:18:38 CET", "2018-04-08 23:18:38 CET")))

Ожидаемый результат:

   firstname    surname  birthdate      persondatetime countperson timebetweenentries
1:      Jens Mustermann 1960-05-08 2018-03-01 23:18:38           0                NaN
2:      Jens Mustermann 1960-05-08 2018-04-01 23:18:38           1           30.95833
3:      Paul    Mueller 1960-05-08 2018-04-06 23:18:38           0                NaN
4:    Dieter      Brian 1960-05-08 2018-04-08 23:18:38           0                NaN
5:      Paul    Mueller 1960-05-08 2018-05-01 23:18:38           1           25.00000
6:      Jens Mustermann 1960-05-08 2018-06-01 23:18:38           2           45.97917

У вас есть идеи, как мне избежать петли?Я думал о других идеях, но моя проблема всегда связана с датой!

1 Ответ

1 голос
/ 09 июля 2019

Вы можете использовать код, подобный тому, что @ chinsoon12 опубликовал в своем комментарии, чтобы воссоздать столбец countperson.

data[data, 
     on=.(firstname, surname, birthdate=birthdate, persondatetime > persondatetime),
     countperson:=.N, 
     by=.EACHI]
data[, countperson := coalesce(countperson, 0L)]

Синтаксис data.table для этого объединения обновлений - X[I, on=.(conditions), var:=.N, by=.EACHI].Для каждой строки в data.table I найдены строки в X, соответствующие conditions.Использование аргумента by=.EACHI группирует результаты этого объединения по строкам в I.В data.table символ .N представляет количество строк в группе.В этом случае для каждой строки в I, .N - это количество строк в X, которые соответствуют на основе conditions.Если строка в I не имеет совпадающих строк в X, то .N - это NA, который мы устанавливаем в 0, используя coalesce в следующей строке.

Один из способов воссоздать вашу переменную timebetweenentries - этосгруппируйте по столбцам, которые указывают, что строки принадлежат одному и тому же человеку, вычислите среднюю разницу в persondatetime для каждой группы и назначьте ее столбцу в вашей data.table.Если вам нужно время между последовательными записями, вам нужно отсортировать persondatetime, прежде чем вы получите различия.

Приведенный ниже код выполняет всю сортировку сразу, используя функцию setkey data.table.Это должно ускорить группирование и избежать вызова сортировки (persondatetime) для каждой группы.

setkey(data, firstname, surname, birthdate, persondatetime)
data[, timebetweenentries := mean(abs(diff(persondatetime)), na.rm=T)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...