找到第二个数据帧中每个元素的两个数据帧之间的最小距离

时间:2014-12-14 04:12:51

标签: r plyr dplyr

我有两个数据框ev1和ev2,描述了在许多测试中收集的两种类型事件的时间戳。因此,每个数据帧都有“test_id”和“timestamp”列。我需要找到的是在同一测试中每个ev2的最小距离ev1。

我有一个合并两个数据集的工作代码,计算距离,然后使用dplyr过滤最小距离:

ev1 = data.frame(test_id = c(0, 0, 0, 1, 1, 1), time=c(1, 2, 3, 2, 3, 4))
ev2 = data.frame(test_id = c(0, 0, 0, 1, 1, 1), time=c(6, 1, 8, 4, 5, 11))

data <- merge(ev2, ev1, by=c("test_id"), suffixes=c(".ev2", ".ev1"))

data$distance <- data$time.ev2 - data$time.ev1

min_data <- data %>%
  group_by(test_id, time.ev2) %>%
  filter(abs(distance) == min(abs(distance)))

虽然这很有效,但合并部分非常慢并且感觉效率低下 - 我正在为同一个test_id生成一个包含ev2-&gt; ev1的所有组合的巨大表格,只是将其过滤为一个。在合并期间,似乎应该有一种“即时过滤”的方法。在那儿?

更新:当使用akrun概述的data.table方法时,以下两个“group by”列的情况会失败:

ev1 = data.frame(test_id = c(0, 0, 0, 1, 1, 1), time=c(1, 2, 3, 2, 3, 4), group_id=c(0, 0, 0, 1, 1, 1))
ev2 = data.frame(test_id = c(0, 0, 0, 1, 1, 1), time=c(5, 6, 7, 1, 2, 8), group_id=c(0, 0, 0, 1, 1, 1))
setkey(setDT(ev1), test_id, group_id)
DT <- ev1[ev2, allow.cartesian=TRUE][,distance:=abs(time-i.time)]

eval中的错误(expr,envir,enclos):找不到对象'i.time'

2 个答案:

答案 0 :(得分:6)

以下是我使用data.table

的方式
require(data.table)
setkey(setDT(ev1), test_id)
ev1[ev2, .(ev2.time = i.time, ev1.time = time[which.min(abs(i.time - time))]), by = .EACHI]
#    test_id ev2.time ev1.time
# 1:       0        6        3
# 2:       0        1        1
# 3:       0        8        3
# 4:       1        4        4
# 5:       1        5        4
# 6:       1       11        4

x[i]中的data.table表单的联接中,前缀i.用于引用ix和{{}时的列1}}为特定列共享相同的名称。

请参阅this SO post以获取有关其工作原理的说明。

这在语法上更容易理解正在发生的事情,并且内存效率高(以低速 1 为代价),因为它根本没有实现整个连接结果。事实上,这正是你在post-过滤器中所说的那样,同时合并

  1. 速度上,在大多数情况下都没关系。如果i中有很多行,那么可能会慢一点,因为i - 表达式必须针对每个行进行评估j中的一行。相比之下,@ akrun的答案是笛卡尔连接,然后进行一次过滤。因此,尽管内存很高,但它并未评估i中每行的j。但同样,除非你使用非常大的 i,否则这种情况甚至不应该发生。
  2. HTH

答案 1 :(得分:0)

可能有帮助:

library(data.table)
setkey(setDT(ev1), test_id)
DT <- ev1[ev2, allow.cartesian=TRUE][,distance:=time-i.time]
DT[DT[,abs(distance)==min(abs(distance)), by=list(test_id, i.time)]$V1]
#    test_id time i.time distance
#1:       0    3      6        3
#2:       0    1      1        0
#3:       0    3      8        5
#4:       1    4      4        0
#5:       1    4      5        1
#6:       1    4     11        7

或者

 ev1[ev2, allow.cartesian=TRUE][,distance:= time-i.time][,
      .SD[abs(distance)==min(abs(distance))], by=list(test_id, i.time)]

更新

使用新分组

setkey(setDT(ev1), test_id, group_id)
setkey(setDT(ev2), test_id, group_id)
DT <- ev1[ev2, allow.cartesian=TRUE][,distance:=i.time-time]
DT[DT[,abs(distance)==min(abs(distance)), by=list(test_id, 
                                group_id,i.time)]$V1]$distance
#[1]  2  3  4 -1  0  4

根据您提供的代码

min_data$distance
#[1]  2  3  4 -1  0  4