You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

dplyr的group_by()未按预期工作:浮点数精度引发分组异常

浮点数精度问题导致dplyr分组统计异常的解决方法

问题描述

在信号检测理论项目中,通过命中率(HR)和误报率(FAR)计算辨别分数(公式:discernment = HR - FAR),使用dplyr的group_by()+summarise()统计各辨别分数的出现次数时,出现视觉上相同的辨别值被分成不同行的异常情况:

  • 例如预期-0.95仅占一行,但实际结果显示两行;
  • 用filter(discernment == -0.95)仅能筛选出其中一行;
  • 将辨别值转为因子后发现,部分看似整数位后两位的数值(如-0.01)实际是-0.00999999999999998,本质是浮点数精度误差导致。

可复现代码

# 最小复现示例
library(tidyverse)

# 生成HR和FAR的所有可能取值(步长0.01)
HR_values <- seq(0, 1, by = 0.01)
FAR_values <- seq(0, 1, by = 0.01)

# 创建所有组合的网格数据框
combinations <- expand.grid(HR = HR_values, FAR = FAR_values)

# 计算辨别分数
combinations <- combinations %>% 
  mutate(discernment = HR - FAR)

# 统计各辨别分数的出现次数
occurences <- combinations %>%
  group_by(discernment) %>%
  summarise(n_occurrences = n()) %>% 
  arrange(discernment)

# 查看前7行,可见疑似重复的辨别值
occurences %>% slice(1:7) 

# 筛选-0.95仅返回一行,验证数值存在细微差异
occurences %>% filter(discernment == -0.95) 

# 转为因子后查看数值细节,确认精度问题
occurences <- occurences %>% 
  mutate(discernment_as_factor = as.factor(discernment))

occurences %>% slice(6:7) 
occurences %>% slice(310:317) 

解决方法

1. 四舍五入到指定小数位

由于HR和FAR都是步长0.01的数值,辨别分数的理论精度最多为两位小数,直接对计算结果四舍五入即可消除误差:

# 方法一:计算辨别分数时直接四舍五入
combinations <- combinations %>% 
  mutate(discernment = round(HR - FAR, digits = 2))

# 方法二:分组时对辨别分数进行四舍五入
occurences <- combinations %>%
  group_by(discernment = round(discernment, digits = 2)) %>%
  summarise(n_occurrences = n()) %>% 
  arrange(discernment)

2. 使用近似比较函数(适用于过滤场景)

如果需要保留原始数值,仅在过滤时匹配近似值,可以使用dplyr内置的near()函数:

# 筛选近似等于-0.95的行
occurences %>% filter(near(discernment, -0.95, tol = 1e-6))

3. 使用精确十进制类型

借助decimal包存储精确的十进制小数,从根源避免二进制浮点数误差:

library(decimal)

# 用decimal类型生成HR和FAR数值
HR_values <- as.decimal(seq(0, 1, by = 0.01))
FAR_values <- as.decimal(seq(0, 1, by = 0.01))

# 计算精确的辨别分数
combinations <- expand.grid(HR = HR_values, FAR = FAR_values) %>%
  mutate(discernment = HR - FAR)

# 分组统计
occurences <- combinations %>%
  group_by(discernment) %>%
  summarise(n_occurrences = n()) %>% 
  arrange(discernment)

原因说明

计算机中的浮点数采用二进制存储,部分十进制小数(如0.01)无法被精确转换为二进制小数,导致HR与FAR相减后产生微小的精度误差。这些误差肉眼不可见,但会被dplyr的group_by()识别为不同的分组键,最终出现看似重复的行。

内容的提问来源于stack exchange,提问作者Jan Pfänder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 17:31:03