为何R语言unique()函数无法处理lm()对象提取的拟合值?
这是个典型的浮点数精度坑!我来给你拆解清楚问题根源和解决办法:
先复现你遇到的异常场景
首先用你的测试数据和模型拟合来重现问题:
# 你的测试数据集 test_data <- data.frame( x = c(rep(1.00050239485720394857,4), 1.00050239485720394854, rep(2.0002230948570293845,5), rep(3.0005903847502398475,5)), y = c(rep(4.00423409872345,5), rep(2.034532039485722,5), rep(1.1234152304957,5)) ) # 拟合线性模型 model <- lm(y ~ x, data = test_data) # 尝试获取拟合值的唯一值,会得到远超预期的结果 unique(fitted(model))
问题根源:浮点数的精度误差
为什么直接处理数据框时unique()正常,处理拟合值就出问题?
- 你输入的原始数据虽然是高精度小数,但R存储时会按浮点数规则做近似,这些近似后的数值刚好被
unique()识别为相等的分组。 - 但拟合
lm()/aov()时,模型会进行一系列浮点运算(系数计算、矩阵乘法等),这些运算会引入极其微小的精度偏差——两个拟合值可能只在小数点后10多位有差异,但unique()是严格基于二进制存储值判断相等的,所以会把这些几乎一样的值当成不同的个体。
你可以用print(fitted(model), digits = 15)查看高精度的拟合值,就能看到那些肉眼几乎不可见的差异了。
解决办法:基于容忍度的近似去重
我们需要把“严格相等”改成“近似相等”,常用两种方法:
方法1:用dplyr的便捷函数(推荐)
dplyr::n_distinct()支持设置容忍度,或者直接对拟合值做四舍五入后去重:
library(dplyr) # 统计去重后的数量(设置容忍度为1e-8) n_distinct(fitted(model), tolerance = 1e-8) # 获取去重后的具体数值 unique_fitted <- unique(round(fitted(model), digits = 8))
方法2:自定义容忍度去重函数
如果你不想加载额外包,可以自己写一个基于差值判断的去重函数:
# 自定义函数:按容忍度判断近似相等 unique_tol <- function(x, tol = 1e-8) { x_sorted <- sort(x) # 计算相邻值的差值,保留差值大于容忍度的位置 diffs <- diff(x_sorted) keep_positions <- c(TRUE, diffs > tol) x_sorted[keep_positions] } # 应用到拟合值 unique_tol(fitted(model))
内容的提问来源于stack exchange,提问作者Jerome Dineen
相关产品推荐
相关产品推荐

