【问题标题】:R nested for loops returns unexpected resultsR嵌套for循环返回意外结果
【发布时间】:2021-02-20 15:17:01
【问题描述】:

背景 我在 R 中使用嵌套循环从移动应用程序中排除用户的重叠会话数据。由于我无法共享数据,我正在使用 nycflights13 包中的 flights 数据框,我也设法在其中复制了问题。

目标:排除所有从同一目的地出发并在时间上重叠(同时在空中)的航班。在重叠航班中,始终选择航班号最高的航班(在原始数据中是有意义的)。

问题:我最终只有 12 次航班,而不是预期的数千次。除了痛苦的低效率,你能看出问题在哪里吗?

重现问题所需的软件包:tidyverse,[nycflights13][1]

我们的“解决方案”:

# data (flights from package nycflights13)
df_flights <- flights

df_flights2 <- df_flights %>% 
  drop_na() %>% 
  filter(carrier == "MQ") %>% # to decrease running time, just a piece of the data
  mutate(
    unique_n_day = as.factor(as.numeric(date(time_hour))), # creating unique number for a day to loop over
    dest = as.factor(dest),
    air_time = air_time*60) # converting to seconds
    

flight_list <- list()

## loop

for (i in levels(df_flights2$dest)){
  df_dest <- df_flights2[df_flights2$dest == i,]
  
  for (d in levels(df_dest$unique_n_day)){
    df_day <- df_dest[df_dest$unique_n_day == d,]
    
    for(n in 1:nrow(df_day)){
      
      df <- df_day[df_day$time_hour >= df_day$time_hour[n] & 
                 df_day$time_hour <= df_day$time_hour[n] +  df_day$air_time[n],]
      
      if (nrow(df) >= 1){
        flight_list[[n]] <- df[which(df$flight==max(df$flight))[1],]
        
      }else(d <- "I know it is silly") # max() complains when the df is empty-> if statement 
    }  
  }
}
# unlisting
fulldata_flight <- do.call("rbind", flight_list)

# dropping duplicated values 
fulldata_flight_clean <- distinct(fulldata_flight)

编辑: 如果寻找返回重叠间隔的函数,那么我建议使用 data.table::foverlaps() 函数,因为下面的解决方案不能完美地捕捉所有内容。 [1]:https://github.com/hadley/nycflights13

【问题讨论】:

  • 您在unique_n_day 行中使用lubridate::date 吗?
  • 我对“排除所有从同一目的地出发并在时间上重叠的航班(同时在空中)中的措辞感到困惑。在重叠的航班中,始终选择一个具有最高的航班号(在原始数据中是有道理的)。”是否要包括除时间和目的地重叠且没有最高航班号的航班之外的所有航班?
  • 如果航班 A 与航班 B 重叠,航班 B 与航班 C 重叠,但航班 C 不与航班 A 重叠,您会查看哪些航班号来选择最高的航班号?
  • 我不确定你想要什么的具体细节,但我预计与 data.table 的非 equi 连接将比这种方法快 10-100 倍。
  • 对于您的用例来说,这似乎忽略了在不同日期开始的重叠会话是否不重要?如果会议时间很短,我可以想象这可以忽略不计。

标签: r nested-loops


【解决方案1】:

您在代码的第 29 行重用 n:

flight_list[[n]] <- df[which(df$flight==max(df$flight))[1],]

但是,n 每天迭代航班和目的地,恰好是最多 14 个(第 23 行):

for(n in 1:nrow(df_day))

这意味着结果被覆盖。 只需将最终列表迭代器设置在第一个循环之外,并在添加到列表后手动推进它。

此外,通过首先识别所有有效的目的地和日期组合,您可以摆脱一个 for 循环。请参阅下面的完整代码。

library(tidyverse)
library(nycflights13)
library(lubridate)
# data (flights from package nycflights13)
df_flights <- flights

df_flights2 <- df_flights %>% 
  drop_na() %>% 
  filter(carrier == "MQ") %>% # to decrease running time, just a piece of the data
  mutate(
    unique_n_day = as.factor(as.numeric(date(time_hour))), # creating unique number for a day to loop over
    dest = as.factor(dest),
    air_time = air_time*60) # converting to seconds


flight_list <- list()
list_id <- 1 # Results list iterator

# Get all valid destination and day combinations
dest_day_comb <- df_flights2 %>%
  group_by(dest, unique_n_day) %>%
  count() %>%
  ungroup()

## loop
for (i in seq(nrow(dest_day_comb))) {
  current_comb <- dest_day_comb[i,]
  df_day <- df_flights2 %>%
    filter(dest == current_comb$dest,
           unique_n_day == current_comb$unique_n_day)
  # Since we are only iterating over valid combinations, there is no need to check if df_day is > 0.
  for(n in 1:nrow(df_day)){
    
    df <- df_day[df_day$time_hour >= df_day$time_hour[n] & 
                 df_day$time_hour <= df_day$time_hour[n] +  df_day$air_time[n],]
    
    flight_list[[list_id]] <- df[which(df$flight==max(df$flight))[1],] # Using the list_id to add to the results list.
    # Info: This creates duplicates since valid solutions are added nrow(df) times.
    # However, these duplicates are removed later on.
    list_id <- list_id + 1
  }  
}

# unlisting
fulldata_flight <- do.call("rbind", flight_list)

# dropping duplicated values 
fulldata_flight_clean <- distinct(fulldata_flight)

【讨论】:

  • 这正是我想要的:更整洁、更快、更有效,谢谢。关于我们实现中的错误的问题得到了回答,但是,在进一步的探索中,我注意到如何定义重叠会话的方式过于包容。我最终没有比较时间,而是使用了 data.table 中的 foverlaps 函数。鉴于循环运行,我应该更新您的解决方案还是保留它?
  • 答案应该与问题相符,以便其他人无需通过 cmets 即可理解。因此,要么更新问题和答案(这在某种程度上是无用的,因为主要问题是关于循环中的错误),要么保持原样。也许您可以对有关 foverlaps 解决方案的问题添加评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多