Why does ggplot not plot when storing to variable? - r

I thought this was obvious, but recently I was using the package openair and noticed that when I run the following:
library(openair)
myplot <- windRose(mydata)
the plot myplot is still plotted in the viewer. After looking at the windRose function it is obvious the plot function is being called.
However, why does myggplot <- ggplot(mtcars, aes(cyl, mpg)) + geom_point() not have the same outcome of plotting to the viewer. I am guessing the difference is in how these functions are programmed but I cannot easily identify how ggplot handles the plotting part.

This is not a ggplot-specific behavior, but a more general principle: The R REPL doesn't print assignment statements in general, whereas for expressions, it calls print() or show() on the resulting value, depending on the object type (see Autoprinting section of R Internals for details). For example,1
> 1 + 1 # expression
[1] 2
> x <- 1 + 1 # assignment
>
For a ggplot object, calling print on the object triggers a rendering. So, if you don't assign, it gets rendered. For example,
> ggplot(mtcars, aes(hp, mpg)) + geom_point()
> g <- ggplot(mtcars, aes(hp, mpg)) + geom_point()
>
The plot function, on the other hand, includes rendering as part of it, which is why the other function you called gets rendered despite the assignment.
Note that one can use the invisible function to temporarily set R_Visible to FALSE, which turns off the default behavior of printing expressions, but will still push the evaluated result to .Last.value.
> invisible(1 + 1)
> .Last.value
[1] 2
However, because plot() calls a render to the graphics device as part of its code, invisible() will not stop it from rendering.
> invisible(plot(mtcars$hp, mtcars$mpg))
[1] Credit #Gregor

Related

Function that simply returns the plot

The function I'm asking for is just for convenience during programming.
Adding layers in ggplot2 with the "+" operator is great. Especially adding layers in the middle amounts to just adding another line of code.
However, if I want to try to add a layer after the last line, I have to append a "+" to the last row and if I want to remove this layer again, I also have to remove the "+" again:
ggplot(df, aes(x,y,...)) +
geom_X(...) + # after this line, I can easily add layers
... +
layer_Z(...) # to add a layer after here, I have to modify also this line
I'm searching for a function ggidentity() which just returns the plot itself to use it as a default last line so I can easily add more lines, as in
ggplot(df, aes(x,y,...)) +
geom_X(...) + # after this line, I can easily add layers
... +
layer_Z(...) + # now it's easy to add layers after this line
ggidentity() # this doesn't change anything in the plot
I tried it with a simple function
identity <- function(x) x
which works well with the magrittr-package (and improves my workflow in exploratory data analysis), but not with ggplot2.
I think we need geom_blank(), example:
library(ggplot2) # ggplot2_2.2.1
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
geom_blank() # The blank geom draws nothing

How to add a character string as a code to ggplot object

A part of my code for ggplot is stored in a character vector. I would like to use this code as an additional geoms for my ggplot.
Example1:
DF=data.frame(x=seq(1:10), y=seq(1:20))
a='geom_line()'# This is a string that should be converted to RCode
So far I tried:
ggplot(DF, aes(x,y))+geom_point()+a
Error: Don't know how to add a to a plot
ggplot(DF, aes(x,y))+geom_point()+as.name(a)
Error: Don't know how to add as.name(a) to a plot
ggplot(DF, aes(x,y))+geom_point()+eval(parse(text=a))
Error in geom_line() + geom_line(y = 1) :
non-numeric argument to binary operator
ggplot(DF, aes(x,y))+geom_point()+deparse(substitute(a))
Error: Don't know how to add deparse(substitute(a)) to a plot
Example 2:
DF=data.frame(x=seq(1:10), y=seq(1:20))
a='geom_line()+geom_line(y=1)'
Probable you are wondering, why I would like to do that in a first place? In a for loop, I created expressions and stored them in a list as characters. Later, I pasted together all expressions into a single string expression. Now, I would like to add this string to a ggplot command. Any suggestions?
Edit: Example 1 was successfully solved. But Example 2 stays unsolved.
the parse function has text argument you need to pass a to. Try:
ggplot(DF, aes(x,y)) + geom_point() + eval(parse(text = a))
More info here:
http://adv-r.had.co.nz/Expressions.html#parsing-and-deparsing
In case of multiple statements, it is possible to deparse the original expression, add the new and then evaluate as a whole
original <- deparse(quote(ggplot(DF, aes(x,y)) + geom_point()))
new_call <- paste(original, '+', a)
eval(parse(text = new_call))
You can also use a function to define these code into a list. Please see: https://homepage.divms.uiowa.edu/~luke/classes/STAT4580/dry.html
Here I cited the related code:
Defining a theme_slopegraph function to do the theme adjustment allows
the adjustments to be easily reused:
theme_slopechart = function(toplabels = TRUE) {
thm <- theme(...)
list(thm, ...) # add multiple codes
#...
}
p <- basic_barley_slopes ## from twonum.R
p + theme_slopechart()

R if ggplot not last in function, it won't plot

