R语言中基于最短欧氏距离匹配填充列NA值的问题求解
问题原因分析
- 问题1的根源:dplyr的
mutate()默认执行向量化运算,你代码中的a、b会被识别为整个列向量,而非当前NA行对应的单个a_i、b_i值,fillgroup$a - a实际是整列做广播减法而非逐行计算与当前行的距离,最终所有行的计算结果完全一致,所以which.min始终返回1。 - 问题2的根源:计算距离时没有过滤当前行本身,即使距离计算逻辑正确,最小距离永远是0(自身和自身的距离),会直接取当前行的NA值,导致填充失效。
修正后的实现代码
tidyverse 版本(适配dplyr语法)
首先我们先给示例数据补充NA值方便测试,再运行修正代码:
library(tidyverse) # 加载示例数据,修改第3、5行c值为NA模拟待填充场景 fillgroup <- structure(list(c = c(0.00131, 0.02349, NA, 0.00342, NA, 0.00203), a = c(1763L, 487L, 1560L, 1088L, 1519L, 1803L), b = c(685L, 1456L, 289L, 861L, 293L, 736L)), class = c("data.table", "data.frame")) # 填充逻辑实现 fillgroup_filled <- fillgroup %>% # 新增行号用于排除当前行自身 mutate(rowid = row_number()) %>% # 按行分组,保证每次运算取当前行的a、b、行号值 rowwise() %>% mutate( match = ifelse( is.na(c), { # 计算所有行和当前行的平方欧氏距离 dist_vec <- (fillgroup$a - a)^2 + (fillgroup$b - b)^2 # 当前行的距离设为无穷大,排除自身匹配 dist_vec[rowid] <- Inf which.min(dist_vec) }, NA_integer_ ), # 用匹配到的行的c值填充NA c = ifelse(is.na(c), fillgroup$c[match], c) ) %>% # 取消行分组,删除临时列 ungroup() %>% select(-rowid, -match)
data.table 版本(大数据量下效率更高)
你的原始数据是data.table类,量大时用原生data.table语法性能远高于rowwise逐行运算:
library(data.table) setDT(fillgroup) # 新增行号 fillgroup[, rowid := .I] # 仅对c为NA的行逐行处理填充 fillgroup[is.na(c), c := { cur_a <- a cur_b <- b cur_id <- rowid dist_vec <- (fillgroup$a - cur_a)^2 + (fillgroup$b - cur_b)^2 dist_vec[cur_id] <- Inf fillgroup$c[which.min(dist_vec)] }, by = rowid] # 删除临时行号列 fillgroup[, rowid := NULL]
内容的提问来源于stack exchange,提问作者Tammy Tan
相关产品推荐
相关产品推荐

