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

R中离散数据密度计算:stats::density与ggplot2结果差异咨询

差异核心原因
  • 计算逻辑本质不同:ggplot2::geom_histogram()生成的..density..是分箱直方图密度,计算规则为(单个分箱内样本数 / 总样本数) / 分箱宽度。你的代码中设置binwidth=1,输出结果就是每个整数取值对应的样本占比,完全匹配离散取整后的数据分布。而stats::density()是核密度估计(KDE)函数,默认使用高斯核做连续平滑,会将相邻点的概率质量做加权扩散,属于连续分布的平滑估计,和直方图的离散分箱计数逻辑完全不同,结果自然存在明显差异。
  • 函数调用参数设置错误:你调用density()时设置from = -3, to = 6,直接截断了x<-3和x>6区间的密度估计,但生成的round(rnorm(1000))样本本身存在超出[-3,6]范围的值,截断会导致密度总积分不等于1,结果整体偏移。同时设置n = length(-3:6)仅生成10个估计网格点,网格密度不足以覆盖数据实际范围,后续用geom_col()绘图时也没有做分箱对齐,进一步放大了视觉差异。
  • 场景错配:如果你的目标是预先计算和..density..一致的直方图密度值(适配大数据量场景),不需要调用核密度估计函数,直接通过分箱计数计算即可,计算效率远高于KDE。如果你需要的是核密度平滑结果,应该用geom_line()绘制连续曲线而非geom_col()画离散柱形,同时调整density()的取值范围覆盖全量数据、增加网格点数量,结果会和geom_density()输出对齐。
修正方案

直接调用基础R的hist()函数并关闭绘图模式,提前计算分箱密度,结果和ggplot2内置计算的..density..完全一致,大数据量下计算速度极快:

library(tidyverse)
library(patchwork)

set.seed(123) # 固定随机种子保证结果可复现
df <- data.frame(A = round(rnorm(1000)),
                 B = round(rnorm(1000)),
                 C = round(rnorm(1000))) %>% 
  pivot_longer(cols = everything(), names_to = "group")

# 预计算直方图密度,和ggplot2内置计算逻辑完全对齐
dens_df <- df %>% 
  group_by(group) %>% 
  group_modify(~{
    # 分箱边界设为半整数,匹配整数数据binwidth=1的分箱规则,覆盖x从-3到10的展示范围
    h_res <- hist(.x$value, breaks = seq(-3.5, 10.5, 1), plot = FALSE)
    data.frame(density.x = h_res$mids, density.y = h_res$density)
  })

plot_precomputed <- dens_df %>% 
  ggplot()+
  aes(x = density.x, y = density.y) %>% 
  geom_col(width = 1, col = "white")+ # 柱宽设为1和分箱宽度一致
  scale_x_continuous(breaks = seq(-3,10,1), limits = c(-3,10))+
  stat_function(fun = dnorm, n = 14, args = list(mean = 0, sd = 1), geom = "point", col = "red") +
  stat_function(fun = dnorm, n = 14, args = list(mean = 0, sd = 1), geom = "point", size = 2, col = "red") +
  stat_function(fun = dnorm, n = 14, args = list(mean = 0, sd = 1), geom = "line", col = "red") +
  facet_wrap(~group)+
  labs(title = "precomputed histogram density")

plot_ggplot <- df %>% 
  ggplot()+
  aes(x = value, y = ..density..) %>% 
  geom_histogram(
    binwidth = 1,
    col = "white")+
  scale_x_continuous(breaks = seq(-3,10,1), limits = c(-3,10))+
  stat_function(fun = dnorm, n = 14, args = list(mean = 0, sd = 1), geom = "point", col = "red") +
  stat_function(fun = dnorm, n = 14, args = list(mean = 0, sd = 1), geom = "point", size = 2, col = "red") +
  stat_function(fun = dnorm, n = 14, args = list(mean = 0, sd = 1), geom = "line", col = "red") +
  facet_wrap(~group)+
  labs(title = "using ggplot2 ..density..")

plot_precomputed + plot_ggplot

运行上述代码后,两个子图的柱形高度会完全一致。

内容的提问来源于stack exchange,提问作者CyG

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:54:18