如何在Julia DataFrames包及R中实现close join操作?
在Julia DataFrames包和R语言中实现closejoin操作
示例数据集
先定义原问题中的示例数据集:
Julia InMemorydatasets 格式
using InMemorydatasets classA = Dataset(id = ["id1", "id2", "id3", "id4", "id5"], mark = [50, 69.5, 45.5, 88.0, 98.5]); grades = Dataset(mark = [0, 49.5, 59.5, 69.5, 79.5, 89.5, 95.5], grade = ["F", "P", "C", "B", "A-", "A", "A+"]);
InMemorydatasets 中可直接用closejoin实现按mark列的最近匹配关联,包括默认的双向最近匹配,以及指定direction=:forward的向前匹配(取不小于当前值的最近值)。
一、Julia DataFrames 包实现
DataFrames 包没有内置closejoin函数,但可通过自定义函数结合排序、查找操作实现相同逻辑:
1. 准备数据
using DataFrames classA = DataFrame(id = ["id1", "id2", "id3", "id4", "id5"], mark = [50, 69.5, 45.5, 88.0, 98.5]) grades = DataFrame(mark = [0, 49.5, 59.5, 69.5, 79.5, 89.5, 95.5], grade = ["F", "P", "C", "B", "A-", "A", "A+"]) # 确保grades的mark列有序(关联逻辑依赖有序列) sort!(grades, :mark)
2. 实现默认双向最近匹配(对应closejoin(classA, grades, on=:mark))
function get_nearest_grade(mark_val, grades_df) marks = grades_df.mark idx = searchsortedfirst(marks, mark_val) # 处理边界情况:小于最小值取第一个,大于最大值取最后一个 if idx == 1 return grades_df.grade[1] elseif idx > length(marks) return grades_df.grade[end] else # 比较前后两个值的距离,取更近的那个 prev_dist = mark_val - marks[idx-1] next_dist = marks[idx] - mark_val return prev_dist <= next_dist ? grades_df.grade[idx-1] : grades_df.grade[idx] end end # 给classA添加匹配后的grade列 classA.grade = [get_nearest_grade(m, grades) for m in classA.mark]
3. 实现向前匹配(对应closejoin(..., direction=:forward, border=:nearest))
向前匹配指取不小于当前值的最近匹配值,边界超出时取最后一个值:
function get_forward_grade(mark_val, grades_df) marks = grades_df.mark idx = searchsortedfirst(marks, mark_val) # 边界处理:超出最大值时取最后一个元素 idx = idx > length(marks) ? length(marks) : idx return grades_df.grade[idx] end classA.grade_forward = [get_forward_grade(m, grades) for m in classA.mark]
二、R语言实现
R中可用data.table包高效实现,也可用dplyr结合映射操作实现:
1. 准备数据
classA <- data.frame(id = c("id1", "id2", "id3", "id4", "id5"), mark = c(50, 69.5, 45.5, 88.0, 98.5)) grades <- data.frame(mark = c(0, 49.5, 59.5, 69.5, 79.5, 89.5, 95.5), grade = c("F", "P", "C", "B", "A-", "A", "A+"))
2. 用data.table实现高效匹配
data.table的roll参数可直接实现类似逻辑:
library(data.table) # 转换为data.table并设置键 setDT(classA) setDT(grades) setkey(grades, mark) # 默认双向最近匹配 classA[, grade := grades[classA, on = "mark", roll = "nearest"]$grade] # 向前匹配(对应direction=:forward, border=:nearest) classA[, grade_forward := grades[classA, on = "mark", roll = Inf]$grade]
3. 用dplyr+purrr实现(适合熟悉tidyverse的用户)
library(dplyr) library(purrr) # 先对grades按mark排序 grades <- grades %>% arrange(mark) # 双向最近匹配 classA <- classA %>% mutate(grade = map_chr(mark, function(m) { # 计算当前mark与grades中所有mark的距离,取最小值对应的grade dists <- abs(m - grades$mark) grades$grade[which.min(dists)] })) # 向前匹配 classA <- classA %>% mutate(grade_forward = map_chr(mark, function(m) { # 找到第一个不小于当前mark的位置 idx <- findInterval(m, grades$mark) + 1 # 边界处理:超出最大值时取最后一个 idx <- ifelse(idx > nrow(grades), nrow(grades), idx) grades$grade[idx] }))
内容的提问来源于stack exchange,提问作者Warwick Wang
相关产品推荐
相关产品推荐

