Max Candocia
Max Candocia

Reputation: 4385

How can I pass a variable through a quoted expression into a function that returns a function using that value?

Here's an example:

library(ggplot2)
library(scales) # for percent() function

custom_percent <- function(n){
    function(x){
         return(paste(percent(x), sprintf('(%d)', round(x* (n)))))
    }
 }

mydata = data.frame(x = rep(c('a','b','c'), each=100))

ggplot(mydata) + geom_text(aes_(x=~x, y=~..prop.., 
                           label=bquote(.(custom_percent)(length(x))(..prop..)),
                           stat='count')

The function works properly outside of such an environment, but in order to use it with ggplot, it has to be quoted in one way or another.

Unfortunately, I can't figure out how to properly return a generated function that contains a variable that is passed in such a manner. I've tried this:

custom_percent <- function(n){
    n = enquo(n)
    function(x){
         return(paste(percent(x), sprintf('(%d)', round(x* (!!n)))))
    }
 }

But I get an error when I do this: Error in !n : invalid argument type

Does anyone know how to properly use the desired value of n in the function returned by custom_percent()?

And before anyone asks, I know I can generate the statistics in a summary data frame beforehand or use utils::getAnywhere() as a workaround, but that's not the point of the question.

Upvotes: 1

Views: 308

Answers (2)

Melissa Key
Melissa Key

Reputation: 4551

The issue isn't your function - which looks fine as originally written. You're missing parenthesis in your geom_text call, which I think is the primary issue.

custom_percent <- function(n){
  function(x){
    return(paste(percent(x), sprintf('(%d)', round(x* (n)))))
  }
}

# I made the data slightly more variable
mydata = data.frame(x = rep(c('a','b','c'), rbinom(3, 100, .8)))

ggplot(mydata, aes(x = x)) + 
  # geom_bar(stat = 'count') + 
  geom_text(aes_(label=bquote(.(custom_percent)(length(x))(..count.. / sum(..count..)))),
    stat='count')

I think this gets you what you want (and throwing ..prop.. in there works too, it's just flat 1s for this data - which did not make sense to me. If thats what you wanted/expected, by all means switch it back. You might check out https://sebastiansauer.github.io/percentage_plot_ggplot2_V2/ - that shows where you can use ..prop.. and ..count.., but it doesn't really explain why).

Another thing you can do (taking advantage of ..prop.. as written):

ggplot(mydata, aes(x = x, y = ..prop..), stat = 'count') + 
  # geom_bar(aes(y = ..prop..), stat = 'count') +
  geom_text(aes_(label=bquote(.(custom_percent)(length(x))((..count..) / sum(..count..)))),
    stat='count') + 
  scale_y_continuous(labels=scales::percent)

Upvotes: 1

Onyambu
Onyambu

Reputation: 79288

You can use the base r substitute to capture the whole expression and then write the expression as a functionBody

custom_percent <- function(n){
 n=eval(n)
 m=substitute(return(paste(percent(x), sprintf('(%d)', round(x*n)))))
 `functionBody<-`(function(x)1,parent.frame(),m)
}

s=3
custom_percent(s)
function (x) 
return(paste(percent(x), sprintf("(%d)", round(x * 3))))

as you can see, n has been replaced with 3. You can try any other number. And it will work

Upvotes: 0

Related Questions