Сопоставить значения с несколькими условиями, используя два data.frames - PullRequest
1 голос
/ 26 апреля 2020

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

Полеты, которые я хочу заменить, должны соответствовать ряду условий (в пределах определенного временного горизонта должен быть один и тот же день недели, и он должен лететь в тот же пункт назначения). Кроме того, я хочу, чтобы R выбрал рейс, ближайший (по времени) к новому времени прибытия при транзите (из данных misconnection data.frame).

Ошибка data.frame выглядит следующим образом (всего 1620 строк):

miscon <- data.frame(flight.date = as.Date(c("2019-08-05", "2019-10-03", "2019-07-21", "2019-05-29"), format="%Y-%m-%d"),
                     Outbound.airport = c("MXP", "KRK", "KLU", "OTP"),  
                     arr.time = as.POSIXct(c("19:25:00", "20:52:00", "07:33:00", "18:49:00"), format="%H:%M:%S"),    
                     next.pos.dep = as.POSIXct(c("19:36:00", "21:17:00", "07:58:00", "19:14:00"), format="%H:%M:%S"),
                     weekday = c("4", "7", "7", "3"))

view(miscon)

        flight.date    Outbound.airport    arr.time    next.pos.dep    Weekday
1       2019-08-05     MXP                 19:25:00    19:36:00        4
2       2019-10-03     KRK                 20:52:00    21:17:00        7
3       2019-07-21     KLU                 07:33:00    07:58:00        7
4       2019-05-29     OTP                 18:49:00    19:14:00        3

И график данных.frame будет выглядеть следующим образом:

tt <- data.frame(start.date = as.Date(c("2019-03-25", "2019-05-02", "2019-07-30", "2019-05-29"), format="%Y-%m-%d"),
                 end.date = as.Date(c("2019-10-21", "2019-10-27", "2019-08-26", "2019-06-01"), format="%Y-%m-%d"),
                 weekday = c("1234567", "1.3..67", "1.34567", "..3.5.."),
                 Outbound.airport = c("KLU", "KLU", "MXP", "OTP"),  
                 dep.time = as.POSIXct(c("12:20:00", "15:55:00", "19:55:00", "20:34:00"), format="%H:%M:%S"))    

view(tt)

    start.date    end.date     Weekday     Outbound.airport    dep.time
1   2019-03-25    2019-10-21   1234567     KLU                 12:20:00   
2   2019-05-02    2019-10-27   1.3..67     KLU                 15:55:00
3   2019-07-30    2019-08-26   1.34567     MXP                 19:55:00
4   2019-03-30    2019-06-01   ..3.5..     OTP                 20:34:00

В Excel эта проблема решается с помощью сопоставления индексов, которым я управлял. Тем не менее, проблема в том, что Excel справляется с этой задачей, слишком велика, поэтому мне нужно преобразовать ее в R. Я пытался использовать функцию match и mutate в R, но кажется, что значения, которые я сопоставляю, должны быть равными - что я и делаю не ожидайте, что мой будет.

Также было найдено интересное решение аналогичной проблемы с использованием пакета DescTools, который я пытался реализовать безуспешно.

get_close2 <- function(xx=tt, yy=miscon) {
  pos <- vector(mode = "numeric")
  for(i in 1:dim(yy)[1]) {
    pos[i] <- DescTools::Closest(xx$dep.time, yy$next.pos.dep[i])
    #print(pos[i])
    yy$new.flight[i] <- pos[i]
  }
  out <- yy
  return(out)
}

get_close2()

Для этого я пробовал только с одним условием. Он сгенерировал столбец, но только с NA. Очевидно, я сейчас далеко, поэтому обращаюсь за помощью. Надеюсь, что проблема была ясна. Конечный результат предпочтительно будет выглядеть примерно так:

miscon
        flight.date    Outbound.airport    arr.time    next.pos.dep    Weekday   new.flight.time
1       2019-12-05     MXP                 19:25:00    19:36:00        4         19:55:00
2       2019-10-03     KRK                 20:52:00    21:17:00        7         NA
3       2019-07-21     KLU                 07:33:00    07:58:00        7         12:20:00
4       2019-05-29     OTP                 18:49:00    19:14:00        3         20:34:00

Ответы [ 2 ]

0 голосов
/ 27 апреля 2020

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

library(data.table)
library(dplyr)
library(tidyr)

tt <- tt %>% separate(weekday, into = as.character(1:7), sep = 1:6) %>% 
  gather(key="key", value="weekday", -c(start.date, end.date, Outbound.airport, dep.time)) %>%
  filter(weekday %in% 1:7) %>%
  select(-key)

Затем я бы сделал левое соединение miscon и tt на Аэропорт и будний день.

tt <- data.table(tt)
miscon <- data.table(miscon)
setkey(miscon, Outbound.airport, weekday)
setkey(tt, Outbound.airport, weekday)
df <- tt[miscon]

Проверьте, чтобы дата рейса была действительной:

df = df[flight.date>=start.date & flight.date<=end.date]

Теперь у вас есть датафрейм всех возможных соединений. Осталось только найти минимальное время между рейсами для каждого соединения.

df[,timediff:= dep.time-arr.time, by=.(weekday, Outbound.airport)]

Теперь вы можете фильтровать строки по минимальной временной задержке (timediff):

df = df[ , .SD[which.min(timediff)],  by=.(weekday, Outbound.airport, flight.date, arr.time, next.pos.dep)]
setnames(df, "dep.time", "new.flight.time")

> df
   weekday Outbound.airport flight.date            arr.time        next.pos.dep start.date   end.date     new.flight.time   timediff
1:       7              KLU  2019-07-21 2020-04-27 07:33:00 2020-04-27 07:58:00 2019-03-25 2019-10-21 2020-04-27 12:20:00 17220 secs
2:       4              MXP  2019-08-05 2020-04-27 19:25:00 2020-04-27 19:36:00 2019-07-30 2019-08-26 2020-04-27 19:55:00  1800 secs
3:       3              OTP  2019-05-29 2020-04-27 18:49:00 2020-04-27 19:14:00 2019-05-29 2019-06-01 2020-04-27 20:34:00  6300 secs

Решение представляет собой смесь из dplyr и data.table.

0 голосов
/ 27 апреля 2020

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

# setup
library(data.table)
setDT(tt)
setDT(miscon)

# make tt long format splitting weekdays out
tt <- melt(tt[, paste("V", 1:7, sep = "") := tstrsplit(weekday, "")][, -"weekday"], measure.vars = paste("V", 1:7, sep = ""))[value != "."][, c("weekday", "value", "variable") := .(value, NULL, NULL)]

# join, calculate time difference, convert format of times, rank on new.dep.time within group, and filter
newDT <- miscon[tt, on = c("Outbound.airport", "weekday"), nomatch = 0][
  , new.dep.time := as.numeric(dep.time - arr.time)][
  , c("arr.time", "dep.time", "next.pos.dep") := .(format(arr.time, "%H:%M"), format(dep.time, "%H:%M"), format(next.pos.dep, "%H:%M"))][
  , new.dep.rank := rank(new.dep.time), by = c("Outbound.airport", "weekday")][
  new.dep.rank == 1, -c("new.dep.rank", "new.dep.time")]
...