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

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

问题描述

我有一个普遍的问题,我找不到令人满意的答案.我正在构建一组可视化功能,并且希望让用户在使用它们时具有灵活性.例如,我想使其是否可选,是否在条形图中包括误差线,或者 geom_text()中的标签是百分数还是十进制.

如果我们想到 ggplot()中代码的典型构造,则我们有用 + 分隔的元素.因此,如果我要允许可选的构造,则可能需要打开"按钮.或转向"整个 geom (例如,如果用户不希望绘图中出现错误条,则完全忽略 geom_errorbar()),或者在内调整> geom (例如,更改 geom_text()中唯一的 label 参数以将标签转换为百分比或保留小数).

我希望我的问题不会邀请太多基于观点的答案,而是让人们在使用自定义函数包装 ggplot()时设计出编码可选参数的标准/典型方式.

示例

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

  1. 是否应订购酒吧(请参见 reorder_cols 参数)
  2. 是否提供用户自己的x轴标签集(请参见 x_axis_labels 参数)
  3. 是否添加错误栏( add_errorbar )
  4. 是否以百分比显示条形标签( show_in_percents )

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

 库(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),名称(x_axis_labels)),expand = c(0,0))没有_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<-不带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),宽度= 0.1,大小= 0.75)} 别的 {my_errorbar<-空}ggplot(数据)+my_geom_bar +my_errorbar +my_scale_x_discrete}labels_for_barplot<-c("bar_1","bar_2","bar_3")mtcars%>%lm(mpg〜factor(carb),数据=.)%>%扫帚:: tidy()%&%;%mutate(errorbar_lwr =估计-标准错误,errorbar_upr =估算值+标准误差)%&%;%bar_chart(数据=.,x_var =项,y_value =估计,reorder_cols = TRUE,add_errorbar = TRUE,x_axis_labels =标签_for_barplot) 

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

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天全站免登陆