Use of Ggplot() Within Another Function in R

Use of ggplot() within another function in R

As Joris and Chase have already correctly answered, standard best practice is to simply omit the meansdf$ part and directly refer to the data frame columns.

testplot <- function(meansdf)
{
p <- ggplot(meansdf,
aes(fill = condition,
y = means,
x = condition))
p + geom_bar(position = "dodge", stat = "identity")
}

This works, because the variables referred to in aes are looked for either in the global environment or in the data frame passed to ggplot. That is also the reason why your example code - using meansdf$condition etc. - did not work: meansdf is neither available in the global environment, nor is it available inside the data frame passed to ggplot, which is meansdf itself.


The fact that the variables are looked for in the global environment instead of in the calling environment is actually a known bug in ggplot2 that Hadley does not consider fixable at the moment.
This leads to problems, if one wishes to use a local variable, say, scale, to influence the data used for the plot:

testplot <- function(meansdf)
{
scale <- 0.5
p <- ggplot(meansdf,
aes(fill = condition,
y = means * scale, # does not work, since scale is not found
x = condition))
p + geom_bar(position = "dodge", stat = "identity")
}

A very nice workaround for this case is provided by Winston Chang in the referenced GitHub issue: Explicitly setting the environment parameter to the current environment during the call to ggplot.
Here's what that would look like for the above example:

testplot <- function(meansdf)
{
scale <- 0.5
p <- ggplot(meansdf,
aes(fill = condition,
y = means * scale,
x = condition),
environment = environment()) # This is the only line changed / added
p + geom_bar(position = "dodge", stat = "identity")
}

## Now, the following works
testplot(means)

Using gglocator() within another function

Running ggplot inside a function doesn't automatically print the produced ggplot object the way it does in the console.

Remember that in an interactive session in the console, any object returned by the code you type will be automatically printed: if you type sum(1:5) in the console, then the result [1] 15 is printed to the console. However, if you randomly include the line sum(1:5) in the middle of a function, it will have no effect and the result is not printed. The same is true of ggplot objects.

So the solution is to wrap your plotting code in an explicit call to print:

library(ggmap)

my_function<- function(point_num) {

x11()

print(iris %>%
ggplot()+
aes(Sepal.Width,Sepal.Length)+
geom_point())

points <- gglocator(n = point_num, mercator = FALSE)

return(points)
}

my_function(1)

#> Sepal.Width Sepal.Length
#> 1 2.707581 5.196849

ggplot2 variables within function

You're doing several things wrong.

First, everything specified inside aes() should be columns in your data frame. Do not reference separate vectors, or redundantly call columns via data_df[,1]. The whole point of specifying data = data_df is that then everything inside aes() is evaluated within that data frame.

Second, to write functions to create ggplots on different columns based on arguments, you should be using aes_string so that you can pass the aesthetic mappings as characters explicitly and avoid problems with non-standard evaluation.

Similarly, I would not rely on deparse(substitute()) for the plot title. Use some other variable built into the data frame, or some other data structure.

For instance, I would do something more like this:

data_df = data.frame(matrix(rnorm(200), nrow=20))
time=1:nrow(data_df)
data_df$time <- time

graphit <- function(data,column){
ggplot(data=data, aes_string(x="time", y=column)) +
geom_point(alpha=1/4) +
ggtitle(column)
}

graphit(data_df,"X1")

Create ggplot2 function and specify arguments as variables in data as per ggplot2 standard functionality

With the devel version of ggplot2, we can pass unquoted arguments, convert it to quosure (with enquo) and evaluate it (!!)

ggfun <- function(dat, x.var, y.var){
x.var <- enquo(x.var)
y.var <- enquo(y.var)
ggp <- ggplot(data = dat,
aes(x = !! x.var,
y = !! y.var)) +
geom_point()

return(ggp)
}

ggfun(dat = mtcars, x.var = drat, y.var = hp)

