R - Группировка данных по идентификатору, подсчет количества записей в диапазонах дат начала и окончания для каждого идентификатора. - PullRequest
2 голосов
/ 06 мая 2019

Обратите внимание, что я уже написал некоторый код, использующий dplyr, чтобы делать то, что мне нужно, но он кажется очень неуклюжим, и мне интересно, есть ли более элегантное решение

У меня есть датафрейм, который, по сути, таков:

df = data.frame(
  id = c(1,1,1,2,2,2),
  date = as.Date(c('2018/01/01', '2018/01/02', 
      '2018/01/03', '2018/01/01', '2018/01/02', '2018/06/01'))
)
  id       date
1  1 2018-01-01
2  1 2018-01-02
3  1 2018-01-03
4  2 2018-01-01
5  2 2018-01-02
6  2 2018-06-01

Я хочу получить таблицу, которая показывает для каждого идентификатора количество записей в течение 30 дней после первой записи и количество записей в течение 30 дней после последней записи. Для этой простой версии вывод должен выглядеть следующим образом:

id  start.records   end.records
1   3               3
2   2               1

Я могу получить желаемый вывод с помощью этого кода:

df %>%
  group_by(id) %>%
  summarize(min.date = min(date)) %>%
  mutate(min.date.plus.30 = min.date + 30) %>%
  fuzzy_left_join(
    df,
    by = list(x=c("id", "min.date.plus.30"), y=c("id", "date")),
    match_fun = list(`==`, `>`)
  ) %>%
  group_by(id.x, min.date) %>%
  summarize(start.records = n()) %>%
  left_join(
    df %>%
      group_by(id) %>%
      summarize(max.date = max(date)) %>%
      mutate(max.date.minus.30 = max.date - 30) %>%
      fuzzy_left_join(
        df,
        by = list(x=c("id", "max.date.minus.30"), y=c("id", "date")),
        match_fun = list(`==`, `<`)
      ) %>%
      group_by(id.x, max.date) %>%
      summarize(end.records = n()),
    by = "id.x"
  )

Но это похоже на очень не элегантное решение.

Есть ли лучший способ сделать это? Я бы предпочел не использовать sqldf, поскольку он не легко обрабатывает вычисления даты, а мой реальный набор данных содержит более 150 000 строк, и даже простые тестовые запросы sqldf выполняются вечно.

Заранее спасибо за любую помощь!

1 Ответ

2 голосов
/ 06 мая 2019

Может быть, мы можем использовать

library(data.table)
library(lubridate)
setDT(df)[, .(start.records = sum(date <=  (first(date) + days(30))), 
       end.records = sum(date >= (last(date) - days(30)))), by = id]
#   id start.records end.records
#1:  1             3           3
#2:  2             2           1

Или используя dplyr

library(dplyr)
df %>%
   group_by(id) %>%
   summarise(
       start.records = sum(date <=  (first(date) + days(30))), 
       end.records = sum(date >= (last(date) - days(30))))
# A tibble: 2 x 3
#     id start.records end.records
#  <dbl>         <int>       <int>
#1     1             3           3
#2     2             2           1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...