如何为包装 ggplot() 的自定义函数编写可选参数?

How to code optional arguments for a custom function that wraps ggplot()?

我有一个一般性问题,但找不到满意的答案。我正在构建一组可视化功能,我想让用户在使用它们时具有灵活性。例如,我想让它保持可选状态,即误差线是否应包含在条形图中,或者 geom_text() 中的标签是百分比还是小数。

如果我们考虑 ggplot() 中的典型代码构造,我们的元素由 + 分隔。因此,如果我想允许可选构造,我可能需要“打开”或“关闭”整个 geoms(例如,如果用户不想要错误栏,则完全忽略 geom_errorbar() plot),或者以其他方式调整 within geoms(例如,更改 geom_text() 中唯一的 label 参数以将标签转换为百分比或保留小数).

我希望我的问题不会引起过于基于意见的答案,而是让人们在用自定义函数包装 ggplot() 时列出 standard/typical 编码可选参数的方法。

例子

我想出了一个我不喜欢的解决方案。我认为这会使代码变长且难以阅读。我也不能说它在计算方面是否有效。
假设我想为条形图构建一个自定义函数。有几件事我希望它们是“可调整的”:

  1. 是否应该订购柱状图(参见 reorder_cols 参数)
  2. 是否提供用户自己的一组 x 轴标签(参见 x_axis_labels 参数)
  3. 是否添加误差线(add_errorbar)
  4. 是否以百分比显示条形标签(show_in_percents)

然后我把每个可选的代码赋值到一个变量中,根据bar_chart()的相关参数,用条件判断应该包含哪一段代码。

library(tidyverse)
library(broom)
#> Warning: package 'broom' was built under R version 4.0.3

bar_chart <- function(data, x_var, y_value, reorder_cols = TRUE, x_axis_labels = NULL, add_errorbar = NULL) {
  
  reordered <- geom_bar(stat = "identity", width = 0.8, aes(x = reorder({{ x_var }}, -{{ y_value }} ), y = {{ y_value }}, fill = as_factor({{ x_var }})))
  not_reordered <- geom_bar(stat = "identity", width = 0.8, aes(x = {{ x_var }}, y = {{ y_value }}, fill = as_factor({{ x_var }})))
  
  with_x_axis_labels <- scale_x_discrete(labels = setNames(str_wrap(x_axis_labels, 10), names(x_axis_labels)), expand = c(0, 0))
  without_x_axis_labels <- scale_x_discrete(expand = c(0, 0))
  
  if (reorder_cols == TRUE) {
    my_geom_bar <- reordered
  } else {
    my_geom_bar <- not_reordered
  }
  
  if (is.null(x_axis_labels)) {
    my_scale_x_discrete <- without_x_axis_labels
  } else {
    my_scale_x_discrete <- with_x_axis_labels
  }
  
  if (add_errorbar == TRUE) {
    my_errorbar <-  geom_errorbar(aes(x = {{ x_var }}, y = {{ y_value }}, ymin = errorbar_lwr, ymax = errorbar_upr), width = 0.1, size = 0.75)
  } else {
    my_errorbar <- NULL
  }
  
  
  ggplot(data) +
    my_geom_bar +
    my_errorbar +
    my_scale_x_discrete
  
}


labels_for_barplot <-
  c("bar_1", "bar_2", "bar_3")

mtcars %>%
  lm(mpg ~ factor(carb), data = .) %>%
  broom::tidy() %>%
  mutate(errorbar_lwr = estimate - std.error,
         errorbar_upr = estimate + std.error) %>%
  bar_chart(data = ., x_var = term, y_value = estimate, reorder_cols = TRUE, add_errorbar = TRUE, x_axis_labels = labels_for_barplot)

reprex package (v0.3.0)

于 2021 年 1 月 17 日创建

总而言之,我给出这个例子是为了询问是否有替代的、更简洁的方法来在 ggplot 的包装器中实现可选参数。


编辑


正如@Tjebo 指出的那样,我的代码有问题,根本不会 运行。我更新了它,还删除了关于 geom_text() 的部分,因为它太混乱了。

这不会试图回答所有问题(因为有几个),而只是为了演示您可以利用的原理。查看 the ggplot book on programming with ggplot2

我们的想法是创建一个包含所有 ggplot 对象(例如 aes、geom、scale)的列表。返回的对象 NULL 将被简单地丢弃。这就是全部的美。

我删除了比例尺,因为它有点难以理解您想要达到的目标。这个想法会非常相似。并且实际上通常将整个问题简化为我认为是问题的要点。

library(tidyverse)

bar_chart <- function(data, xvar, yvar,
                      se = TRUE, show_percents = TRUE,
                      myscale = TRUE) {
  newy <- deparse(substitute(yvar))
  if (show_percents) {
    my_label <- paste0(100 * round(data[[newy]], 2), "%")
  } else {
    my_label <- round(data[[newy]], 2)
  }

  ggplot({{data}}, aes({{xvar}}, {{yvar}})) +
    list(
      geom_col(width = 0.8),
      geom_text(vjust = 1.4, color = "white", size = 6, fontface = "bold", label = my_label),
      if (se) geom_errorbar(aes(ymin = {{yvar}} - .1, ymax = {{yvar}} + .1), width = 0.1, size = 0.75)
    )
}

iris2 <- iris %>%
  group_by(Species) %>%
  slice_max(Sepal.Length)

bar_chart(iris2, Species, Sepal.Length)

bar_chart(iris2, Species, Sepal.Length, se = FALSE)

reprex package (v0.3.0)

于 2021 年 1 月 17 日创建