78

I would like to generate a figure that has a combination of base and ggplot graphics. The following code shows my figure using the base plotting functions of R:

t <- c(1:(24*14)) 
P <- 24 
A <- 10 
y <- A*sin(2*pi*t/P)+20

par(mfrow=c(2,2))
plot(y,type = "l",xlab = "Time (hours)",ylab = "Amplitude",main = "Time series")
acf(y,main = "Autocorrelation",xlab = "Lag (hours)", ylab = "ACF")
spectrum(y,method = "ar",main = "Spectral density function", 
         xlab = "Frequency (cycles per hour)",ylab = "Spectrum")
require(biwavelet)
t1 <- cbind(t, y)
wt.t1=wt(t1)
plot(wt.t1, plot.cb=FALSE, plot.phase=FALSE,main = "Continuous wavelet transform",
     ylab = "Period (hours)",xlab = "Time (hours)")

Which generates enter image description here

Most of these panels look sufficient for me to include in my report. However, the plot showing the autocorrelation needs to be improved. This looks much better by using ggplot:

require(ggplot2)
acz <- acf(y, plot=F)
acd <- data.frame(lag=acz$lag, acf=acz$acf)
ggplot(acd, aes(lag, acf)) + geom_area(fill="grey") +
  geom_hline(yintercept=c(0.05, -0.05), linetype="dashed") +
  theme_bw()

enter image description here

However, seeing as ggplot is not a base graphic, we cannot combine ggplot with layout or par(mfrow). How could I replace the autocorrelation plot generated from the base graphics with the one generated by ggplot? I know I can use grid.arrange if all of my figures were made with ggplot but how do I do this if only one of the plots are generated in ggplot?

MichaelChirico
  • 32,615
  • 13
  • 106
  • 186
KatyB
  • 3,850
  • 7
  • 40
  • 70
  • 3
    it might be almost as easy, and look more consistent, to use `polygon` with the output of `acf()` to construct a base-graphics plot that resembles the `ggplot` one. – Ben Bolker Jan 02 '13 at 15:18
  • 1
    Thanks for our response. This question is really aimed at learning how to combine ggplot and base graphics in a figure window, I realize that there may be more efficient ways of generating the figure shown, but for future purposes I would like to learn the method specified. – KatyB Jan 02 '13 at 15:21
  • 3
    check out the `gridBase` package ... – Ben Bolker Jan 02 '13 at 15:24
  • You might like to take a look at the `gridGraphics` package, which "[Redraws] base graphics as grid graphics". – maj Aug 15 '15 at 19:56
  • Although it is marked as duplicated, this answer worked extremely well for me: http://stackoverflow.com/a/21857177/1436851 – Antoni Mar 09 '16 at 09:54

4 Answers4

56

Using gridBase package, you can do it just by adding 2 lines. I think if you want to do funny plot with the grid you need just to understand and master viewports. It is really the basic object of the grid package.

vps <- baseViewports()
pushViewport(vps$figure) ##   I am in the space of the autocorrelation plot

The baseViewports() function returns a list of three grid viewports. I use here figure Viewport A viewport corresponding to the figure region of the current plot.

Here how it looks the final solution:

enter image description here

library(gridBase)
library(grid)

par(mfrow=c(2, 2))
plot(y,type = "l",xlab = "Time (hours)",ylab = "Amplitude",main = "Time series")
plot(wt.t1, plot.cb=FALSE, plot.phase=FALSE,main = "Continuous wavelet transform",
     ylab = "Period (hours)",xlab = "Time (hours)")
spectrum(y,method = "ar",main = "Spectral density function", 
         xlab = "Frequency (cycles per hour)",ylab = "Spectrum")
## the last one is the current plot
plot.new()              ## suggested by @Josh
vps <- baseViewports()
pushViewport(vps$figure) ##   I am in the space of the autocorrelation plot
vp1 <-plotViewport(c(1.8,1,0,1)) ## create new vp with margins, you play with this values 
require(ggplot2)
acz <- acf(y, plot=F)
acd <- data.frame(lag=acz$lag, acf=acz$acf)
p <- ggplot(acd, aes(lag, acf)) + geom_area(fill="grey") +
  geom_hline(yintercept=c(0.05, -0.05), linetype="dashed") +
  theme_bw()+labs(title= "Autocorrelation\n")+
  ## some setting in the title to get something near to the other plots
  theme(plot.title = element_text(size = rel(1.4),face ='bold'))
print(p,vp = vp1)        ## suggested by @bpatiste
Edward
  • 9,369
  • 2
  • 10
  • 24
