I would like to create 4 plots which show 4 different conditions in a simulation. The 4 conditions in the simulation are iterated using a for loop. What I would like to do is:
for (cond in 1:4){
1.RUN SIMULATION
2.PLOT RESULTS
}
In the end I would like to have 4 plots arranged on a grid. With plot() I can just use par(mfrow) and the plots would be added automatically. Is there a way to do the same with ggplot?
I am aware that I could use grid.arrange() but that would require storing the plots in separate objects, plot1...plot5. But its not possible to do:
for (cond in 1:4){
1. run simulation
2. plot[cond]<-ggplot(...)
}
I cannot give separate names to the plots, like plot1, plot2, plot3 within the loop.
You could use gridExtra package:
library(gridExtra)
library(ggplot2)
p <- list()
for(i in 1:4){
p[[i]] <- ggplot(YOUR DATA, ETC.)
}
do.call(grid.arrange,p)
I would use facetting in this case. In my experience, explicitly arranging sub-plots is rarely needed in ggplot2. A mockup example will probably illustrate my point better:
run_model = function(id) {
data.frame(x_values = 1:1000,
y_values = runif(1000),
id = sprintf('Plot %d', id))
}
df = do.call('rbind', lapply(1:4, run_model))
head(df)
x_values y_values id
1 1 0.7000696 Plot 1
2 2 0.3992786 Plot 1
3 3 0.2718229 Plot 1
4 4 0.4049928 Plot 1
5 5 0.4158864 Plot 1
6 6 0.1457746 Plot 1
Here, id is the column to specifies to which model run a value belongs. Plotting it can simply be done using:
library(ggplot2)
ggplot(df, aes(x = x_values, y = y_values)) + geom_point() + facet_wrap(~ id)
Another option is to use multiplot function:
library(ggplot2)
p <- list()
for(i in 1:4){
p[[i]] <- ggplot(YOUR DATA, ETC.)
}
do.call(multiplot,p)
More information about that - http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_%28ggplot2%29/
Related
I am trying to construct a list of ggplot graphics, which will be plotted later. What I have so far, using Anscombe's quartet for an example, is:
library(ggplot2)
library(gridExtra)
base <- ggplot() + xlim(4,19)
plots = vector(mode = "list", length = 4)
for(i in 1:4) {
x <- anscombe[,i]
y <- anscombe[,i+4]
p <- geom_point(aes(x,y),colour="blue")
q <- geom_smooth(aes(x,y),method="lm",colour="red",fullrange=T)
plots[[i]] <- base+p+q
}
grid.arrange(grobs = plots,ncol=2)
As I travel through the loop, I want the current values of the plots p and q to be added with the base plot, into the i-th value of the list. That is, so that list element number i contains the plots relating to the i-th x and y columns from the dataset.
However, what happens is that the last plot only is drawn, four times. I've done something very similar with base R, using mfrow, plot and abline, so that I believe my logic is correct, but my implementation isn't. I suspect that the issue is with these lines:
plots = vector(mode = "list", length = 4)
plots[[i]] <- base+p+q
How can I create a list of ggplot graphics; starting with an empty list?
(If this is a trivial and stupid question, I apologise. I am very new both to R and to the Grammar of Graphics.)
The code works properly if lapply() is used instead of a for loop.
plots <- lapply(1:4, function(i) {
# create plot number i
})
The reason for this issue is that ggplot uses lazy evaluation. By the time the plots are rendered, the loop already iterated to i=4 and the last plot will be displayed four times.
Full working example:
library(ggplot2)
library(gridExtra)
base <- ggplot() + xlim(4,19)
plots <- lapply(1:4, function(i) {
x <- anscombe[,i]
y <- anscombe[,i+4]
p <- geom_point(aes(x,y),colour="blue")
q <- geom_smooth(aes(x,y),method="lm",colour="red",fullrange=T)
base+p+q
})
grid.arrange(grobs = plots,ncol=2)
To force evaluation, there's a simple solution, change aes(...) into aes_(...) and your code works.
library(ggplot2)
library(gridExtra)
base <- ggplot() + xlim(4,19)
plots <- lapply(1:4, function(i) {
x <- anscombe[,i]
y <- anscombe[,i+4]
p <- geom_point(aes_(x,y),colour="blue")
q <- geom_smooth(aes_(x,y),method="lm",colour="red",fullrange=T)
base+p+q
})
grid.arrange(grobs = plots,ncol=2)
I have five graphs plotted, each with one slight variable change, the randmod function as seen below.
library(spatstat)
library(ggplot2)
library(dplyr)
library(ggpubr)
library(tidyr)
set.seed(4)
dim <- 2000
radiusCluster<-100
lambdaParent<-.02
lambdaDaughter<-30
hosts<-900
randmod<-0 #this is the variable that changes
delta.t <- 1
iterations <- 1000
sigma <- 0.1
beta <- 1
theta <- 10
b <- .4
numbparents<-rpois(1,lambdaParent*dim)
xxParent<-runif(numbparents,0+radiusCluster,dim-radiusCluster)
yyParent<-runif(numbparents,0+radiusCluster,dim-radiusCluster)
numbdaughter<-rpois(numbparents,(lambdaDaughter))
sumdaughter<-sum(numbdaughter)
theta<-2*pi*runif(sumdaughter)
rho<-radiusCluster*sqrt(runif(sumdaughter))
xx0=rho*cos(theta)
yy0=rho*sin(theta)
xx<-rep(xxParent,numbdaughter)
yy<-rep(yyParent,numbdaughter)
xx<-xx+xx0
yy<-yy+yy0
cds<-data.frame(xx,yy)
is_outlier<-function(x){
x > dim| x < 0
}
cds<-cds[!(is_outlier(cds$xx)|is_outlier(cds$yy)),]
sampleselect<-sample(1:nrow(cds),hosts,replace=F)
cds<-cds%>%slice(sampleselect)
randfunction<-function(x){
x<-runif(length(x),0,dim)
}
randselect<-sample(1:nrow(cds),floor(hosts*randmod),replace=F)
cds[randselect,]<-apply(cds[randselect,],1,randfunction)
landscape<-ppp(x=cds$xx,y=cds$yy,window=owin(xrange=c(0,dim),yrange=c(0,dim)))
plot1<-ggplot(data.frame(landscape))+geom_point(aes(x=x,y=y))+coord_equal()+theme_minimal()+ggtitle("Rf=0")
plot1
This produces a graph identical to this:
I repeat this process for 4 other values of randmod, i.e.:
set.seed(4)
dim <- 2000
radiusCluster<-100
lambdaParent<-.02
lambdaDaughter<-30
hosts<-900
randmod<-0.25 #change in randmod
delta.t <- 1
iterations <- 1000
sigma <- 0.1
beta <- 1
theta <- 10
b <- .4
numbparents<-rpois(1,lambdaParent*dim)
xxParent<-runif(numbparents,0+radiusCluster,dim-radiusCluster)
yyParent<-runif(numbparents,0+radiusCluster,dim-radiusCluster)
numbdaughter<-rpois(numbparents,(lambdaDaughter))
sumdaughter<-sum(numbdaughter)
theta<-2*pi*runif(sumdaughter)
rho<-radiusCluster*sqrt(runif(sumdaughter))
xx0=rho*cos(theta)
yy0=rho*sin(theta)
xx<-rep(xxParent,numbdaughter)
yy<-rep(yyParent,numbdaughter)
xx<-xx+xx0
yy<-yy+yy0
cds<-data.frame(xx,yy)
is_outlier<-function(x){
x > dim| x < 0
}
cds<-cds[!(is_outlier(cds$xx)|is_outlier(cds$yy)),]
sampleselect<-sample(1:nrow(cds),hosts,replace=F)
cds<-cds%>%slice(sampleselect)
randfunction<-function(x){
x<-runif(length(x),0,dim)
}
randselect<-sample(1:nrow(cds),floor(hosts*randmod),replace=F)
cds[randselect,]<-apply(cds[randselect,],1,randfunction)
landscape<-ppp(x=cds$xx,y=cds$yy,window=owin(xrange=c(0,dim),yrange=c(0,dim)))
plot2<-ggplot(data.frame(landscape))+geom_point(aes(x=x,y=y))+coord_equal()+theme_minimal()+ggtitle("Rf=0.25")
plot2
Producing the graph below:
My problem is this, when I use ggarrange, the graphs become squished together and very unclear.
ggarrange(plot1,plot2,plot3,plot4,plot5,nrow=3,ncol=2)
I've tried other packages such as "egg" and "cowplot" to produce a graph that is at least reasonable in the plotting frame but without success. I have also tried:
ggsave("arrange.png", arrangeGrob(grobs = l))
But this also produces the same squished plot. Is it possible to either increase the scale of the plots within the equivalent of ggarrange, or possibly save the plots to a separate file that will maintain their original size?
I need to present this information clearly so that is why the graph as it stands is unacceptable.
Try with patchwork:
library(patchwork)
#Code
G <- wrap_plots(list(plot1,plot2,plot3,plot4,plot5),nrow=3,ncol=2)
Output:
I am saving multiple ggplots to a list to be used in a subsequent multiplot. The plots are generated in a loop and appended to the list, however, after the loop all plot objects in the list are the same as the last plot of the loop. I have done the type of operation before, without any issues. Has anyone experienced the same, and solved the problem?
figList <- list()
aoinum <- 1
for (aoi in AOI_list){
...
# prepare dataframe for plotting
dat <- data.frame(...)
fig <- ggplot(data=dat, aes(x=x, y=y, fill=z, alpha=q)) +
geom_bar(...)+
...
figList[[aoi]] <- fig
aoinum = aoinum + 1
}
This is how I managed to make a list of plots in a for loop
#Define list
ggcluster<-list()
for (cluster in 1:nclusters){
# Simple plot )geom_polygon in my case)
ggcluster[[cluster]]<-ggplot() +
geom_polygon(data = datoshp.df, aes(long, lat, group = group))
}
# Build multiplot panel (two columns)
pngname<-paste(output_path,"plot-name",".png",sep="")
png(pngname,width = 1000, height = 1000)
do.call(grid.arrange, c(ggcluster,list(ncol=2)))
dev.off()
Huge fan of facet plots in ggplot2. However, sometimes I have too many subplots and it'd be nice to break them up into a list of plots. For example
df <- data.frame(x=seq(1,24,1), y=seq(1,24,1), z=rep(seq(1,12),each=2))
df
x y z
1 1 1 1
2 2 2 1
3 3 3 2
4 4 4 2
5 5 5 3
. . . .
. . . .
myplot <- ggplot(df,aes(x=x, y=y))+geom_point()+facet_wrap(~z)
myplot
How would I write a function to take the resulting plot and split it into a list of plots? Something along these lines
splitFacet <- function(subsPerPlot){
# Method to break a single facet plot into a list of facet plots, each with at most `subsPerPlot` subplots
# code...
return(listOfPlots)
}
Split plot into individual plots
We build a function along these steps :
We go through the structure of the object to get the names of the variables used for faceting (here 'z').
We overwrite the facet element of our plot object with the one from the empty ggplot object (so if we print it at this stage facets are gone).
We extract the data and split it along the variables we identified in 1st step.
We overwrite the original data with each subset (12 times here) and store all outputs in a list.
code
splitFacet <- function(x){
facet_vars <- names(x$facet$params$facets) # 1
x$facet <- ggplot2::ggplot()$facet # 2
datasets <- split(x$data, x$data[facet_vars]) # 3
new_plots <- lapply(datasets,function(new_data) { # 4
x$data <- new_data
x})
}
new_plots <- splitFacet(myplot)
length(new_plots) # [1] 12
new_plots[[3]] # 3rd plot
Split plot into faceted plots of n subplots max
If we want to keep the facets but have less plots by facet we can skip step 2, and rework our split instead so it includes several values of the variables used for faceting.
Rather than making a separate function we'll generalize the 1st, n is the number of facets you get by plot.
n = NULL means you get the previous output, which is slightly different from n = 1 (one facet by plot).
splitFacet <- function(x, n = NULL){
facet_vars <- names(x$facet$params$facets) # 1
if(is.null(n)){
x$facet <- ggplot2::ggplot()$facet # 2a
datasets <- split(x$data, x$data[facet_vars]) # 3a
} else {
inter0 <- interaction(x$data[facet_vars], drop = TRUE) # 2b
inter <- ceiling(as.numeric(inter0)/n)
datasets <- split(x$data, inter) # 3b
}
new_plots <- lapply(datasets,function(new_data) { # 4
x$data <- new_data
x})
}
new_plots2 <- splitFacet(myplot,4)
length(new_plots2) # [1] 3
new_plots2[[2]]
This might come in handy too :
unfacet <- function(x){
x$facet <- ggplot2::ggplot()$facet
x
}
The tidy way
If the code is available, no need to go through all this trouble, we can split the data before feeding it to ggplot :
library(tidyverse)
myplots3 <-
df %>%
split(ceiling(group_indices(.,z)/n_facets)) %>%
map(~ggplot(.,aes(x =x, y=y))+geom_point()+facet_wrap(~z))
myplots3[[3]]
While I was looking for a solution for this I can across ggplus. Specifically the function facet_multiple:
https://github.com/guiastrennec/ggplus
It lets you split a facet over a number of pages by specifying the amount of plots you want per page. In your example it would be:
library(ggplus)
df <- data.frame(x=seq(1,24,1), y=seq(1,24,1), z=rep(seq(1,12),each=2))
myplot <- ggplot(df,aes(x=x, y=y))+geom_point()
facet_multiple(plot = myplot, facets = 'z', ncol = 2, nrow = 2)
Is this the sort of thing you need? It worked a treat for me.
This is similar to Moody_Muddskipper's answer, but works with any type of faceting (facet_grid or facet_wrap), handles arbitrary expressions in facets, and doesn't draw facet strip bars.
library(rlang)
library(ggplot2)
split_facets <- function(x) {
facet_expr <- unlist(x[["facet"]][["params"]][c("cols", "rows", "facets")])
facet_levels <- lapply(facet_expr, rlang::eval_tidy, data = x[["data"]])
facet_id <- do.call(interaction, facet_levels)
panel_data <- split(x[["data"]], facet_id)
plots <- vector("list", length(panel_data))
for (ii in seq_along(plots)) {
plots[[ii]] <- x
plots[[ii]][["data"]] <- panel_data[[ii]]
plots[[ii]][["facet"]] <- facet_null()
}
plots
}
split_facets(ggplot(df,aes(x=x, y=y))+geom_point()+facet_wrap(~z))
split_facets(ggplot(df,aes(x=x, y=y))+geom_point()+facet_grid(z %% 2 ~ z %% 5))
It uses rlang::eval_tidy to evaluate the facet expressions, combines them into a single categorical factor, then uses that to split the data. It also "suppresses" each subplot's faceting part by replacing it with facet_null().
Posting this for anyone wanting to use ggplus. ggplus will work with later versions of R, but you need to install it using the developer's directions, i.e.
devtools::install_github("guiastrennec/ggplus")
I ran into the same issue when trying to install it using RStudio, then realized that it's just not one of the "standard packages." I'm using 3.4.4.
I would like to create 3 plots each containing a plot of 2 lines from different data frames, and then label each plot with a specific fraction.
So for example I have the 3 data frames:
df1 <- data.frame(x=c(1,2,3,4),y=c(2,3,4,5), z=c(3,3,6,8))
df2 <- data.frame(x=c(3,4,5,6),y=c(1,3,6,7), z=c(2,4,4,8))
df3 <- data.frame(x=c(1,2,2,3),y=c(2,5,6,9), z=c(2,5,6,7))
And I would like to:
1) Create 3 different plots for each data frame, each with one red and one blue line;
2) Add an annotation over the blue line of each plot using a different fraction for each plot.
For example the plot for data frame 1 is something like this:
p1 <- ggplot(data = df1) + geom_line(aes(x=x,y=y, colour="blue")) + geom_line(aes(x=x,y=z, colour="red")) + scale_colour_manual(name="data", values=c("red", "blue"))
Then to add the labels over the blue line I have tried:
p1 + geom_text(aes(x=df1$x[which.max(df1$y)]+1, y = max(df1$y)+4, label = "{\frac{23 22 22}{44 28 32}}", size=2, parse=TRUE))
But this does not work, and I have searched so many hours and cannot find how to use fractions (and brackets enclosing the fraction) in the annotations. Any help is deeply appreciated!
-fra
It is not clear what do you want to have. This is an attempt;
I use mapply to loop over plots and fractions and generate a list of plots.
I create fractions using frac(x,y)
I set limits of plots using scale_y_continuous
I use gridExtra to arrange plots in the same plot (optional)
Here the complete code:
## a generic function that take a fraction ana a data.frame as inputs
## it generate a plot
plot.frac <- function(dat,frac){
p <- ggplot(dat) +
geom_line(aes(x=x,y=y, colour="blue")) +
geom_line(aes(x=x,y=z, colour="red")) +
scale_colour_manual(name="data", values=c("red", "blue"))+
geom_text(x=dat$x[which.max(dat$y)]-0.05, y = max(dat$y)+4,
label = frac, size=5,parse=TRUE)+
## Note the use of limits here to display the annotation
scale_y_continuous(limits = c(min(dat$y), max(dat$y)+5))
p
}
## create a list of data.frame of mapply
df.list <- list(df1,df2,df3)
## ggplot2 use plotmath so for fraction you use frac(x,y)
## here I construct the 2 terms using paste
frac.func <- function(num,den) paste('frac("',num,'","',den,'")',sep='')
num1 <- "line1:23 22 22"
den1 <- "line2: 44 28 32"
num2 <- "line1:23 50 22"
den2 <- "line2: 44 50 32"
num3 <- "line1:23 80 22"
den3 <- "line2: 44 80 32"
## create a list of fractions for mapply
frac.list <- list(frac.func(num1,den1),
frac.func(num2,den2),
frac.func(num3,den3))
frac.list <- list(frac,frac,frac)
## use mapply to call the plot over the 2 lists of data.frame and fractions
ll <- mapply(plot.frac,df.list,frac.list,SIMPLIFY=FALSE)
library(gridExtra)
do.call(grid.arrange,ll)