I made a self-defined plottest function as this:
plottest<-function(dataframe, var1){
ggplot(dataframe)+geom_point(aes(x=T, y=var1))
}
I wish I could pass a dataframe and a column name to it so I can repeated plot different columns.
df <- data.frame(T=(1:10), y1=(21:30), y2=(51:60), y3 = (61:70))
But when I do:
library(ggplot2)
plottest(df, y1)
Error message shows up saying:object 'var1' not found.
What should I do to make this work??
Try:
df <- data.frame(T=(1:10), y1=(21:30), y2=(51:60), y3 = (61:70))
plottest<-function(dataframe, var1){
ggplot(dataframe, aes_string(x='T', y=var1))+geom_point()
}
plottest(df, 'y1')
It would be even cleaner to fix the abcissa in the function argument as default parameter.
You can make you original function work with a simple added line (and I added the ylab() call to prevent the plot from always using "y" as the ylab.
plottest<-function(dataframe, var1){
y <- substitute(var1)
ggplot(dataframe)+geom_point(aes(x=T, y=y)) + ylab(y)
}
library(ggplot2)
plottest(df, y1)
Related
I am trying to write a function that seems like it should be very simple but I am having problems with it. I want to write a function that takes in three arguements: a dataframe, x-axis variable and y-axis variable. Based on these, I want it to return a scatterplot in which the x-axis variable and y-axis variable can be changed. This is the very basic function I wrote:
scatter_plot <- function(dataframe, x_input, y_input) {
plot <- ggplot(data = dataframe) +
geom_point(mapping = aes(x = x_input, y = y_input),
)
}
For reproducibility, consider the dataset midwest that is in the ggplot2 package. The code I wrote does not produce errors when I run it, but when I try to pass arguments into it, such as
scatter_plot(midwest, percollege, percpovertyknown)
the function returns
"Error in FUN(X[[i]], ...) : object 'percollege' not found"
It seems like it does not recognize the variables in the argument, but I have been playing around with the function for quite some time and I can't seem to figure it out. Can someone help me with how to fix this so my function works correctly?
tidyverse uses non standard evaluation (NSE), which makes using its facilities in functions slightly more complicated than you expect. Here's a version of your function that works for me.
scatter_plot <- function(dataframe, x_input, y_input) {
qX <- enquo(x_input)
qY <- enquo(y_input)
plot <- ggplot(data = dataframe) +
geom_point(mapping = aes(x = !! qX, y = !! qY),
)
return(plot)
}
As you've assigned your plot to an object, I've added a return statement.
See here for more information on NSE.
Using !!rlang::ensym() in your function should work.
scatter_plot <- function(dataframe, x_input, y_input) {
plot <- ggplot(data = dataframe) +
geom_point(mapping = aes(x = !!rlang::ensym(x_input), y = !!rlang::ensym(y_input)))
plot
}
Example
scatter_plot(midwest, percollege, percpovertyknown)
There's some problem when trying to use new functions to analyse a dataset.
I am trying to plot the relationship between hits and runs in the mlb11 dataset by R language.
The function was as
f_plot<-function(x,y,z){
ggplot(x,aes(y,z))+geom_point()+geom_smooth(method="lm")
}
and if I start to plot like this:
f_plot(mlb11,hits, runs)
then it will give :
Error in FUN(X[[i]], ...) : object 'hits' not found
Then if I try this:
f_plot(mlb11,mlb11$hits, mlb11$runs)
It will gives
this output
This fixed the problem!!
But I am very curious about why the function{} cant read the variable names automatically even if we have already designated the dataset "mlb11"?? Would be appreciated to know more about this basic problem!! Thanks!!
The reason is in this line
ggplot(x,aes(y,z))+geom_point()+geom_smooth(method="lm")
it's looking for a variable called y and z in the data x. The "easiest" way to solve this is to do this
f_plot<-function(x,y,z){
yy <- as.character(substitute(y))
zz <- as.character(substitute(z))
code = sprintf('ggplot(x,aes(%s,%s))+geom_point()+geom_smooth(method="lm")', yy, zz)
#print(code)
eval(parse(text = code))
}
f_plot(hehe, a, b)
this will do the trick but is not the best way to write it. The function substitute takes what you passed in to y and make it into an expression, which the cocdes converts into a string/text and then put inside the ggplot which then gets evaluated by eval(parse())
The problem here is that the helper function aes() expects bare variable names as input. This means that when you type aes(y, z), the helper function will not look for the values of the variables "y" and "z" that you supplied to the function f_plot(). Instead, it will look for variables called "y" and "z" in the supplied data frame; independently of the values for y and z that were given to f_plot().
To circumvent this behaviour, you can use aes_(): this variant doesn't treat its inputs as bare variable names, but instead as variables that refer to a bare variable name in the form of an R expression. For this approach to work, you then only have to convert the "y" and "z" inputs of the function f_plot() to R expressions using substitute().
library(ggplot2)
f_plot <- function(x, y, z) {
y <- substitute(y)
z <- substitute(z)
ggplot(x, aes_(y, z)) +
geom_point() +
geom_smooth(method = "lm")
}
df <- data_frame(var1 = 1:10, var2 = 1:10)
f_plot(df, var1, var2)
This should make your function work as intended.
I am trying to write a function that calls ggplot with varying arguments to the aes:
hmean <- function(data, column, Label=label){
ggplot(data,aes(column)) +
geom_histogram() +
facet_wrap(~Antibody,ncol=2) +
ggtitle(paste("Mean Antibody Counts (Log2) for ",Label," stain"))
}
hmean(Log2Means,Primary.Mean, Label="Primary")
Error in eval(expr, envir, enclos) : object 'column' not found
Primary.Mean is the varying argument (I have multiple means). Following various posts here I have tried
passing the column name quoted and unquoted (which yieds either an "unexpected string constant" or the "object not found error)
setting up a local ennvironment (foo <-environment() followed by a environment= arg in ggplot)
creating a new copy of the data set using a data2$column <- data[,column]
None of these appear to work within ggplot. How do I write a function that works?
I will be calling it with different data.frames and columns:
hmean(Log2Means, Primary.mean, Label="Primary")
hmean(Log2Means, Secondary.mean, Label="Secondary")
hmean(SomeOtherFrame, SomeColumn, Label="Pretty Label")
You example is not reproducible, but likely this will work:
hmean <- function(data, column, Label=label){
ggplot(data, do.call("aes", list(y = substitute(column))) ) +
geom_histogram() +
facet_wrap(~Antibody,ncol=2) +
ggtitle(paste("Mean Antibody Counts (Log2) for ",Label," stain"))
}
hmean(Log2Means,Primary.Mean, Label="Primary")
If you need more arguments to aes, do like this:
do.call("aes", list(y = substitute(function_parameter), x = quote(literal_parameter)))
You could try this:
hmean <- function(data, column, Label=label){
# cool trick?
data$pColumn <- data[, column]
ggplot(data,aes(pColumn)) +
geom_histogram() +
facet_wrap(~Antibody,ncol=2) +
ggtitle(paste("Mean Antibody Counts (Log2) for ",Label," stain"))
}
hmean(Log2Means,'Primary.Mean', Label="Primary")
I eventually got it to work with an aes_string() call: aes_string(x=foo, y=y, colour=color), wehre y and color were also defined externally to ggplot().
I have a df with multiple y-series which I want to plot individually, so I wrote a fn that selects one particular series, assigns to a local variable dat, then plots it. However ggplot/geom_step when called inside the fn doesn't treat it properly like a single series. I don't see how this can be a scoping issue, since if dat wasn't visible, surely ggplot would fail?
You can verify the code is correct when executed from the toplevel environment, but not inside the function. This is not a duplicate question. I understand the problem (this is a recurring issue with ggplot), but I've read all the other answers; this is not a duplicate and they do not give the solution.
set.seed(1234)
require(ggplot2)
require(scales)
N = 10
df <- data.frame(x = 1:N,
id_ = c(rep(20,N), rep(25,N), rep(33,N)),
y = c(runif(N, 1.2e6, 2.9e6), runif(N, 5.8e5, 8.9e5) ,runif(N, 2.4e5, 3.3e5)),
row.names=NULL)
plot_series <- function(id_, envir=environment()) {
dat <- subset(df,id_==id_)
p <- ggplot(data=dat, mapping=aes(x,y), color='red') + geom_step()
# Unsuccessfully trying the approach from http://stackoverflow.com/questions/22287498/scoping-of-variables-in-aes-inside-a-function-in-ggplot
p$plot_env <- envir
plot(p)
# Displays wrongly whether we do the plot here inside fn, or return the object to parent environment
return(p)
}
# BAD: doesn't plot geom_step!
plot_series(20)
# GOOD! but what's causing the difference?
ggplot(data=subset(df,id_==20), mapping=aes(x,y), color='red') + geom_step()
#plot_series(25)
#plot_series(33)
This works fine:
plot_series <- function(id_) {
dat <- df[df$id_ == id_,]
p <- ggplot(data=dat, mapping=aes(x,y), color='red') + geom_step()
return(p)
}
print(plot_series(20))
If you simply step through the original function using debug, you'll quickly see that the subset line did not actually subset the data frame at all: it returned all rows!
Why? Because subset uses non-standard evaluation and you used the same name for both the column name and the function argument. As jlhoward demonstrates above, it would have worked (but probably not been advisable) to have simply used different names for the two.
The reason is that subset evaluates with the data frame first. So all it sees in the logical expression is the always true id_ == id_ within that data frame.
One way to think about it is to play dumb (like a computer) and ask yourself when presented with the condition id_ == id_ how do you know what exactly each symbol refers to. It's ambiguous, and subset makes a consistent choice: use what's in the data frame.
Notwithstanding the comments, this works:
plot_series <- function(z, envir=environment()) {
dat <- subset(df,id_==z)
p <- ggplot(data=dat, mapping=aes(x,y), color='red') + geom_step()
p$plot_env <- envir
plot(p)
# Displays wrongly whether we do the plot here inside fn, or return the object to parent environment
return(p)
}
plot_series(20)
The problem seems to be the subset is interpreting id_ on the RHS of the == as identical to the LHS, to this is equivalent to subletting on T, which of course includes all the rows of df. That's the plot you are seeing.
I'm trying to use a local variable in aes when I plot with ggplot. This is my problem boiled down to the essence:
xy <- data.frame(x=1:10,y=1:10)
plotfunc <- function(Data,YMul=2){
ggplot(Data,aes(x=x,y=y*YMul))+geom_line()
}
plotfunc(xy)
This results in the following error:
Error in eval(expr, envir, enclos) : object 'YMul' not found
It seems as if I cannot use local variables (or function arguments) in aes. Could it be that it occurrs due to the content of aes being executed later when the local variable is out of scope? How can I avoid this problem (other than not using the local variable within aes)?
I would capture the local environment,
xy <- data.frame(x=1:10,y=1:10)
plotfunc <- function(Data, YMul = 2){
.e <- environment()
ggplot(Data, aes(x = x, y = y*YMul), environment = .e) + geom_line()
}
plotfunc(xy)
Here's an alternative that allows you to pass in any value through the YMul argument without having to add it to the Data data.frame or to the global environment:
plotfunc <- function(Data, YMul = 2){
eval(substitute(
expr = {
ggplot(Data,aes(x=x,y=y*YMul)) + geom_line()
},
env = list(YMul=YMul)))
}
plotfunc(xy, YMul=100)
To see how this works, try out the following line in isolation:
substitute({ggplot(Data, aes(x=x, y=y*YMul))}, list(YMul=100))
ggplot()'s aes expects YMul to be a variable within the data data frame. Try including YMull there instead:
Thanks to #Justin: ggplot()'s aes seems to look forYMul in the data data frame first, and if not found, then in the global environment. I like to add such variables to the data frame, as follows, as it makes sense to me conceptually. I also don't have to worry about changes to global variables having unexpected consequences to functions. But all of the other answers are also correct. So, use whichever suits you.
require("ggplot2")
xy <- data.frame(x = 1:10, y = 1:10)
xy <- cbind(xy, YMul = 2)
ggplot(xy, aes(x = x, y = y * YMul)) + geom_line()
Or, if you want the function in your example:
plotfunc <- function(Data, YMul = 2)
{
ggplot(cbind(Data, YMul), aes(x = x, y = y * YMul)) + geom_line()
}
plotfunc(xy)
I am using ggplot2, and your example seems to work fine with the current version.
However, it is easy to come up with variants which still create trouble. I was myself confused by similar behavior, and that's how I found this post (top Google result for "ggplot how to evaluate variables when passed"). For example, if we move ggplot out of plotfunc:
xy <- data.frame(x=1:10,y=1:10)
plotfunc <- function(Data,YMul=2){
geom_line(aes(x=x,y=y*YMul))
}
ggplot(xy)+plotfunc(xy)
# Error in eval(expr, envir, enclos) : object 'YMul' not found
In the above variant, "capturing the local environment" is not a solution because ggplot is not called from within the function, and only ggplot has the "environment=" argument.
But there is now a family of functions "aes_", "aes_string", "aes_q" which are like "aes" but capture local variables. If we use "aes_" in the above, we still get an error because now it doesn't know about "x". But it is easy to refer to the data directly, which solves the problem:
plotfunc <- function(Data,YMul=2){
geom_line(aes_(x=Data$x,y=Data$y*YMul))
}
ggplot(xy)+plotfunc(xy)
# works
Have you looked at the solution given by #wch (W. Chang)?
https://github.com/hadley/ggplot2/issues/743
I think it is the better one
essentially is like that of #baptiste but include the reference to the environment directly in the call to ggplot
I report it here
g <- function() {
foo3 <- 4
ggplot(mtcars, aes(x = wt + foo3, y = mpg),
environment = environment()) +
geom_point()
}
g()
# Works
If you execute your code outside of the function it works. And if you execute the code within the function with YMul defined globally, it works. I don't fully understand the inner workings of ggplot but this works...
YMul <- 2
plotfunc <- function(Data){
ggplot(Data,aes(x=x,y=y*YMul))+geom_line()
}
plotfunc(xy)