【问题标题】:Changing whisker definition in geom_boxplot更改 geom_boxplot 中的晶须定义
【发布时间】:2011-06-13 12:32:46
【问题描述】:

我正在尝试使用 ggplot2 / geom_boxplot 生成一个箱线图,其中晶须被定义为第 5 个和第 95 个百分位数,而不是 0.25 - 1.5 IQR / 0.75 + IQR,并且像往常一样绘制这些新晶须的异常值。我可以看到 geom_boxplot 美学包括 ymax / ymin,但我不清楚我如何将值放在这里。好像是:

stat_quantile(quantiles = c(0.05, 0.25, 0.5, 0.75, 0.95))

应该能够提供帮助,但我不知道如何关联此统计数据的结果以设置适当的 geom_boxplot() 美学:

geom_boxplot(aes(ymin, lower, middle, upper, ymax))

我看过其他帖子,人们提到本质上是手动构建类似于箱线图的对象,但我宁愿保持整个箱线图格式塔完整,只是修改两个正在绘制的变量的含义。

【问题讨论】:

    标签: r ggplot2 statistics boxplot ggproto


    【解决方案1】:

    geom_boxplot 和 stat_summary 可以做到:

    # define the summary function
    f <- function(x) {
      r <- quantile(x, probs = c(0.05, 0.25, 0.5, 0.75, 0.95))
      names(r) <- c("ymin", "lower", "middle", "upper", "ymax")
      r
    }
    
    # sample data
    d <- data.frame(x=gl(2,50), y=rnorm(100))
    
    # do it
    ggplot(d, aes(x, y)) + stat_summary(fun.data = f, geom="boxplot")
    
    # example with outliers
    # define outlier as you want    
    o <- function(x) {
      subset(x, x < quantile(x)[2] | quantile(x)[4] < x)
    }
    
    # do it
    ggplot(d, aes(x, y)) + 
      stat_summary(fun.data=f, geom="boxplot") + 
      stat_summary(fun.y = o, geom="point")
    

    【讨论】:

    • kohske,这确实改变了胡须(谢谢!),但异常值消失了。
    • 示例已更新:有多种方法可以做到这一点,但也许这是在 geom_point 中绘制异常值的最简单方法。
    • 太棒了! o 函数可能应该使用相同的 probs = c(0.05, 0.95)[1] / [2] 以便排除的点与胡须匹配。再次感谢。看来我需要了解更多关于 stat_summary 的信息。
    • 是否可以在 ymin 和 ymax 中放置胡须?
    • 在上面的代码中,将“subset(x....)”行替换为“subset(x, x quantile(x, 0.95)) "
    【解决方案2】:

    现在可以在ggplot2_2.1.0 中指定胡须端点。复制?geom_boxplot中的例子:

     # It's possible to draw a boxplot with your own computations if you
     # use stat = "identity":
     y <- rnorm(100)
     df <- data.frame(
       x = 1,
       y0 = min(y),
       y25 = quantile(y, 0.25),
       y50 = median(y),
       y75 = quantile(y, 0.75),
       y100 = max(y)
     )
     ggplot(df, aes(x)) +
       geom_boxplot(
        aes(ymin = y0, lower = y25, middle = y50, upper = y75, ymax = y100),
        stat = "identity"
      )
    

    【讨论】:

      【解决方案3】:

      基于@konvas 的回答,从ggplot2.0.x 开始,您可以使用ggproto 系统extend ggplot 并定义您自己的统计数据。

      通过复制 ggplot2 stat_boxplot 代码并进行一些编辑,您可以快速定义一个新的统计数据 (stat_boxplot_custom),它将您要用作参数的百分位数 (qs) 而不是 @987654330 stat_boxplot 使用的 @ 参数。新的统计数据在这里定义:

      # modified from https://github.com/tidyverse/ggplot2/blob/master/R/stat-boxplot.r
      library(ggplot2)
      stat_boxplot_custom <- function(mapping = NULL, data = NULL,
                           geom = "boxplot", position = "dodge",
                           ...,
                           qs = c(.05, .25, 0.5, 0.75, 0.95),
                           na.rm = FALSE,
                           show.legend = NA,
                           inherit.aes = TRUE) {
        layer(
            data = data,
            mapping = mapping,
            stat = StatBoxplotCustom,
            geom = geom,
            position = position,
            show.legend = show.legend,
            inherit.aes = inherit.aes,
            params = list(
            na.rm = na.rm,
            qs = qs,
            ...
            )
        )
      }
      

      然后,定义层函数。请注意,我直接从stat_boxplot 复制了 b/c,您必须使用::: 访问一些内部 ggplot2 函数。这包括直接从StatBoxplot 复制的许多内容,但关键领域是直接从qs 参数计算统计数据:stats &lt;- as.numeric(stats::quantile(data$y, qs))compute_group 函数内部。

      StatBoxplotCustom <- ggproto("StatBoxplotCustom", Stat,
        required_aes = c("x", "y"),
        non_missing_aes = "weight",
      
        setup_params = function(data, params) {
          params$width <- ggplot2:::"%||%"(
            params$width, (resolution(data$x) * 0.75)
          )
      
          if (is.double(data$x) && !ggplot2:::has_groups(data) && any(data$x != data$x[1L])) {
            warning(
              "Continuous x aesthetic -- did you forget aes(group=...)?",
              call. = FALSE
            )
          }
      
          params
        },
      
        compute_group = function(data, scales, width = NULL, na.rm = FALSE, qs = c(.05, .25, 0.5, 0.75, 0.95)) {
      
          if (!is.null(data$weight)) {
            mod <- quantreg::rq(y ~ 1, weights = weight, data = data, tau = qs)
            stats <- as.numeric(stats::coef(mod))
          } else {
          stats <- as.numeric(stats::quantile(data$y, qs))
          }
          names(stats) <- c("ymin", "lower", "middle", "upper", "ymax")
          iqr <- diff(stats[c(2, 4)])
      
          outliers <- (data$y < stats[1]) | (data$y > stats[5])
      
          if (length(unique(data$x)) > 1)
          width <- diff(range(data$x)) * 0.9
      
          df <- as.data.frame(as.list(stats))
          df$outliers <- list(data$y[outliers])
      
          if (is.null(data$weight)) {
            n <- sum(!is.na(data$y))
          } else {
            # Sum up weights for non-NA positions of y and weight
            n <- sum(data$weight[!is.na(data$y) & !is.na(data$weight)])
          }
      
          df$notchupper <- df$middle + 1.58 * iqr / sqrt(n)
          df$notchlower <- df$middle - 1.58 * iqr / sqrt(n)
      
          df$x <- if (is.factor(data$x)) data$x[1] else mean(range(data$x))
          df$width <- width
          df$relvarwidth <- sqrt(n)
          df
        }
      )
      

      还有一个gist here,包含这个代码。

      那么,stat_boxplot_custom 可以像stat_boxplot 一样被调用:

      library(ggplot2)
      y <- rnorm(100)
      df <- data.frame(x = 1, y = y)
      # whiskers extend to 5/95th percentiles by default
      ggplot(df, aes(x = x, y = y)) +
        stat_boxplot_custom()
      # or extend the whiskers to min/max
      ggplot(df, aes(x = x, y = y)) +
        stat_boxplot_custom(qs = c(0, 0.25, 0.5, 0.75, 1))
      

      【讨论】:

      • 这个答案太棒了!以上不适用于 facet_grid。这完美无缺。非常感谢!
      猜你喜欢
      • 2015-05-11
      • 1970-01-01
      • 2017-10-25
      • 2018-08-13
      • 2014-06-19
      • 1970-01-01
      • 2018-03-29
      • 1970-01-01
      相关资源
      最近更新 更多