【问题标题】:Match values with multiple conditions using two data.frames使用两个 data.frames 匹配具有多个条件的值
【发布时间】:2020-08-09 21:50:31
【问题描述】:

我是 R 的新手,需要一些帮助。 我有两个具有相当相似信息的数据框。第一个数据框包含有关航空公司错误连接的信息,而另一个数据框是同一航空公司的整个时间表。现在,我需要在错误连接 data.frame 中创建一个新列,包括时间表中的航班,这些航班可以替换中转延误的航班。

我要替换的航班需要满足一系列条件(在一定的时间范围内,需要是同一工作日,并且需要飞往同一目的地)。另外,我希望 R 选择最接近(按时间)在中转的新到达时间的航班(来自错误连接的 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

时间表 data.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。确实尝试使用 R 中的 match 和 mutate 函数,但似乎我匹配的值必须相等 - 我这样做了不要指望我的。

还使用 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

【问题讨论】:

  • 我对@9​​87654325@ 的列感到困惑。这些信息是否相关?
  • 感谢您的提问。到了最后,但现在这无关紧要。原因是下一次可能的出发时间需要比实际到达中转站晚 25 分钟(我刚刚编辑了问题,现在它说的是 arr.time,而不是 dep.time - 更有意义)。然后,下一个航班将是下一次可能起飞的最短时间。
  • 请提供代码以制作最小可重现的问题示例
  • 抱歉 @rg255 ,我现在在问题描述中提供了它。
  • ..3.5.. 是否意味着航班在一周的第三天和第五天,即周三和周五?

标签: r dataframe merge match closest


【解决方案1】:

好的,这不是很漂亮,但是您遇到了一个相当复杂的问题,我并不清楚这是否可以满足您的需求 - 您需要在比您提供的小示例更大的数据集上检查它首先确定。

# 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")]

【讨论】:

    【解决方案2】:

    我认为您可以按照以下方式进行操作。首先,我将重新排列 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)
    

    然后我会在机场和工作日进行miscontt 的左连接。

    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]
    

    现在您有了一个包含所有可能连接的 data.frame。剩下的唯一事情就是找到每个连接的航班之间的最短时间。

    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
    

    解决方案是dplyrdata.table 的混合。

    【讨论】:

    • 这很好用,谢谢!!实际上,我用滚动连接做了一些类似的事情,但在工作日遇到了困难,所以这个 def 有帮助!但是,我仍然很难弄清楚如何确保新航班在正确的时间段内。 “tt$start.date”和“tt$end.date”表示特定航班时刻表的时间段,new.flight.time 需要在该时间段内。我尝试使用 将其添加到 setkey() 函数中,但没有成功。
    • 我编辑了答案,以便检查航班日期是否在时间表范围内。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-14
    • 1970-01-01
    • 2016-07-19
    • 2015-12-08
    • 2015-02-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多