在geom_boxplot中改变须的定义

时间:2021-07-21 12:26:01

I'm trying to use ggplot2 / geom_boxplot to produce a boxplot where the whiskers are defined as the 5 and 95th percentile instead of 0.25 - 1.5 IQR / 0.75 + IQR and outliers from those new whiskers are plotted as usual. I can see that the geom_boxplot aesthetics include ymax / ymin, but it's not clear to me how I put values in here. It seems like:

我试着用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))

should be able to help, but I don't know how to relate the results of this stat to set the appropriate geom_boxplot() aesthetics:

应该会有帮助,但是我不知道如何将这个属性的结果关联到设置适当的geom_boxplot()美学:

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

I've seen other posts where people mention essentially building a boxplot-like object manually, but I'd rather keep the whole boxplot gestalt intact, just revising the meaning of two of the variables being drawn.

我还见过其他文章,人们提到手工构建一个类似boxplot的对象,但我宁愿保持整个boxplot格式塔完整,只修改要绘制的两个变量的含义。

3 个解决方案

#1


35  

geom_boxplot with stat_summary can do it:

使用stat_summary的geom_boxplot可以做到:

# 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")

#2


5  

Building on @konvas's answer, beginning in ggplot2.0.x, you can extend ggplot using the ggproto system and define your own stat.

基于@konvas的答案,从ggplot2.0开始。您可以使用ggproto系统扩展ggplot并定义您自己的属性。

By copying the ggplot2 stat_boxplot code and making a few edits, you can quickly define a new stat (stat_boxplot_custom) that takes the percentiles you want to use as an argument (qs) instead of the coef argument that stat_boxplot uses. The new stat is defined here:

通过复制ggplot2 stat_boxplot代码并进行一些编辑,您可以快速定义一个新的stat (stat_boxplot_custom),它使用您希望使用的百分比作为参数(qs),而不是stat_boxplot使用的coef参数。新的属性定义如下:

# 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,
      ...
      )
  )
}

Then, the layer function is defined. Note that b/c I copied directly from stat_boxplot, you have to access a few internal ggplot2 functions using :::. This includes a lot of stuff copied directly over from StatBoxplot, but the key area is in computing the stats directly from the qs argument: stats <- as.numeric(stats::quantile(data$y, qs)) inside of the compute_group function.

然后,定义层函数。注意,我直接从stat_boxplot复制了b/c,您必须使用::::访问一些内部的ggplot2函数。这包括许多直接从StatBoxplot复制过来的内容,但关键的地方是直接从qs参数:stats <- as计算stats。compute_group函数内部的数字(stats:分位数(数据$y, qs)。

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
  }
)

There is also a gist here, containing this code.

这里还有一个要点,包含这个代码。

Then, stat_boxplot_custom can be called just like stat_boxplot:

然后,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))

在geom_boxplot中改变须的定义

#3


3  

It is now possible to specify the whiskers endpoints in ggplot2_2.1.0. Copying from the examples in ?geom_boxplot:

现在可以在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"
  )

在geom_boxplot中改变须的定义

#1


35  

geom_boxplot with stat_summary can do it:

使用stat_summary的geom_boxplot可以做到:

# 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")

#2


5  

Building on @konvas's answer, beginning in ggplot2.0.x, you can extend ggplot using the ggproto system and define your own stat.

基于@konvas的答案,从ggplot2.0开始。您可以使用ggproto系统扩展ggplot并定义您自己的属性。

By copying the ggplot2 stat_boxplot code and making a few edits, you can quickly define a new stat (stat_boxplot_custom) that takes the percentiles you want to use as an argument (qs) instead of the coef argument that stat_boxplot uses. The new stat is defined here:

通过复制ggplot2 stat_boxplot代码并进行一些编辑,您可以快速定义一个新的stat (stat_boxplot_custom),它使用您希望使用的百分比作为参数(qs),而不是stat_boxplot使用的coef参数。新的属性定义如下:

# 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,
      ...
      )
  )
}

Then, the layer function is defined. Note that b/c I copied directly from stat_boxplot, you have to access a few internal ggplot2 functions using :::. This includes a lot of stuff copied directly over from StatBoxplot, but the key area is in computing the stats directly from the qs argument: stats <- as.numeric(stats::quantile(data$y, qs)) inside of the compute_group function.

然后,定义层函数。注意,我直接从stat_boxplot复制了b/c,您必须使用::::访问一些内部的ggplot2函数。这包括许多直接从StatBoxplot复制过来的内容,但关键的地方是直接从qs参数:stats <- as计算stats。compute_group函数内部的数字(stats:分位数(数据$y, qs)。

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
  }
)

There is also a gist here, containing this code.

这里还有一个要点,包含这个代码。

Then, stat_boxplot_custom can be called just like stat_boxplot:

然后,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))

在geom_boxplot中改变须的定义

#3


3  

It is now possible to specify the whiskers endpoints in ggplot2_2.1.0. Copying from the examples in ?geom_boxplot:

现在可以在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"
  )

在geom_boxplot中改变须的定义