在 R 中使用 geom_density_2d() 时出错:`stat_density2d()` 中的计算失败:带宽必须严格为正

Ony*_*kay 10 r ggplot2

在尝试使用 ggplot2 制作测试 2d 密度图时,我使用了代码片段:

ggplot(df, aes(x = S1.x, y = S1.y)) + geom_point() + geom_density_2d()
Run Code Online (Sandbox Code Playgroud)

我收到错误消息:“计算失败stat_density2d():带宽必须严格为正”

我的数据框如下所示:

> df

transcriptID S1.x      S1.y      S2.x       S2.y    
DQ459412     0.000000  0.000000  0.000000   0.000000
DQ459413     1.584963  2.358379  4.392317   3.085722    
DQ459415     0.000000  0.000000  0.000000   0.000000    
DQ459418     0.000000  0.000000  0.000000   0.000000    
DQ459419     0.000000  0.000000  4.000000   2.891544    
DQ459420     0.000000  0.000000  0.000000   0.000000      
Run Code Online (Sandbox Code Playgroud)

还有,var(df[,"S1.x"]) > 0var(df[,"S1.y"]) > 0

图 1 - 有误差的二维密度图

但是,通过运行,我得到了一个没有错误的密度图:

ggplot(df, aes(x = S2.x, y = S2.y)) + geom_point() + geom_density_2d()
Run Code Online (Sandbox Code Playgroud)

图 2 - 没有错误的密度图

如何解决图 1 中的错误?

Mik*_*ise 11

所以真正的问题是S1.xS1.y值在它们的列中只有一个非零值。事实证明,geom_density_2d仅凭一两个值并不能真正估计密度。但是请继续阅读...

更新:

这个问题以前有人问过,答案通常是你的数据列中需要有非零方差。但是您确实有非零方差,那么为什么它不起作用呢?

  • 查看内部geom_density_2d我们看到它使用MASS::kde2d包函数来计算分布。
  • 查看kde2d我们看到它用于MASS::bandwidth.nrd(df$x)估计带宽。
  • 查看帮助(其中包含代码),bandwidth.nrd我们看到它使用经验法则获取quantile分布的 ,并从第 1 个分位数中减去第 2 个分位数以获得带宽估计值。
  • 对原始数据进行分位数分析,我们看到数据的分位数为零。
  • MASS::kde2d使用bandwidth.nrd该带宽估计值在原始数据上运行会给您带来相同的错误:
library(MASS)
nn <- c("DQ459412","DQ459413","DQ459415","DQ459418","DQ459419","DQ459420")
s1x <- c(0,1.584963,0,0,0,0)
s1y <- c(0,2.358379,0,0,0,0) 
s2x <- c(0,4.392317,0,0,4,0)
s2y <- c(0,3.085722,0,0,2.891544,0) 
df <- data.frame(transcriptID=nn,S1.x=s1x,S1.y=s1y,S2.x=s2x,S2.y=s2y)
Run Code Online (Sandbox Code Playgroud)
> quantile(df$s1x)
      0%      25%      50%      75%     100% 
0.000000 0.000000 0.000000 0.000000 1.584963 
> quantile(df$s1y)
      0%      25%      50%      75%     100% 
0.000000 0.000000 0.000000 0.000000 2.358379 
Run Code Online (Sandbox Code Playgroud)
h <- c(MASS::bandwidth.nrd(df$x), MASS::bandwidth.nrd(df$y))
dens <- MASS::kde2d(df$s1x, df$s1y, h = h, n = n,  lims = c(0,1,0,1))
Run Code Online (Sandbox Code Playgroud)

MASS::kde2d(df$s1x, df$s1y, h = h, n = n, lims = c(0, 1, 0, 1)) 中的错误:带宽必须严格为正

所以使用的真正标准geom_density_2D是 x 和 y 数据都需要在它们的第一个和第二个分位数之间有一个非零的差距。

现在修复它,如果我做一个小的修改 - 用 0.1 替换一个零,如下所示:

nn <- c("DQ459412","DQ459413","DQ459415","DQ459418","DQ459419","DQ459420")
s1x <- c(0,1.584963,0,0,0.1,0)
s1y <- c(0,2.358379,0,0,0.1,0) 
s2x <- c(0,4.392317,0,0,4,0)
s2y <- c(0,3.085722,0,0,2.891544,0) 
df <- data.frame(transcriptID=nn,S1.x=s1x,S1.y=s1y,S2.x=s2x,S2.y=s2y)
print(df)
Run Code Online (Sandbox Code Playgroud)

产生:

  transcriptID     S1.x     S1.y     S2.x     S2.y
1     DQ459412 0.000000 0.000000 0.000000 0.000000
2     DQ459413 1.584963 2.358379 4.392317 3.085722
3     DQ459415 0.000000 0.000000 0.000000 0.000000
4     DQ459418 0.000000 0.000000 0.000000 0.000000
5     DQ459419 0.100000 0.100000 4.000000 2.891544
6     DQ459420 0.000000 0.000000 0.000000 0.000000
Run Code Online (Sandbox Code Playgroud)

然后我得到这个情节而不是你的错误。

在此处输入图片说明 您可以让该0.1值接近零,最终它将无法再计算分布,您将再次出现错误。

处理这种情况的一种一般方法是向您的数据添加非常少量的噪声,模拟这样一个事实,即基于连续分布的真实测量的任何有意义的计算都应该不受该少量噪声的影响。

希望有帮助。

  • 是的,这有帮助!我的实际数据有 198716 行,其中 S1.x 和 S1.y 列有多个非零行。我认为问题在于数据的实际大小。当我将其减少到 200 行时,我看到一个微小的密度图,没有错误。我认为随着行数的增加,密度图变得难以辨别。 (2认同)
  • @MikeWise,我遇到了同样的问题,感谢您的侦探工作,我找到了问题的原因。实际上我的第 1、第 2、第 3 分位数都是相同的,但我认为它们必须是 3 个不同的,因为当我更改第 1 个分位数时,它仍然不起作用。只有当我也改变了第二个时,我才能得到一个情节。 (2认同)

Mar*_*ius 5

@Mike Wise 的答案确实非常可靠,我的答案在某种程度上是对它的补充。实际上,该bandwidth.nrd函数计算第 3 个和第1 个分位数之间的差异,而不是第 2 个和第 1 个(来自函数的代码):

r <- quantile(distances, c(0.25, 0.75))
Run Code Online (Sandbox Code Playgroud)

我建议不要向数据添加随机噪声,而是建议自己预先计算带宽并将它们传递给函数,测试非零值,如下所示:

kde2d(df$s1x, df$s1y, 
      h = c(ifelse(bandwidth.nrd(df$s1x) == 0, 0.1, bandwidth.nrd(df$s1x)),
            ifelse(bandwidth.nrd(df$s1y) == 0, 0.1, bandwidth.nrd(df$s1y))))
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。