【问题标题】:Is value within a range值是否在一个范围内
【发布时间】:2018-06-13 05:26:55
【问题描述】:

我有两个要比较的数据框。

instances <- data.frame(id = c("AED","AED","CFR","DRR","DRR","DRR","UN","PO"),
         dates = as.POSIXct(c("2018-05-17 09:52:00","2018-05-17 10:49:00","2018-05-17 10:38:00","2018-05-17 11:29:00","2018-05-17 12:12:00","2018-05-17 13:20:00","2018-05-17 14:28:00","2018-05-17 15:59:00")))

ranges <- data.frame(id = c("AED","CFR","DRR","DRR","UN"),
             start = as.POSIXct(c("2018-05-17 10:00:00","2018-05-17 10:18:00","2018-05-17 11:18:00","2018-05-17 13:10:00","2018-05-17 14:18:00")),
             end = as.POSIXct(c("2018-05-17 11:56:00","2018-05-17 12:23:00","2018-05-17 12:01:00","2018-05-17 14:18:00",NA)))

通过 id,我想将实例数据框中的每个日期与范围数据框中列出的相应日期范围进行比较。如果范围数据帧中没有匹配的 id,那么它应该返回 FALSE,如果 range$end 是 NA,它也应该返回 FALSE。结果应该如下:

result <- data.frame(id = c("AED","AED","CFR","DRR","DRR","DRR","UN","PO"),
             dates = c("2018-05-17 09:52:00","2018-05-17 10:49:00","2018-05-17 10:38:00","2018-05-17 11:29:00","2018-05-17 12:12:00","2018-05-17 13:20:00","2018-05-17 14:28:00","2018-05-17 15:59:00"),
             inRange = c(FALSE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, FALSE),
             outsideRange = c(TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE))

【问题讨论】:

  • 我认为大卫在这里的回答应该接近涵盖这个 - stackoverflow.com/a/41132376/496803 类似:instances[ranges, on=c("id","dates&gt;=start","dates&lt;=end"), hit := 1]
  • ranges 中有两个带有“DRR”的id。你怎么知道什么时候选择哪一个?
  • 为什么你需要一个 inRange 和 outsideRange 变量,为什么你想要的结果中的所有行都不相反?

标签: r dataframe range


【解决方案1】:
library(dplyr)

instances %>% 
  full_join(ranges) %>% 
  mutate(inRange = case_when(dates >= start & dates <= end ~ T, T ~ F))

    id               dates               start                 end inRange
1  AED 2018-05-17 09:52:00 2018-05-17 10:00:00 2018-05-17 11:56:00 FALSE
2  AED 2018-05-17 10:49:00 2018-05-17 10:00:00 2018-05-17 11:56:00  TRUE
3  CFR 2018-05-17 10:38:00 2018-05-17 10:18:00 2018-05-17 12:23:00  TRUE
4  DRR 2018-05-17 11:29:00 2018-05-17 11:18:00 2018-05-17 12:01:00  TRUE
5  DRR 2018-05-17 11:29:00 2018-05-17 13:10:00 2018-05-17 14:18:00 FALSE
6  DRR 2018-05-17 12:12:00 2018-05-17 11:18:00 2018-05-17 12:01:00 FALSE
7  DRR 2018-05-17 12:12:00 2018-05-17 13:10:00 2018-05-17 14:18:00 FALSE
8  DRR 2018-05-17 13:20:00 2018-05-17 11:18:00 2018-05-17 12:01:00 FALSE
9  DRR 2018-05-17 13:20:00 2018-05-17 13:10:00 2018-05-17 14:18:00  TRUE
10  UN 2018-05-17 14:28:00 2018-05-17 14:18:00                <NA> FALSE
11  PO 2018-05-17 15:59:00                <NA>                <NA> FALSE

【讨论】:

  • 这与预期的输出不同。
  • 抱歉。我已经稍微改变了我的答案,但我同意你的评论这个问题有点模棱两可。
  • 结果数据框应该与instances数据框有相同的行,即8。你有11行。
  • 确实如此。如果我们知道应该合并范围 df 中的 2 个“DRR”行中的哪一个,就可以解决这个问题。