For quoted strings, convert it to symbol with sym (from rlang) and do the evaluation

ggfun <- function(dat, x.var, y.var){
x.var <- rlang::sym(x.var)
y.var <- rlang::sym(y.var)
ggp <- ggplot(data = dat,
aes(x = !! x.var,
y = !! y.var)) +
geom_point()

return(ggp)
}

ggfun(dat = mtcars, x.var = "drat", y.var = "hp")

Sample Image


If we want to pass either quoted or unquoted, the quosure is changed to character (quo_name), then to symbol (sym) and evaluate (!!)

ggfun <- function(dat, x.var, y.var){
x.var <- rlang::sym(quo_name(enquo(x.var)))
y.var <- rlang::sym(quo_name(enquo(y.var)))
ggp <- ggplot(data = dat,
aes(x = !! x.var,
y = !! y.var)) +
geom_point()

return(ggp)
}

p1 <- ggfun(dat = mtcars, x.var = drat, y.var = hp)
p2 <- ggfun(dat = mtcars, x.var = "drat", y.var = "hp")

all.equal(p1, p2)
#[1] TRUE

lazyeval in ggplot2 within other function

One way is to use substitute and aes_q.

draw_point <- function(data, x, y){
ggplot(data, aes_q(substitute(x), substitute(y))) +
geom_point()
}
draw_point(mtcars, wt, qsec)

However, if you want both draw_point(mtcars, wt, qsec) and draw_point(mtcars, "wt", "qsec") to work, you have to be a bit more creative.
The following is a first draft of what you could do with the lazyeval package. This cannot handle all cases, but it should get you started.

draw_point <- function(data, x, y, ...){
# lazy x and y
ld <- as.lazy_dots(list(x = lazy(x), y = lazy(y)))
# combine with dots
ld <- c(ld, lazy_dots(...))
# change to names wherever possible
ld <- as.lazy_dots(lapply(ld, function(x){
try(x$expr <- as.name(x$expr), silent=TRUE)
x
}))
# create call
cl <- make_call(quote(aes), ld)
# ggplot command
ggplot(data, eval(cl$expr)) +
geom_point()
}

# examples that work
draw_point(mtcars, wt, qsec, col = factor(cyl))
draw_point(mtcars, "wt", "qsec")
draw_point(mtcars, wt, 'qsec', col = factor(cyl))

# examples that doesn't work
draw_point(mtcars, "wt", "qsec", col = "factor(cyl)")

R Build a customized ggplot within another function with multiple y axis

The error you get is that df does not have a column called y.axis1. The easiest way to refer to the column that have the name that is stored in the variable y.axis1 is to use aes_string() instead of aes(). Also don't set the color in the call to aes()

So change all

   geom_line(aes(y = y.axis1, colour = y.axis1), size = 1)

to

   geom_line(aes_string(y = y.axis1), size = 1,color="red") # Or whatever color you want 

However a better way to solve the problem is to reshape the dataframe to long formating so that all x coordinates lays in one column all y coordinates in one column and the grouping of these in a third column. Your function could then be defined as

graph.date <- function(df,y.axes,x.axis){
index <- which(names(df) %in% y.axes)
plotDF <- gather(df,y.type,y.data,index)

ggplot(plotDF,aes_string(x.axis)) +
geom_line(mapping=aes(y=y.data,color=y.type))
}

Here you will pass a vector of y axes instead of having one parameter for each y axis

One function per facet

Looks like you in fact can facet by the function if you feed each layer its own data with the faceting variable specified:

library(ggplot2) # using ggplot2 3.3.5
ggplot(data.frame(x=0), aes(x)) +
geom_function(fun = sin, data = data.frame(x = -5:5, fun_name = "sin")) +
geom_function(fun = cos, data = data.frame(x = -5:5, fun_name = "cos")) +
facet_wrap(~fun_name)

Sample Image



Related Topics



Leave a reply



Submit