如何在基于多个列的查找data.frame中查找最接近的行?

时间:2019-09-10 18:20:56

标签: r dataframe

假设我有以下具有数据(v)和查找数据帧(l)的数据框:

v <- data.frame(d = c(as.Date('2019-01-01'), as.Date('2019-01-05'), as.Date('2019-01-30'), as.Date('2019-02-02')), kind=c('a', 'b', 'c', 'a'), v1=c(1,2,3,4))
v
           d kind v1
1 2019-01-01    a  1
2 2019-01-05    b  2
3 2019-01-30    c  3
4 2019-02-02    a  4

l <- data.frame(d = c(as.Date('2019-01-01'), as.Date('2019-01-04'), as.Date('2019-02-01')), kind=c('a','b','a'), l1=c(10,20,30))
l
           d kind l1
1 2019-01-01    a 10
2 2019-01-04    b 20
3 2019-02-01    a 30

我想使用l列在c("d", "kind")数据框中找到与v中的每一行相对应的最接近的行。列kind是否需要完全匹配,并且可能在findInterval(...)上使用d

我希望我的结果是:

           d kind v1 l1
1 2019-01-01    a  1 10
2 2019-01-05    b  2 20
3 2019-01-30    c  3 NA
4 2019-02-02    a  4 30
  

注意:我更喜欢base-R的实现,但是   见到别人很有趣

我尝试过findInterval(...),但不知道如何使它与多列配合工作

2 个答案:

答案 0 :(得分:2)

这里仅是base-R中的镜头。 (我相信data.table会更优雅地完成此操作,但我感谢您不愿引入其他软件包。)

通过kind将每个帧分割成帧列表:

v_spl <- split(v, v$kind)
l_spl <- split(l, l$kind)
str(v_spl)
# List of 3
#  $ a:'data.frame':    2 obs. of  3 variables:
#   ..$ d   : Date[1:2], format: "2019-01-01" "2019-02-02"
#   ..$ kind: Factor w/ 3 levels "a","b","c": 1 1
#   ..$ v1  : num [1:2] 1 4
#  $ b:'data.frame':    1 obs. of  3 variables:
#   ..$ d   : Date[1:1], format: "2019-01-05"
#   ..$ kind: Factor w/ 3 levels "a","b","c": 2
#   ..$ v1  : num 2
#  $ c:'data.frame':    1 obs. of  3 variables:
#   ..$ d   : Date[1:1], format: "2019-01-30"
#   ..$ kind: Factor w/ 3 levels "a","b","c": 3
#   ..$ v1  : num 3

现在,我们确定两者之间共有的唯一kind,无需尝试加入所有内容:

### this has the 'kind' in common
(nms <- intersect(names(v_spl), names(l_spl)))
# [1] "a" "b"

### this has the 'kind' we have to bring back in later
(miss_nms <- setdiff(names(v_spl), nms))
# [1] "c"

对于不常见的kind,请进行间隔连接:

joined <- Map(
  v_spl[nms], l_spl[nms],
  f = function(v0, l0) {
    ind <- findInterval(v0$d, l0$d)
    ind[ ind < 1 ] <- NA
    v0$l1 <- l0$l1[ind]
    v0
  })

最终,我们会将rbind的内容放回原处,但是miss_nms中的内容将没有新的列。这是一种使用适当的NA值来精确捕获新列的一行的通用方法:

emptycols <- joined[[1]][, setdiff(colnames(joined[[1]]), colnames(v)),drop=FALSE][1,,drop=FALSE][NA,,drop=FALSE]
emptycols
#    l1
# NA NA

并将该列添加到尚未找到的框架中:

unjoined <- lapply(v_spl[miss_nms], cbind, emptycols)
unjoined
# $c
#            d kind v1 l1
# 3 2019-01-30    c  3 NA

最后将所有内容重新整合到一个框架中:

do.call(rbind, c(joined, unjoined))
#              d kind v1 l1
# a.1 2019-01-01    a  1 10
# a.4 2019-02-02    a  4 30
# b   2019-01-05    b  2 20
# c   2019-01-30    c  3 NA

答案 1 :(得分:0)

如果您想要完全匹配,则可以:

 vl <- merge(v, l, by = c("d","kind"))

出于您的目的,您可以将d转换为年,月或日的其他变量,并使用 merge