【解决方案2】:

data.table 解决方案

我会使用 data.table 中的 foverlaps() 函数来解决这个问题...唯一的问题是它只接受完整的日期范围,并且在提供的样本数据中范围 [,5] 没有结束日期。 ..

> ranges
   id               start                 end
1 AED 2018-05-17 10:00:00 2018-05-17 11:56:00
2 CFR 2018-05-17 10:18:00 2018-05-17 12:23:00
3 DRR 2018-05-17 11:18:00 2018-05-17 12:01:00
4 DRR 2018-05-17 13:10:00 2018-05-17 14:18:00
5  UN 2018-05-17 14:18:00                <NA>

为了得到以下单词的解决方案,所有范围都必须有一个开始和一个结束。 所以,让我们使用一些弥补时间戳来填写 NA。

ranges <- data.frame(id = c("AED","CFR","DRR","DRR","UN"),
                     start = as.POSIXct(c("2018-05-17 10:00:00","2018-05-17 10:18:00","2018-05-17 11:18:00","2018-05-17 13:10:00","2018-05-17 14:18:00")),
                     end = as.POSIXct(c("2018-05-17 11:56:00","2018-05-17 12:23:00","2018-05-17 12:01:00","2018-05-17 14:18:00", "2018-05-17 16:18:00")))

> ranges
   id               start                 end
1 AED 2018-05-17 10:00:00 2018-05-17 11:56:00
2 CFR 2018-05-17 10:18:00 2018-05-17 12:23:00
3 DRR 2018-05-17 11:18:00 2018-05-17 12:01:00
4 DRR 2018-05-17 13:10:00 2018-05-17 14:18:00
5  UN 2018-05-17 14:18:00 2018-05-17 16:18:00

工作流程

library(data.table)
#make instances a data.table without key
instances.dt <- setDT( instances, key = NULL )
#create a data.table with the ranges, set keys 
ranges.dt <- setDT( ranges, key = c("id", "start", "end") )

#create a temporary 'range', where start == end, based on the dates-column
instances.dt[, c( "start", "end") := dates]

#create a column 'inRange' using data.table's foverlaps(). 
#use the secons column of the fovelaps' result. If  this column is NA, then no 'hit' was found 
#in ranges.dt and inrange == FALSE, else inRange == TRUE
instances.dt[, inRange := !is.na( foverlaps(instances.dt, ranges.dt, type = "within", mult = "first", nomatch = NA)[,2] )]

#outsideRange is the opposite of inRange
instances.dt[, outsideRange := !inRange]

#remove the temporary columns 'start' and 'end'
instances.dt[, c("start", "end") := NULL]

结果

> instances.dt
    id               dates inRange outsideRange
1: AED 2018-05-17 09:52:00   FALSE         TRUE
2: AED 2018-05-17 10:49:00    TRUE        FALSE
3: CFR 2018-05-17 10:38:00    TRUE        FALSE
4: DRR 2018-05-17 11:29:00    TRUE        FALSE
5: DRR 2018-05-17 12:12:00   FALSE         TRUE
6: DRR 2018-05-17 13:20:00    TRUE        FALSE
7:  UN 2018-05-17 14:28:00    TRUE        FALSE
8:  PO 2018-05-17 15:59:00   FALSE         TRUE

即使对于巨大的 data.tables,它的运行速度也非常快。

你可以缩短代码,但我总是喜欢一步一步分析,提高可读性。

使用 magrittr 的管道运算符链接

library(data.table)
library(magrittr)

ranges.dt <- setDT( ranges, key = c("id", "start", "end") )
result <- setDT( instances, key = NULL ) %>% 
  .[, c( "start", "end") := dates] %>%
  .[, inRange := !is.na( foverlaps( ., ranges.dt, type = "within", mult = "first", nomatch = NA )[,2] )] %>%
  .[, outsideRange := !inRange] %>%
  .[, c("start", "end") := NULL]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    • 1970-01-01
    相关资源
    最近更新 更多