agstudy
  • 116,828
  • 17
  • 186
  • 250
  • Yes I have tried this, the problem with this is that the plot generated with ggplot is much bigger than the other panels (as you can see above). Is there a method for changing this? – KatyB Jan 02 '13 at 16:31
  • +1 Very nice. If you replaced the call to `acf(...)` with a call to `plot.new()`, you'd avoid having to call `grid.rect()` to 'white-out' the acf plot. – Josh O'Brien Jan 02 '13 at 17:42
  • 1
    Just b/c I came back and saw that you'd added this much simpler and cleverer answer. – Josh O'Brien Jan 02 '13 at 17:51
  • @JoshO'Brien Thanks a lot! – agstudy Jan 02 '13 at 17:56
  • instead of `grid.draw(ggplotGrob(p))` I'd use the `vp` argument in the `print` method for ggplot objects. – baptiste Jan 02 '13 at 21:52
  • @baptiste thanks! I update the solution , and I remove also the call to the grid.rect as suggested by JoshO'Brien – agstudy Jan 02 '13 at 22:47
  • Thanks. I used it in order to print a few plots to a pdf in different pages. But for some reason the margins get bigger in every new page and at some point plots disappear. How can I correct that? Thanks – AMM Jun 09 '15 at 16:02
16

You can use the print command with a grob and viewport.
First plot your base graphics then add the ggplot

library(grid)

# Let's say that P is your plot
P <- ggplot(acd, # etc... )

# create an apporpriate viewport.  Modify the dimensions and coordinates as needed
vp.BottomRight <- viewport(height=unit(.5, "npc"), width=unit(0.5, "npc"), 
                           just=c("left","top"), 
                           y=0.5, x=0.5)

# plot your base graphics 
par(mfrow=c(2,2))
plot(y,type #etc .... )

# plot the ggplot using the print command
print(P, vp=vp.BottomRight)
Ricardo Saporta
  • 52,793
  • 14
  • 136
  • 168
  • Hello Ricardo. Do you know how to control the widths of the plots with your method ? For instance I want a base graphic and a ggplot2 graphic side by side, but with a larger width for the base graphic. – Stéphane Laurent Jan 16 '13 at 11:27
  • Hi Stéphane, you can accomplish this adjust the settings in the `viewport()` line. Specifically, you would want to adjust `width` and the `y` value, experimenting with different values until you get your desired results. – Ricardo Saporta Jan 18 '13 at 06:01
  • Thank you Ricardo. Actually I have open a question here http://stackoverflow.com/questions/14358526/controlling-column-widths-for-side-by-side-base-graphic-and-ggplot2-graphic/14358752 – Stéphane Laurent Jan 18 '13 at 08:12
9

I'm a fan of the gridGraphics package. For some reason I had trouble with gridBase.

library(ggplot2)
library(gridGraphics)
data.frame(x = 2:10, y = 12:20) -> dat
plot(dat$x, dat$y)
grid.echo()
grid.grab() -> mapgrob
ggplot(data = dat) + geom_point(aes(x = x, y = y)) 
pushViewport(viewport(x = .8, y = .4, height = .2, width = .2))    
grid.draw(mapgrob)

enter image description here

Chris
  • 1,545
  • 12
  • 20
9

cowplot package has recordPlot() function for capturing base R plots so that they can be put together in plot_grid() function.

library(biwavelet)
library(ggplot2)
library(cowplot)
library(gridGraphics)

t <- c(1:(24*14)) 
P <- 24 
A <- 10 
y <- A*sin(2*pi*t/P)+20

plot(y,type = "l",xlab = "Time (hours)",ylab = "Amplitude",main = "Time series")
### record the previous plot
p1 <- recordPlot()  

spectrum(y,method = "ar",main = "Spectral density function", 
         xlab = "Frequency (cycles per hour)",ylab = "Spectrum")
p2 <- recordPlot()

t1 <- cbind(t, y)
wt.t1=wt(t1)
plot(wt.t1, plot.cb=FALSE, plot.phase=FALSE,main = "Continuous wavelet transform",
     ylab = "Period (hours)",xlab = "Time (hours)")
p3 <- recordPlot()

acz <- acf(y, plot=F)
acd <- data.frame(lag=acz$lag, acf=acz$acf)
p4 <- ggplot(acd, aes(lag, acf)) + geom_area(fill="grey") +
  geom_hline(yintercept=c(0.05, -0.05), linetype="dashed") +
  theme_bw()

### combine all plots together
plot_grid(p1, p4, p2, p3,
          labels = 'AUTO',
          hjust = 0, vjust = 1)

Created on 2019-03-17 by the reprex package (v0.2.1.9000)

Edward
  • 9,369
  • 2
  • 10
  • 24
Tung
  • 23,290
  • 6
  • 81
  • 97