Количество перекрывающихся даты и времени внутри одной таблицы (R) - PullRequest
4 голосов
/ 05 октября 2019

У меня есть таблица из 50 000 строк с четырьмя столбцами.

ID     Arrival             Departure             Gender

1   10/04/2015 23:14    11/04/2015 00:21           F
1   11/04/2015 07:59    11/04/2015 08:08           F
3   10/04/2017 21:53    30/03/2017 23:37           M
3   31/03/2017 07:09    31/03/2017 07:57           M
3   01/04/2017 01:32    01/04/2017 01:35           M
3   01/04/2017 13:09    01/04/2017 14:23           M
6   10/04/2015 21:31    10/04/2015 23:17           F
6   10/04/2015 23:48    11/04/2015 00:05           F
6   01/04/2016 21:45    01/04/2016 22:48           F
6   02/04/2016 04:54    02/04/2016 07:38           F
6   04/04/2016 18:41    04/04/2016 22:48           F
10  10/04/2015 22:39    11/04/2015 00:42           M
10  13/04/2015 02:57    13/04/2015 03:07           M
10  31/03/2016 22:29    01/04/2016 08:39           M
10  01/04/2016 18:49    01/04/2016 19:44           M
10  01/04/2016 22:28    02/04/2016 00:31           M
10  05/04/2017 09:27    05/04/2017 09:28           M 
10  06/04/2017 15:12    06/04/2017 15:43           M

Это очень маленькое представление таблицы. То, что я хочу выяснить, это то, что в то же время, что и каждая запись, сколько других присутствовало, а затем разделить их по полу. Итак, скажем, например, что во время первого присутствия человека с идентификатором 1 присутствовало лицо с идентификатором 6, а лицо с идентификатором 10 присутствовало дважды в одном и том же интервале. Это будет означать, что в то же время произошло 2 других совпадения. Это также означает, что лицо с ID 1 перекрывается с 1 мужчиной и 1 женщиной.

Таким образом, его результат должен выглядеть следующим образом:

ID           Arrival            Departure         Males encountered        Females encountered
1       10/04/2015 23:14    11/04/2015 00:21             1                          1

Как бы я мог рассчитать это? Я пытался работать с foverlaps и сумел решить это с помощью Excel, но я бы хотел сделать это в R.

Ответы [ 2 ]

1 голос
/ 05 октября 2019

Вот решение data.table с использованием foverlaps.

Во-первых, обратите внимание, что в ваших данных есть ошибка:

ID           Arrival           Departure      Gender
3   10/04/2017 21:53    30/03/2017 23:37           M

Пользователь прибыл почти один месяц после того, как он фактически ушел . Мне нужно было избавиться от этих данных, чтобы foverlaps запустился.

library(data.table)

dt <- data.table(df)
dt <- dt[Departure > Arrival, ]  # filter wrong cases

setkey(dt, "Arrival", "Departure")  # prepare for foverlaps
dt2 <- copy(dt)  # use a different dt, inherits the key

запустите foverlaps, а затем

  • отфильтруйте (оставьте только) случаи, когда приход второгоperson - это до , чем ID и те же пользовательские случаи.
  • Добавьте переменную, в которой мы подсчитываем одновременных гостей мужского пола, и
  • переменную, в которой мы подсчитываем одновременных гостей женского пола,все сгруппированы по ID и прибытию

.

simultaneous <- foverlaps(dt, dt2)[i.Arrival <= Arrival & ID != i.ID,
                                       .(malesEncountered = sum(i.Gender == "M"),
                                         femalesEncountered = sum(i.Gender == "F")), 
                                       by = .(ID, Arrival)]

Объедините результаты предыдущей команды с нашей исходной таблицей по ID и прибытию

result <- simultaneous[dt, on = .(ID, Arrival)]

: преобразовать в ноль NA в malesEncountered и femalesEncountered:

result[is.na(malesEncountered), malesEncountered := 0][
                 is.na(femalesEncountered), femalesEncountered := o]

установить порядок столбцов в более приятное значение

setcolorder(result, c(1, 2, 5, 6, 3, 4))[]
1 голос
/ 05 октября 2019

Вот одна возможность. При этом используется интервал lubridate и функция int_overlaps, которая находит перекрытия даты. Это имеет недостаток: интервал не работает с dplyr . Так что эта версия просто выполняет всю работу вручную в цикле for.

Она начинается с создания случайного набора данных из 1000 строк, который соответствует вашему: каждый человек приезжает в течение двухлетнего периода и уходит через один или два дня спустя.

Для запуска 1000 требуется около 24 секунд, поэтому вы можете ожидать, что это займет некоторое время для 50K! Цикл for выводит номер строки, чтобы вы могли видеть, где он находится.

Любые вопросы по коду, я знаю.

Должен быть более быстрый векторизованный способ, но интервал не показалсяиграть хорошо с применить либо. У кого-то еще может быть что-то быстрее ...

Конечный результат выглядит как this

library(tidyverse)
library(lubridate)

#Sample data:
#(Date sampling code: https://stackoverflow.com/questions/21502332/generating-random-dates)
#Random dates between 2017 and 2019
x <- data.frame(
  ID = c(1:1000),
  Arrival = sample(seq(as.Date('2017/01/01'), as.Date('2019/01/01'), by="day"), 1000, replace = T),
  Gender = ifelse(rbinom(1000,1,0.5),'Male','Female')#Random Male female 50% probabiliity
)

#Make departure one or two days after arrival
x$Departure = x$Arrival + sample(1:2,1000, replace=T)


#Lubridate has a function for checking whether date intervals overlap
#https://lubridate.tidyverse.org/reference/interval.html

#So first, let's make the arrival and departure dates into intervals
x$interval <- interval(x$Arrival,x$Departure)


#Then for every person / row
#We want to know if their interval overlaps with the rest

#At the moment, dplyr doesn't play nice with interval
#https://github.com/tidyverse/dplyr/issues/3206

#So let's go through each row and do this manually
#Keep each person's result in list initially
gendercounts <- list()

#Check timing
t <- proc.time()

#Go through every row manually (sigh!
for(i in 1:nrow(x)){

  print(paste0("Row ",i))

  #exclude self (don't want to check date overlap with myself)
  overlapcheck <- x[x$ID != x$ID[i],]

  #Find out what dates this person overlaps with - can do all other intervals in one command
  overlapcheck$overlaps <- int_overlaps(x$interval[i],overlapcheck$interval)

  #Eyeball check that is finding the overlaps we want
  #Is this ID date overlapping? Tick
  #View(overlapcheck[overlapcheck$overlaps,])

  #Use dplyr to find out the number of overlaps for male and female
  #Keep only columns where the overlap is TRUE
  #Also drop the interval column first tho as dplyr doesn't like it... (not tidy!)
  gendercount <- overlapcheck %>% 
    select(-interval) %>% 
    filter(overlaps) %>% 
    group_by(Gender) %>%
    summarise(count = n()) %>% #Get count of observations for each overlap for each sex
    complete(Gender, fill = list(count = 0))#Need this to keep zero counts: summarise drops them otherwise


  #We want count for each gender in their own column, so make wide
  gendercount <- gendercount %>% 
    spread(key = Gender, value = count)

  #Store for turning into dataframe shortly
  gendercounts[[length(gendercounts)+1]] <- gendercount

}

#Dlyr command: turn list into dataframe
gendercounts <- bind_rows(gendercounts)

#End result. Drop interval column, order columns
final <- cbind(x,gendercounts) %>% 
  select(ID,Arrival,Departure,Gender,Male,Female)

#~24 seconds per thousand
proc.time()-t

...