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值的轮廓会更稀疏。
伪影原因与解决
伪影通常来自分组计算时的参数不一致:
- 带宽不一致:默认stat_density_2d会为每个分组自动计算带宽,导致不同组的核扩散程度不同,出现异常轮廓。解决:手动指定统一带宽
bw = c(0.5, 0.5)。 - 坐标范围不一致:分组计算时,每个组的x/y范围不同,count模式下的轮廓会延伸到无数据区域,产生伪影。解决:用
xlim/ylim统一所有图的坐标范围。 - 样本量差异:样本量小的组,count模式下的估计误差大,容易出现跳变轮廓。解决:用外部计算的核密度(如
kde2d()),手动控制插值范围。
内容的提问来源于stack exchange,提问作者Anke
相关产品推荐
相关产品推荐

