Error running ggplot2 inside a function

I got this message "Error in FUN(X[[i]], ...) : object 'share' not found" when running ggplot2 inside a function. It works fine if I run it outside a function.

Any idea? Thanks so much!

NOTE: I need to pass 'share' as an argument since this function will be applied to some other variables besides 'share'.

df <- data.frame(own.manu = c("AO", "AO", "AO", "Own", "Own"), brand = c('A', "B", "C", "D", "E"), share = c(.1, .2, .3, .25, .15))

createPlot <- function(myvar, mydata) {
  out <- ggplot2::ggplot(environment=environment()) + 
    ggplot2::geom_bar(ggplot2::aes(y = myvar, x = own.manu, fill = brand), stat = "identity", position = ggplot2::position_stack(reverse = TRUE), data = mydata) +
    ggplot2::scale_y_continuous(labels = scales::percent_format()) +
    ggplot2::geom_text(data = mydata, ggplot2::aes(x = own.manu, 
                                               y = myvar, 
                                               label = ifelse(myvar >= 0.05, paste0(sprintf("%.0f", myvar*100),"%"),"")),
                       position = ggplot2::position_stack(vjust=0.5))
  out
}

out2 <- createPlot(myvar = share, mydata = df)

One option would be to pass the variable name to the function as a string and use get(myvar) every time it is called:

df <- data.frame(own.manu = c("AO", "AO", "AO", "Own", "Own"), brand = c('A', "B", "C", "D", "E"), share = c(.1, .2, .3, .25, .15))

createPlot <- function(myvar, mydata) {
  out <- ggplot2::ggplot(environment=environment()) + 
    ggplot2::geom_bar(ggplot2::aes(y = get(myvar), x = own.manu, fill = brand), stat = "identity", position = ggplot2::position_stack(reverse = TRUE), data = mydata) +
    ggplot2::scale_y_continuous(labels = scales::percent_format()) +
    ggplot2::geom_text(data = mydata, ggplot2::aes(x = own.manu, 
                                                   y = get(myvar), 
                                                   label = ifelse(get(myvar) >= 0.05, paste0(sprintf("%.0f", get(myvar)*100),"%"),"")),
                       position = ggplot2::position_stack(vjust=0.5))
  out
}

out2 <- createPlot(myvar = "share", mydata = df)

share is only found as a variable name within the dataframe, so it can't find it anywhere else.

1 Like

I recently ran into a similar issue, and think I might be able to help -- could you provide the example you mentioned that worked outside of the function, @ktanizar, so I can make sure I can generate the right output?

Hi @ktanizar. You can use double curly bracket to directly pass the name to function.

df <- data.frame(own.manu = c("AO", "AO", "AO", "Own", "Own"), brand = c('A', "B", "C", "D", "E"), share = c(.1, .2, .3, .25, .15))

createPlot <- function(myvar, mydata) {
  out <- ggplot2::ggplot(environment=environment()) + 
    ggplot2::geom_bar(ggplot2::aes(y = {{myvar}}, x = own.manu, fill = brand), stat = "identity", position = ggplot2::position_stack(reverse = TRUE), data = mydata) +
    ggplot2::scale_y_continuous(labels = scales::percent_format()) +
    ggplot2::geom_text(data = mydata, ggplot2::aes(x = own.manu, 
                                                   y = {{myvar}}, 
                                                   label = ifelse({{myvar}} >= 0.05, paste0(sprintf("%.0f", {{myvar}}*100),"%"),"")),
                       position = ggplot2::position_stack(vjust=0.5))
  out
}

out2 <- createPlot(myvar = share, mydata = df)

out2

Created on 2020-03-03 by the reprex package (v0.3.0)

2 Likes

Just a small tweak of @raytong solution to simplify the label-making: You can use

label = scales::percent({{myvar}}, accuracy = 2)

to replace

label = ifelse({{myvar}} >= 0.05, paste0(sprintf("%.0f", {{myvar}}*100),"%"),"")
1 Like

It works! Thank you! One follow-up question: why do we need to use double curly bracket only for 'myvar'? Why not use it for 'own.manu' and 'brand' also?

@ktanizar. It is about the concept of tidy evaluation which you can refer to the following https://tidyeval.tidyverse.org/sec-up-to-speed.html#quote-and-unquote and https://www.tidyverse.org/blog/2019/06/rlang-0-4-0/.
Simply, ggplot is based on a quoted expression. So, when it evaluate, the expression only contains myvar, not the value inside (share). Double curly or called curly curly unquote the myvar and evaluate it's value, then enquoted again by aes. However, own.manu and brand are not variable and no need evaluation before enquote. I hope I can make myself clear.

1 Like

Very clear. Thank you, @raytong!

This is partly your choice: If you always expect your tables to have own.manu and brand columns, and a column with data similar to share's but maybe with a different name, then your function fits the bill. But if you wanted a function that works on tables with similar data but different column names, then you'd want to use {{ for them, too.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.