如何手动实现R中基于范围而非精确匹配的数据框连接?
手动实现区间匹配的数据集连接
数据集与需求说明
现有两个数据集:
dfA <- tibble( name = c("John", "Michael", "Brian", "Thomas", "Peter"), expected = c(128.34, 453.6674, 789.2345, 678.34, 475.467), max = c(128.3464, 453.6901, 789.2740, 678.3739, 475.4908), min = c(128.3336, 453.6447, 789.1950, 678.3061, 475.4432) ) dfB <- tibble( real = c(128.76, 453.658, 789.235, 677.2035, 475.4695), time = c(3.24, 5.65, 4.89, 2.28, 6.12) )
其中dfA$max和dfA$min由以下函数计算生成:
shift <- function (theoretical, tolerance) { return(tolerance*theoretical/10^6) } calculate_max <- function (vector, tolerance) { res <- c() for (i in vector) { max <- i + shift(i, tolerance) res <- append(res, max) } return(res) } calculate_min <- function (vector, tolerance) { res <- c() for (i in vector) { min <- i - shift(i, tolerance) res <- append(res, min) } return(res) }
需求是将两个数据集连接,仅保留dfB$real值落在dfA$min与dfA$max区间内的组合。已通过fuzzyjoin包实现该需求,参考代码如下:
fuzzy_right_join(dfB, dfA, by = c("real"="max", "real"="min"), match_fun = list(`<=`, `>=`))
以下是几种无需逐行遍历的手动实现方法:
方法1:交叉连接后过滤(dplyr)
通过cross_join生成两个数据集的所有行组合,再筛选满足区间条件的记录,逻辑直观,适合数据量较小的场景:
library(dplyr) cross_join(dfB, dfA) %>% filter(real >= min, real <= max)
方法2:purrr向量化匹配
对dfB的每个real值,匹配dfA中符合区间条件的行,最后拼接结果,兼顾可读性与效率:
library(purrr) library(dplyr) map_dfr(dfB$real, ~ dfA %>% filter(min <= .x, .x <= max)) %>% bind_cols(dfB, .)
方法3:矩阵运算实现高效匹配
利用R的矩阵广播特性生成匹配矩阵,直接定位符合条件的行索引,性能最优,适合大数据量场景:
# 生成匹配矩阵:每行对应dfB的real,每列对应dfA的行,TRUE表示匹配成功 match_matrix <- outer(dfB$real, dfA$min, `>=`) & outer(dfB$real, dfA$max, `<=`) # 获取匹配的行索引(row对应dfB,col对应dfA) matches <- which(match_matrix, arr.ind = TRUE) # 根据索引拼接最终结果 result <- cbind(dfB[matches[, "row"], ], dfA[matches[, "col"], ])
以上方法均采用向量化操作,避免了逐行遍历的低效问题,可根据数据集规模选择合适的实现方式。
内容的提问来源于stack exchange,提问作者jpm92
相关产品推荐
相关产品推荐