I discovered that if ggplot is the last thing in a function, then calling the function will result in ggplot creating a plot as expected.
But if ggplot is not the last thing in a function -- say it is followed by an assignment (like x <- t) or a return statement (like return(x)) then ggplot will not create a plot.
What is the work around for this?
P.S. please throw in a comment explaining how to create an inline grey background used to indicate code :-)
Use plot for your ggplot object.
func1 <- function() {
require(ggplot2)
ggplot(mtcars, aes(mpg, wt)) + geom_point()
}
func1() # this creates plot
func2 <- function() {
require(ggplot2)
ggplot(mtcars, aes(mpg, wt)) + geom_point()
cat("hey\n")
}
func2() # this does not create plot
func3 <- function() {
require(ggplot2)
plot(ggplot(mtcars, aes(mpg, wt)) + geom_point())
cat("hey\n")
}
func3() # use plot to make sure your plot is displayed
By the way, func1 creates plot not because ggplot is the last thing to do in the function. It creates plot because the function returns the ggplot object and the code func1() invokes the print method of the object.
To see this, If you do a <- func1(), then the plot is not created, but stored in the a variable.
Note: You can use print insted. print and plot are equivalent for the ggplot object.

Strange behaviour of ggplot2

I simply want to draw multiple arrows on a scatterplot using ggplot2. In this (dummy) example, an arrow is drawn but it moves as i is incremented and only one arrow is drawn. Why does that happen?
library(ggplot2)
a <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
b <- data.frame(x1=c(2,3),y1=c(10,10),x2=c(3,4),y2=c(15,15))
for (i in 1:nrow(b)) {
a <- a + geom_segment(arrow=arrow(),
mapping = aes(x=b[i,1],y=b[i,2],xend=b[i,3],yend=b[i,4]))
plot(a)
}
Thanks.
This isn't strange behavior, this is exactly how aes() is supposed to work. It delays evaluation of the parameters until the plotting actually runs. This is problematic if you include expressions to variable outside your data.frame (like i) and functions (like [,]). These are only evaulated when you actually "draw" the plot.
If you want to force evaulation of your parameters, you can use aes_. This will work for you
for (i in 1:nrow(b)) {
a <- a + geom_segment(arrow=arrow(),
mapping = aes_(x=b[i,1],y=b[i,2],xend=b[i,3],yend=b[i,4]))
}
plot(a)
Now within the loop the parameters for x= and y=, etc are evaluated in the environment and their value are "frozen" in the layer.
Of course, it would be better not to build layers in loops and just procide a proper data object as pointed out in #eipi10's answer.
As #Roland explains in the comment thread to this answer, only one arrow is plotted, because geom_segment(arrow=arrow(), mapping = aes(x=b[i,1],y=b[i,2],xend=b[i,3],yend=b[i,4])) is evaluated only when a is plotted. But i only has one value each time a is plotted. During the first time through the loop, i=1 and during the second time i=2. After the loop i also still equals 2. Thus, only one arrow is plotted each time. If, after the loop, you run i=1:2 then you'll get both arrows. On the other hand, if you change i to anything other than 1 and/or 2, you won't get any arrows plotted.
In any case, you can get both arrows without a loop as follows:
ggplot(mtcars, aes(wt, mpg)) +
geom_point() +
geom_segment(data=b, arrow=arrow(), aes(x=x1,y=y1,xend=x2,yend=y2))
Question regarding #Roland's first comment: Shouldn't the object a be updated each time through the loop by adding the new geom_segment? For example, if I start with the OP's original a, then after one iteration of the loop,
a = a + geom_segment(arrow=arrow(), aes(x=b[1,1],y=b[1,2],xend=b[1,3],yend=b[1,4]))
Then, after two iterations of the loop,
a = a + geom_segment(arrow=arrow(), aes(x=b[1,1],y=b[1,2],xend=b[1,3],yend=b[1,4])) +
geom_segment(arrow=arrow(), aes(x=b[2,1],y=b[2,2],xend=b[2,3],yend=b[2,4]))
where in each case a means the value of a before the start of the loop. Shouldn't those underlying changes to the object a occur regardless of the when or if a is evaluated?

Get current ggplot object

Suppose I plot something like this:
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length)) + geom_point()
Then I realise that I forgot to store the result (i.e. the ggplot object).
How can I retrieve the ggplot object corresponding to the current device?
Is there some ggplot function I can feed cur.dev() into to retrieve the associated plot object, or is it gone forever?
(Note - in this case I could do p <- .Last.value, but let's assume I've typed a few commands since then so that this is not available.
Motivation - adding a hook to knitr to automagically set fig.cap to the title of the plot (if any)).
You are after last_plot
It retrieves the last plot to be modified or created and is used by ggsave
Note that it is the last plot modified or created
set_last_plot is the relevant code (see the source)
It is important note that creating modifying or rendering a ggplot object will set the last plot.
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length)) + geom_point()
f <- last_plot()
# will return the iris plot
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
last_plot()
# will return p
f
last_plot()
# is now f
It will also not count any modifications / manipulation using grid or gridExtra (such as grid.arrange / grid.text
The last object assigned (and it does not need to be a plot object) can be recovered with .Last.value
>require(ggplot2)
#Loading required package: ggplot2
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length)) + geom_point()
gp <- .Last.value
gp
This should return plot objects that have been modified by grid functions as long as there was an assignment. I'm not sure it this is true for actions that were mediated through print calls.

Resources