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

ggplot中stat_density_2d的level解读与每km²密度转换问题

ggplot2 stat_density_2d:密度转换、统一刻度及contour_var问题解决

1. 理解level值与转换为实际km²密度

默认stat_density_2d()生成的level是概率密度的分位数阈值(比如level=0.5对应包含50%样本的区域),没有直接生物学意义。要得到每km²的实际个体密度:

  • 你的数据是UTM[km]格式,x/y单位为公里,因此核密度的天然单位是个体数/km²。但ggplot默认输出的是概率密度(总积分=1),需乘以总样本数转换为实际密度:
    实际密度 = 概率密度 × 总样本数
  • 若直接用stat_density_2d(),需指定contour_var="density",再手动设置level为你需要的密度阈值(如levels=c(2,5,10)代表每km²2、5、10个个体的等高线)。

2. 外部计算核密度(更易控制与校准)

如果在ggplot外计算核密度遇到困难,用MASS::kde2d()是最直接的方案,步骤如下:

library(MASS)
library(ggplot2)

# 示例数据:df包含x(UTM km)、y(UTM km)、survey分组列
# 计算单调查的核密度(h为带宽,单位km,根据数据分布调整)
kde_out <- kde2d(df$x, df$y, h = c(0.5, 0.5))
# 转换为ggplot可用的数据框,同时转换为实际密度
kde_df <- data.frame(
  x = rep(kde_out$x, length(kde_out$y)),
  y = rep(kde_out$y, each = length(kde_out$x)),
  density = as.vector(kde_out$z) * nrow(df)  # 乘样本数得到个体数/km²
)

# 绘制热图+等高线
ggplot(kde_df, aes(x, y, fill = density)) +
  geom_raster(interpolate = TRUE) +
  geom_contour(aes(z = density), color = "white", breaks = c(2,5,10)) +
  scale_fill_viridis_c(name = "个体数/km²")

3. 多调查图保持统一level(相同颜色对应相同密度)

要让不同调查的图颜色刻度完全一致,需统一计算范围和刻度:

# 假设你有多个调查数据框:df_survey1, df_survey2, df_survey3
# 第一步:统一带宽,计算所有调查的密度范围
all_dens <- c()
common_h <- c(0.5, 0.5)  # 所有调查用相同带宽
for (dat in list(df_survey1, df_survey2, df_survey3)) {
  kde <- kde2d(dat$x, dat$y, h = common_h)
  all_dens <- c(all_dens, kde$z * nrow(dat))
}
# 确定全局密度范围
dens_limits <- c(0, max(all_dens))
# 自定义统一的刻度断点
dens_breaks <- seq(0, max(all_dens), by = 5)

# 绘制每个调查的图时,复用刻度设置
ggplot(kde_df_survey1, aes(x, y, fill = density)) +
  geom_raster(interpolate = TRUE) +
  geom_contour(aes(z = density), color = "white", breaks = dens_breaks) +
  scale_fill_viridis_c(name = "个体数/km²", limits = dens_limits, breaks = dens_breaks) +
  xlim(min(df_survey1$x, df_survey2$x, df_survey3$x), 
       max(df_survey1$x, df_survey2$x, df_survey3$x)) +  # 统一坐标范围
  ylim(min(df_survey1$y, df_survey2$y, df_survey3$y), 
       max(df_survey1$y, df_survey2$y, df_survey3$y))

4. contour_var="count" vs "density"的差异与伪影问题

差异本质

  • contour_var="density":绘制单位面积的个体数(个体数/km²),轮廓仅反映密度分布,不受区域大小影响。
  • contour_var="count":绘制等高线包围区域内的估计个体总数(核密度×区域面积),轮廓形状会随数据分布范围变化——比如样本分散在更大区域时,相同count值的轮廓会更稀疏。

伪影原因与解决

伪影通常来自分组计算时的参数不一致:

  1. 带宽不一致:默认stat_density_2d会为每个分组自动计算带宽,导致不同组的核扩散程度不同,出现异常轮廓。解决:手动指定统一带宽bw = c(0.5, 0.5)。
  2. 坐标范围不一致:分组计算时,每个组的x/y范围不同,count模式下的轮廓会延伸到无数据区域,产生伪影。解决:用xlim/ylim统一所有图的坐标范围。
  3. 样本量差异:样本量小的组,count模式下的估计误差大,容易出现跳变轮廓。解决:用外部计算的核密度(如kde2d()),手动控制插值范围。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 07:52:53