【问题标题】:How to deal with zero in log plot如何处理对数图中的零
【发布时间】:2017-03-06 07:16:07
【问题描述】:

问题

我想使用 ggplot2 在 y 轴上使用对数刻度绘制折线图中的数据。不幸的是,我的一些价值观一直下降到零。数据表示依赖于某些参数的特征的相对出现。当样本中没有观察到该特征时,值为零,这意味着它很少出现,或者实际上从不出现。这些零值会导致对数图中出现问题。

以下代码说明了简化数据集上的问题。实际上,数据集包含更多点,因此曲线看起来更平滑,参数p 的值也更多。

library(ggplot2)

dat <- data.frame(x=rep(c(0, 1, 2, 3), 2),
                  y=c(1e0, 1e-1, 1e-4, 0,
                      1e-1, 1e-3, 0, 0),
                  p=c(rep('a', 4), rep('b', 4)))
qplot(data=dat, x=x, y=y, colour=p, log="y", geom=c("line", "point"))

根据上面的数据,我们预计有两条线,第一条在对数图上应该有三个有限点,第二条在对数图上应该只有两个有限点。

但是,正如您所看到的,这会产生一个非常具有误导性的情节。看起来蓝线和红线都收敛到 1e-4 和 1e-3 之间的值。原因是log(0) 给出了-Inf,ggplot 只是放在了下轴上。

我的问题

在 R 中使用 ggplot2 处理这个问题的最佳方法是什么? 最佳我的意思是在效率方面,并且是理想的 R(我对 R 相当陌生)。

该图应表明这些曲线分别在 x=2(红色)或 x=1(蓝色)之后下降到“非常小”。理想情况下,从最后一个有限点向下有一条垂直线。我的意思如下所示。

我的尝试

在这里,我将描述我的想法。但是,鉴于我对 R 还很陌生,我怀疑可能有更好的方法。

library(ggplot2)
library(scales)

dat <- data.frame(x=rep(c(0, 1, 2, 3), 2),
                  y=c(1e0, 1e-1, 1e-4, 0,
                      1e-1, 1e-3, 0, 0),
                  p=c(rep('a', 4), rep('b', 4)))

与上述数据相同。

现在,我正在遍历每个唯一参数p,找到最后一个有限点的 x 坐标,并将其分配给 y 为零的所有点的 x 坐标。也就是实现一条垂直线。

for (p in unique(dat$p)) {
    dat$x[dat$p == p & dat$y == 0] <- dat$x[head(which(dat$p == p & dat$y == 0), 1) - 1]
}

此时情节如下所示。

垂直线在那里。不过,也有几点。这些具有误导性,因为它们表明那里有一个实际的数据点,这是不正确的。

要删除我复制 y 数据的点(似乎很浪费),我们称之为yp,并将零替换为NA。然后我使用新的yp 作为geom_point 的 y 美学。

dat$yp <- dat$y
dat$yp[dat$y == 0] <- NA

ggplot(dat, aes(x=x, y=y, colour=p)) +
    geom_line() +
    geom_point(aes(y=dat$yp)) +
    scale_y_continuous(trans=log10_trans(),
                       breaks = trans_breaks("log10", function(x) 10^x),
                       labels = trans_format("log10", math_format(10^.x)))

我使用ggplot 而不是qplot,这样我就可以为geom_linegeom_point 赋予不同的美感。

最后,剧情是这样的。

这样做的正确方法是什么?

【问题讨论】:

  • 处理这个问题的惯用方法不是在对数刻度上绘制零值。解决此问题的一种方法是改用 log(y + 1) 之类的转换。
  • 您是否考虑过替代转换?更高阶的第 n 个根不会有同样的问题。
  • 不幸的是,它必须是以 10 为底的对数刻度。我需要将我的数据与现有图进行比较。

标签: r ggplot2


【解决方案1】:

如果你使用 ggplot,你可以使用scales::pseudo_log_trans() 作为你的转换对象。这会将您的 -inf 替换为 0。

来自文档 (https://scales.r-lib.org/reference/pseudo_log_trans.html),

一种将数字映射到有符号对数刻度的转换,并平滑过渡到 0 附近的线性刻度。

pseudo_log_trans(sigma = 1, base = exp(1))

例如,我的比例表达式如下所示:

+ scale_fill_gradient(name = "n occurrences", trans="pseudo_log")

未确认,但您可能需要包含 scales 库:

require("scales")

【讨论】:

    【解决方案2】:

    对我来说,我使用

    + scale_y_continuous(trans=scales::pseudo_log_trans(base = 10))
    

    【讨论】:

      【解决方案3】:

      最简单的方法是为每个数字添加一个小值。例如,

      df <- mutate(df, log_var = log(var + 0.01))
      ggplot(df, aes(x = log_var)) + geom_histogram()
      

      【讨论】:

        猜你喜欢
        • 2021-06-05
        • 1970-01-01
        • 1970-01-01
        • 2015-08-24
        • 2023-04-10
        • 1970-01-01
        • 1970-01-01
        • 2021-04-29
        • 1970-01-01
        相关资源
        最近更新 更多