【问题标题】:Filtering df on multiple columns from the same row in another table?过滤另一个表中同一行的多列上的df?
【发布时间】:2020-03-23 15:50:49
【问题描述】:

我有一个包含事件开始和结束时间的数据集(称为 df_time),以及另一个包含事件发生时间的数据集(df_val)。我想使用 df_time 将 df_val 过滤到仅在记录的时间间隔内发生的事件。

不过,我对如何实现这一点有点迷茫。

start = c(1, 5, 7, 4)
end = c(2, 7, 11, 7)
df_time = data.frame(start, end)

time = c(3, 6, 2, 10, 11)
val = c(100, 20, 30, 40, 50)
df_val = data.frame(time, val)

df_val %>% select_all() %>%
  filter(time >= df_time$start & time <= df_time$end)

输出:

  time val
1    6  20
Warning messages:
1: In time >= df_time$start :
  longer object length is not a multiple of shorter object length
2: In time <= df_time$end :
  longer object length is not a multiple of shorter object length

上面将运行警告消息(上),并给我错误的输出(忽略等于值时间戳的开始/结束)。上面,应该打印除 3 之外的所有值。

我不确定如何解决这个问题,如果有任何帮助/资源,我将不胜感激!

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    这是您想要完成的任务吗?

    library(tidyverse)
    start = c(1, 5, 7, 4)
    end = c(2, 7, 11, 7)
    df_time = data.frame(start, end)
    
    time = c(3, 6, 2, 10, 11)
    val = c(100, 20, 30, 40, 50)
    df_val = data.frame(time, val)
    
    # return one row for each start/end pair that time falls between
    map2_dfr(start, end, ~filter(df_val, time >= .x, time <= .y) %>% mutate(start = .x, end = .y))
    #>   time val start end
    #> 1    2  30     1   2
    #> 2    6  20     5   7
    #> 3   10  40     7  11
    #> 4   11  50     7  11
    #> 5    6  20     4   7
    
    #return unique pairs
    map2_dfr(start, end, ~filter(df_val, time >= .x, time <= .y)) %>% unique()
    #>   time val
    #> 1    2  30
    #> 2    6  20
    #> 3   10  40
    #> 4   11  50
    
    #simpler method, probably
    df_val %>% filter(map_lgl(time, ~any((.x >= start) & .x <= end)))
    #>   time val
    #> 1    6  20
    #> 2    2  30
    #> 3   10  40
    #> 4   11  50
    

    reprex package (v0.2.1) 于 2019 年 7 月 25 日创建

    编辑:添加了一些替代方案

    【讨论】:

    • 是的!这回答了我的问题,但我很好奇——假设除了过滤到两组时间之间,我还需要匹配一个额外的 ID 列(df_time 和 df_val 都有一个名为 ID 的列)。上面的方法有什么办法可以完成吗?
    • 当然,您也可以像 left_join(df_val, df_time, by = "id") %&gt;% filter(time &gt;= start, time &lt;= end) 这样的操作。如果没有 id,您也可以使用 crossing(df_val, df_time) %&gt;% filter(time &gt;= start, time &lt;= end) 来获得类似于第一个解决方案的内容。不过,如果表格非常大,我可能不会建议 crossing
    【解决方案2】:

    这是使用data.table 的非等内连接的另一个选项:

    library(data.table)
    setDT(df_time)
    setDT(df_val)
    
    df_time[df_val, on=.(ID, start<time, end>time), nomatch=0L, 
        c(mget(paste0("x.", names(df_time))), mget(paste0("i.", names(df_val))))]
    

    输出:

       x.ID x.start x.end i.ID i.time i.val
    1:    1       5     7    1      6    20
    2:    1       4     7    1      6    20
    3:    1       7    11    1     10    40
    

    【讨论】:

      猜你喜欢
      • 2021-09-07
      • 1970-01-01
      • 2016-03-22
      • 2019-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-09
      相关资源
      最近更新 更多