Search code examples
rggplot2dplyrggplotly

R: using curly braces referring to column name within a conditionally-defined expression inside custom function does not work


I have defined a function which produces a ggplot chart, where the y-variable can either be a level or a percentage, so I have included an if-else to amend the options appropriately. I then run the chart through ggplotly to produce an interactive version, but the hover-over text label does not work as intended. As shown in the picture, the label just prints my function instead of showing the y-value as intended.

I have struggled to find the right approach and think that I am supposed to use eval, quote or expression in some way, but don't understand how it all fits together. What I do know is that without using e.g. quote, I get an error saying the object values does not exist, which suggests that part of the function is evaluated without being told values is a column within my dataset.

Here is my reprex:

library(tidyverse) #for dplyr
library(ggplot2) #for chart
library(scales) #for axis label
library(plotly) #for interactive chart

# dataset
dataset <- data.frame(geography_name=c("a","b","c","d","e","f"),
                      values=c(2,3,4,5,6,7))

# custom helper functions
perc_form = function(x, d=1) sprintf(paste0("%1.",d,"f"), x) 

value_form = function(x,s=2,d= -1) format(signif(round(as.numeric(x), d),s), big.mark=",")

# function producing ggplot chart
barchart <- function( data_set = dataset,
                      x_var = geography_name, 
                      y_var = values, 
                      bar_stat_type=NULL) {
  
  # Set axis type dependent on variable format
  if (bar_stat_type=="pct") {
    y_var_label <-  paste0("Rate: ",quote(perc_form({{y_var}})),"%")
    label_form <- (percent_format(accuracy=1))
  }  else if (bar_stat_type=="money") {
    y_var_label <- paste0("Level: £",quote(value_form({{y_var}})))
    label_form <- (comma_format())
  }
  
  # Chart
  barchart <- data_set %>%  
    ggplot(mapping = aes(x = {{x_var}}, 
                         y = {{y_var}}, 
                         text = paste0(geography_name, "\n",
                                       eval(y_var_label), "\n")))+
    geom_bar(stat = "identity", position = position_dodge(), width = 0.4)+
    scale_y_continuous(labels = eval(label_form)) #this evaluation works
  
  
  return(barchart)
}

# ggplot chart is produced fine
gg <- barchart(bar_stat_type="money")
gg

# ggplotly chart does not have correct hover-over labels
ggplotly(gg)

ggplotly labels do not include actual y-variable value


Solution

  • There is no need for quote or eval. Simply make you y_var_labels functions which can be called inside your ggplot2 code:

    library(ggplot2)
    library(scales)
    library(plotly)
    
    barchart <- function(data_set = dataset,
                         x_var = geography_name,
                         y_var = values,
                         bar_stat_type = NULL) {
      if (bar_stat_type == "pct") {
        y_var_label <- function(x) paste0("Rate: ", perc_form(x), "%")
        label_form <- percent_format(accuracy = 1)
      } else if (bar_stat_type == "money") {
        y_var_label <- function(x) paste0("Level: £", value_form(x))
        label_form <- comma_format()
      }
    
      data_set %>%
        ggplot(mapping = aes(
          x = {{ x_var }},
          y = {{ y_var }},
          text = paste0(
            geography_name, "\n",
            y_var_label({{ y_var }}), "\n"
          )
        )) +
        geom_bar(stat = "identity", position = position_dodge(), width = 0.4) +
        scale_y_continuous(labels = label_form)
    }
    
    gg <- barchart(bar_stat_type = "money")
    
    ggplotly(gg)
    

    enter image description here