如何为包装 ggplot() 的自定义函数编写可选参数? [英] How to code optional arguments for a custom function that wraps ggplot()?

查看:39
本文介绍了如何为包装 ggplot() 的自定义函数编写可选参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个一般性的问题,我找不到满意的答案.我正在构建一组可视化功能,我希望让用户在使用它们时具有灵活性.例如,我想将误差线是否应包含在条形图中,或者 geom_text() 中的标签是否以百分比或十进制表示.

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

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

示例

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

  1. 是否应该对条形进行排序(参见 reorder_cols 参数)
  2. 是否提供用户自己的一组 x 轴标签(参见 x_axis_labels 参数)
  3. 是否添加错误栏(add_errorbar)
  4. 是否以百分比显示条形标签 (show_in_percents)

然后我将每个可选代码分配给一个变量,并根据bar_chart()的相关参数使用条件来确定应该包含哪一段代码.

library(tidyverse)图书馆(扫帚)#>警告:包 'broom' 是在 R 版本 4.0.3 下构建的bar_chart <- 函数(数据,x_var,y_value,reorder_cols = TRUE,x_axis_labels = NULL,add_errorbar = NULL){重新排序 <- 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))如果(reorder_cols == TRUE){my_geom_bar <- 重新排序} 别的 {my_geom_bar <- not_reordered}如果(is.null(x_axis_labels)){my_scale_x_discrete <- without_x_axis_labels} 别的 {my_scale_x_discrete <- with_x_axis_labels}如果(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)} 别的 {my_errorbar <- NULL}ggplot(数据)+my_geom_bar +my_errorbar +my_scale_x_discrete}label_for_barplot <-c(bar_1"、bar_2"、bar_3")mtcars %>%lm(mpg ~ 因子(碳水化合物),数据 = .)%>%扫帚::整洁() %>%变异(errorbar_lwr = 估计 - 标准错误,errorbar_upr = 估计值 + std.error) %>%bar_chart(data = ., x_var = term, y_value = 估计, reorder_cols = TRUE, add_errorbar = TRUE, x_axis_labels = labels_for_barplot)

由 (v0.3.0) 于 2021 年 1 月 17 日创建

I have a general question that I couldn't find a satisfactory answer for. I'm building a set of visualization functions, and I want to let the user have flexibility when using them. For example, I'd like to keep it optional whether errorbars should be included in a bar plot, or whether labels in geom_text() will be in percent or decimal.

If we think of a typical construction of code in ggplot(), we have elements separated by +. So if I want to allow optional construction, I'd likely need to "turn on" or "turn of" either entire geoms (e.g., completely ignore geom_errorbar() if user doesn't want errorbars in plot), or otherwise tweak within geoms (e.g., changing the only label argument within geom_text() to convert labels to percent or keep in decimals).

I hope my question doesn't invite too opinion-based answers, but rather have people lay out the standard/typical way of coding optional arguments when wrapping ggplot() with customized functions.

Example

I came up with a solution that I dislike. I think it makes code long and hard to read. I also can't say much on whether it's efficient or not computationally-wise.
Say that I want to build a custom function for a bar chart. There are several things that I wish them to be "tweakable":

  1. Whether bars should be ordered or not (see reorder_cols argument)
  2. Whether to provide user's own set of x axis labels (see x_axis_labels argument)
  3. Whether to add errorbars (add_errorbar)
  4. Whether to show bar labels in percent (show_in_percents)

Then I assign each optional code into a variable, and use a conditional to determine which piece of code should be included, according to bar_chart()'s relevant argument.

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)

Created on 2021-01-17 by the reprex package (v0.3.0)

All in all, I gave this example to ask whether there are alternative, more concise ways of achieving optional arguments in a wrapper for ggplot.


EDIT


As @Tjebo noted, my code was buggy and simply won't run. I updated it, also removing the part about geom_text() as it was too confusing.

解决方案

This will not attempt to answer all questions (as there are several), but just to demonstrate the principle which you could make use of. Check out the ggplot book on programming with ggplot2

The idea is to create a list which contains all ggplot objects (such as aes, geom, scale). Objects that are returned NULL will be simply discarded. That's the whole beauty.

I have removed the scale because it was somewhat difficult to understand what you wanted to achieve. The idea would be very similar. And actually generally reduced the entire problem to what I believe is the gist of the question.

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)

Created on 2021-01-17 by the reprex package (v0.3.0)

这篇关于如何为包装 ggplot() 的自定义函数编写可选参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