У меня есть главная таблица, которая содержит даты основных событий для каждого человека:

dfMain <- data.frame(last    = c("2017-08-01", "2017-08-01", "2017-08-05","2017-09-02","2017-09-02"),
                 previous    = c(NA, NA, "2017-08-01", "2017-08-05", "2017-08-01"),
                 personid    = c(12341, 122345, 12341, 12341, 122345),
                 diff        = c(NA, NA, 4, 28, 32))

(NA для переменных «previous» и «diff» указывают, что у этого персонажа было первое «главное четное», т. Е. Предыдущие даты и разница во времени отсутствуют)

У меня также есть вторичная таблица, которая состоит из «вторичного события» для каждого человека:

dfSecondary <- data.frame(date = c("2017-09-01", "2017-08-30", "2017-08-04", "2017-08-02", "2017-08-02"),
                      personid = c(122345, 122345, 12341, 122345, 12341))

Мой вопрос: каков оптимальный способ (из-за объема моих данных) дополнить мой фрейм данных «dfMain» числом уникальных вторичных событий между датами основных событий для каждого человека.

В фиктивном примере моя цель - получить эту таблицу:

Occurances  <- c(NA, NA, 2, 0, 3)
dfObjective <- data.frame(dfMain, Occurances)
4
Vasilis Vasileiou 4 Сен 2017 в 15:37

4 ответа

Лучший ответ

{J0ap} подход Jaap с использованием неэквивалентного соединения может быть сжат в виде " один лайнер":

dfMain[, Occurrences := dfSecondary[dfMain, 
                                    on = .(personid, date <= last, date >= previous), 
                                    .N, by = .EACHI]$N][]
         last   previous personid diff Occurrences
1: 2017-08-01       <NA>    12341   NA           0
2: 2017-08-01       <NA>   122345   NA           0
3: 2017-08-05 2017-08-01    12341    4           2
4: 2017-09-02 2017-08-05    12341   28           0
5: 2017-09-02 2017-08-01   122345   32           3

dfSecondary[dfMain, ...] является правым соединением non-equi , которое принимает все строки dfMain и агрегирует в объединении. Результат имеет тот же номер и порядок строк, что и dfMain. Итак, мы можем выбрать столбец подсчета N и создать новый Occurrences столбец.

Соединения Non-equi - это новая функция, представленная в data.table версии 1.9.8 (CRAN 25 ноября 2016 г.).

Данные

Примеры наборов данных должны быть приведены к классу data.table, а различные столбцы даты должны быть преобразованы в класс даты.

library(data.table)
cols <- c("last", "previous")
setDT(dfMain)[, (cols) := lapply(.SD, as.IDate), .SDcols = cols][]
setDT(dfSecondary)[, date := as.IDate(date)][]
4
Community 20 Июн 2020 в 09:12

Вот решение из tidyverse.

library(tidyverse)

# Convert columns of factor to date class
# Add an ID column
dfMain2 <- dfMain %>% 
  mutate_if(is.factor, as.character) %>%
  mutate_if(is.character, as.Date) %>%
  mutate(ID = 1:n())

# Convert columns of factor to date class
# Add a Count column
dfSecondary2 <- dfSecondary %>% 
  mutate_if(is.factor, as.character) %>%
  mutate_if(is.character, as.Date) %>%
  mutate(Count = 1)

# Create sequence of dates between previous and last
# Unnest the data frame
# Perform join based on "Period" = "date", "personid"
# Group the data frame by ID and calculate the total count
dfMain3 <- dfMain2 %>%
  drop_na(previous) %>%
  mutate(Period = map2(previous, last, seq, by = 1)) %>%
  unnest() %>%
  left_join(dfSecondary2, by = c("Period" = "date", "personid")) %>%
  group_by(ID) %>%
  summarise(Occurances = sum(Count, na.rm = TRUE))

# Join the data frame by ID to create dfObjective
dfObjective <- dfMain2 %>%
  left_join(dfMain3, by = "ID") %>%
  select(-ID)

dfObjective
        last   previous personid diff Occurances
1 2017-08-01       <NA>    12341   NA         NA
2 2017-08-01       <NA>   122345   NA         NA
3 2017-08-05 2017-08-01    12341    4          2
4 2017-09-02 2017-08-05    12341   28          0
5 2017-09-02 2017-08-01   122345   32          3

Данные

dfMain <- data.frame(last    = c("2017-08-01", "2017-08-01", "2017-08-05","2017-09-02","2017-09-02"),
                     previous    = c(NA, NA, "2017-08-01", "2017-08-05", "2017-08-01"),
                     personid    = c(12341, 122345, 12341, 12341, 122345),
                     diff        = c(NA, NA, 4, 28, 32))


dfSecondary <- data.frame(date = c("2017-09-01", "2017-08-30", "2017-08-04", "2017-08-02", "2017-08-02"),
                          personid = c(122345, 122345, 12341, 122345, 12341))
1
www 4 Сен 2017 в 13:07

С dplyr и tidyr

library(dplyr)
library(tidyr)

dfMain %>%
  left_join(dfSecondary,by="personid") %>%                  # put everything together
  mutate_at(c("last","previous","date"),as.Date) %>%        # reformat as date
  mutate(is_between = date <= last & date >= previous) %>%  # tests if it's in between
  group_by(last,previous,personid,diff) %>%                 # group by columns from initial df
  summarize(Occ = sum(is_between)) %>%                      # count how many we have in between
  `[<-`(is.na(.$previous),"Occ",NA) %>%                     # add NAs where previous was NA
  ungroup                                                   # ungroup to have regular table

# # A tibble: 5 x 5
#         last   previous personid  diff   Occ
#       <date>     <date>    <dbl> <dbl> <int>
# 1 2017-08-01         NA    12341    NA    NA
# 2 2017-08-01         NA   122345    NA    NA
# 3 2017-08-05 2017-08-01    12341     4     2
# 4 2017-09-02 2017-08-01   122345    32     3
# 5 2017-09-02 2017-08-05    12341    28     0

Примечание: порядок был изменен, скажите, если это проблема, и я ее исправлю.

3
Moody_Mudskipper 4 Сен 2017 в 13:58

Используя data.table - пакет:

# load 'data.table' package and convert date-columns to date-class
library(data.table)
setDT(dfMain)[, 1:2 := lapply(.SD, as.IDate), .SDcols = 1:2][]
setDT(dfSecondary)[, date := as.IDate(date)][]

# create a reference
dfSecondary <- dfSecondary[dfMain
                           , on = .(personid, date > previous, date < last)
                           , .(dates = x.date)
                           , by = .EACHI]
setnames(dfSecondary, 2:3, c('previous','last'))

# join and summarise
dfMain[na.omit(dfSecondary, cols = 1:3)[, sum(!is.na(dates), na.rm = TRUE)
                                        , by = .(personid, previous, last)]
       , on = .(personid, previous, last)
       , Occ := V1][]

Что дает:

         last   previous personid diff Occ
1: 2017-08-01       <NA>    12341   NA  NA
2: 2017-08-01       <NA>   122345   NA  NA
3: 2017-08-05 2017-08-01    12341    4   2
4: 2017-09-02 2017-08-05    12341   28   0
5: 2017-09-02 2017-08-01   122345   32   3
5
Jaap 4 Сен 2017 в 14:36