1

I am writing a function that makes ggplots with given data, and I am interested in setting a parameter that can call the different scales::label_* functions to change the outputs. For example, I want to be able to call create_pie_chart(starwars, fill = gender, label = "percent") and have the labels appear using scales::label_percent or create_pie_chart(starwars, fill = gender, label = "date") and have the labels use scales::label_date.

I know I could just use some if else statements, but I'm curious if there is a tidy eval or NSE way to do this? Maybe using substitute or {{}}?

Here is an example where I tried to use glue to create the function name -- it does not work:

library(dplyr)

create_pie_chart <- function(data,
                             fill,
                             label = "percent") {

    data_counts <- data %>%
      dplyr::summarize(num = dplyr::n(), .by = {{ fill }}) %>%
      dplyr::mutate(prop = num/sum(num))
    
    label_function <- glue::glue("scales::label_", label)
    
    plot <- data_counts %>%
      ggplot2::ggplot(ggplot2::aes(x="", y = prop, fill = {{ fill }})) +
      ggplot2::geom_bar(width = 1, stat = "identity") +
      ggplot2::coord_polar("y", start = 0) + 
      ggplot2::geom_label(
        # This is the line where I would like to use tidy eval
        # to call `scales::label_*` with my `label` parameter
        ggplot2::aes(label = label_function()(prop)),
        position = ggplot2::position_stack(vjust = 0.5)
      ) +
      ggplot2::theme_void()
    
    plot
}

data("starwars")

create_pie_chart(starwars, fill = gender, label = "percent")

I have also tried match.fun/get(..., mode = function) as described in this question, but I get an error stating:

Error in get(as.character(FUN), mode = "function", envir = envir) :
object 'scales::label_percent' of mode 'function' was not found
mfg3z0
  • 561
  • 16

1 Answers1

3

No need for tidyeval. You can use getExportedValue to find a name inside a namespace; so to find scales::label_X, you could use getExportedValue("scales", "label_X"):

label_function <- getExportedValue("scales", glue::glue("label_{label}"))

For non-qualified names (i.e. if you didn’t have scales:: in front of it), you can instead use get(name, mode = "function").

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    For completeness, there is probably a simple and elegant “tidyeval way” of solving this; but the simplest I can come up with off the top of my head now is this monster: `rlang::eval_tidy(rlang::expr((!! rlang::parse_expr(label_function))()))`. Or maybe there isn’t, since this is mixing strings and unevaluated expressions anyway. – Konrad Rudolph Mar 15 '23 at 21:47