How to get plots in several pdf pages using ggplot2 - r

I need help to process graphs into multiple pdf pages. Here is my current code:
file <- read.csv(file="file.csv")
library(ggplot2)
library(gridExtra)
library(plyr)
gg1 <- ggplot() +
geom_line(aes(x=TIME, y=var1, colour = "z1"), file) +
geom_line(aes(x=TIME, y=var2, colour = "z2"), file) +
geom_point(aes(x=TIME, y=var3), file) + facet_wrap( ~ ID, ncol=5)+
xlab("x") +
ylab("Y") +
ggtitle(" x ") + scale_colour_manual(name="Legend",
values=c(z1="red", z2 ="blue")) + theme(legend.position="bottom")
gg10 = do.call(marrangeGrob, c(gg1, list(nrow=4, ncol=4)))
ggsave("need10.pdf", gg10)
Here is the image created, without splitting my images
I wish to have a code to get my plots in a 4 by 4 layout in multiple pages. The last two lines of my code need adjustment and I do not know how to fix it myself.

The ggplus wrapper appears to do what you want. I changed a couple of things in the code block below from your original: facet_wrap is commented out, and file is moved to ggplot so that it doesn't have to be re-specified in each geom_*:
gg1 <- ggplot(file) +
geom_line(aes(x=TIME, y=var1, colour = "z1")) +
geom_line(aes(x=TIME, y=var2, colour = "z2")) +
geom_point(aes(x=TIME, y=var3)) +
# facet_wrap( ~ ID, ncol=5) +
xlab("x") +
ylab("Y") +
ggtitle(" x ") +
scale_colour_manual(name="Legend",
values=c(z1="red", z2 ="blue"),
labels=c("X","Y")) +
theme(legend.position="bottom")
devtools::install_github("guiastrennec/ggplus")
library(ggplus)
pdf("need10.pdf")
gg10 <- facet_multiple(plot=gg1, facets="ID", ncol = 4, nrow = 4)
dev.off()

4 years later...
since ggplus is deprecated, you might consider using ggforce. You could use any of the relevant facet_* options as described in the documentation. E.g., facet_matrix:
# Standard use:
ggplot(mpg) +
geom_point(aes(x = .panel_x, y = .panel_y)) +
facet_matrix(vars(displ, cty, hwy))

Related

Control size of subplots created with patchwork - code improvement

I want to make a multipanel figure containing multiple labeled plots. My plots are produced in ggplot2 & I would like to arrange them with patchwork.
I want to combine two subplots in the first row, followed by two other plots arranged one plot per row:
plot3 + plot4 - both in row #1
plot1 - in row #2
plot2 - in row #3
Here is a dummy example to illustrate the problem:
```{r, fig.width=10, fig.height=13}
library(ggplot2)
library(patchwork)
#Dummy plots
plot1 <- ggplot2::ggplot(data = mpg, aes(x = class, fill=drv)) +
geom_bar(aes(y = ..count..)) + ggplot2::ggtitle("Plot1")
plot2 <- ggplot2::ggplot(data = mpg, aes(x = displ, y = hwy, color=class)) +
geom_point()+ ggplot2::ggtitle("Plot2")
plot3 <- ggplot2::ggplot(data = mpg, aes(x = cty)) +
geom_density()+ ggplot2::ggtitle("Plot3")
plot4 <- ggplot2::ggplot(data = mpg, aes(x = cty, y=drv, fill = fl)) +
geom_col()+ ggplot2::ggtitle("Plot4")
# this works, but it is not the desired layout
final <- plot1 + plot2 + {plot3 + plot4 + patchwork::plot_layout(ncol=2)} +
patchwork::plot_layout(ncol=1,heights = unit(c(4, 7, 2),c('cm')))
plot(final)
#this does not work
final2 <- {plot3 + plot4 + patchwork::plot_layout(ncol=2)} + plot1 + plot2 +
patchwork::plot_layout(ncol=1, heights = unit(c(2, 4, 7),c('cm')))
print(final2)
```
This is the output I can produce, but this is not what I want:
And this is the picture I would like to obtain:
Some of my other attempts:
#this does not work either
up_patch <- plot3 + plot4 + patchwork::plot_layout(ncol=2)
final2 <- up_patch + plot1 + plot2 + patchwork::plot_layout(ncol=1, heights = unit(c(2, 4, 7),c('cm')))
print(final2)
#and this as well
up_patch <- plot3 + plot4 + patchwork::plot_layout(ncol=2, heights= unit(2,c('cm')))
bottom_patch <- plot1 + plot2 + patchwork::plot_layout(ncol=1, heights = unit(c(4, 7),c('cm')))
final2 <- up_patch + bottom_patch
print(final2)
# THIS WORKS but needs improvement
final_desired <- (plot3 | plot4) / plot1 + plot2
print(final_desired)
In the last attempt I was able to produce the desired layout, however I would like to be able to control the dimensions of the subplots as in my dummy example in the beginning of this post). It is important for me to adjust the image size to the size of the page.
I would also like to know how to use a namespace qualifier while calling patchwork in the working example, so I would not call a function from another package by an accident.
I followed the instructions from these sources:
https://ggplot2-book.org/arranging-plots.html
https://patchwork.data-imaginist.com/articles/guides/layout.html - (footnote: I do not understand the textual representation of layout)
Combine multiple patchworks
One option would be to use the design argument to specify the layout:
library(ggplot2)
library(patchwork)
design = "
CD
AA
BB
"
plot1 + plot2 + plot3 + plot4 +
plot_layout(
design = design,
heights = c(2, 4, 7))

Variable geom_text is overwritten when plots saved in list [duplicate]

This question already has an answer here:
List for Multiple Plots from Loop (ggplot2) - List elements being overwritten
(1 answer)
Closed 2 years ago.
I am trying to organize several dozens of plots using ggarrange, so I have setup a loop in which I save each plot in a list. Each plot differs from each other with different data, title, etc. Everything works perfectly until I try to use geom_text to place some text inside the plot. When the plots are saved in the list, each plot inherits the geom_text from the last plot in the list. I don't know how to avoid this.
my.list=vector("list", length = 2);
dt=data.table(x=c(1,100,100000),y=c(1,100,100000))
plotname=c('first','second')
for (i in 1:length(my.list)) {
my.list[[i]]=ggplot(data = dt, aes(x = x, y = y )) + geom_point(size=1.5,aes(color=c('red'))) + labs(x=NULL, y=NULL)
+ scale_color_manual(values='red')
+ theme_bw() + theme(panel.background = element_rect(fill='light grey', colour='black'),legend.position = "none")
+ geom_text(inherit.aes=FALSE,aes(x=500, y=100000, label=paste0('NRMSE:',i))) + ggtitle(paste0(plotname[i])) + coord_equal()
+ geom_abline(slope=1)
+ scale_y_log10(breaks = c(1,10,100,1000,10000,100000),limits=c(1,100000))
+ scale_x_log10(breaks = c(1,10,100,1000,10000,1000000),limits=c(1,100000))
+ labs(x=NULL, y=NULL)
+ theme_bw() + theme(panel.background = element_rect(fill='light grey', colour='black'),legend.position = "none")
}
after this I do
plotosave=ggarrange(plotlist=my.list)
Using lapply instead of forloop works fine:
my.list <- lapply(1:2, function(i) {
ggplot(data = dt, aes(x = x, y = y )) +
geom_point(size=1.5) +
labs(x=NULL, y=NULL) +
theme_bw() +
theme(panel.background = element_rect(fill='light grey', colour='black'),
legend.position = "none") +
geom_text(inherit.aes=FALSE,aes(x=50000, y=100000,
label=paste0('NRMSE:',i))) +
ggtitle(paste0(plotname[i]))
})
ggarrange(plotlist = my.list)
Note: the issue is not with ggarrange.
Roland:
The plot is build when you print the ggplot object. Anything that is not part of the data passed will be taken from the enclosing environment at exactly that time point. If you use the iterator of a for loop in the plot, it has its last value then (or any value you change it to later on). lapply avoids the issue because of the stuff explained in the Note in its documentation.
Related post:
the problem is that ggplot() waits until you print the plot to resolve the variables in the aes() command.
I don't exactly know why this occurs but if you remove aes from geom_text it works.
library(ggplot2)
my.list = vector("list", length = 2)
dt = data.table::data.table(x=c(1,100,100000),y=c(1,100,100000))
plotname = c('first','second')
for (i in 1:length(my.list)) {
my.list[[i]]= ggplot(data = dt, aes(x = x, y = y )) +
geom_point(size=1.5) +
labs(x=NULL, y=NULL) +
theme_bw() +
theme(panel.background = element_rect(fill='light grey', colour='black'),
legend.position = "none") +
geom_text(x=50000, y=100000, label=paste0('NRMSE:',i)) +
ggtitle(paste0(plotname[i]))
}
plotosave = ggpubr::ggarrange(plotlist=my.list)

Annotate x-axis with N in faceted plot

I'm trying to produce a boxplot of some numeric outcome broken down by treatment condition and visit number, with the number of observations in each box placed under the plot, and the visit numbers labeled as well. Here's some fake data that will serve to illustrate, and I give two examples of things I've tried that didn't quite work.
library(ggplot2)
library(plyr)
trt <- factor(rep(LETTERS[1:2],150),ordered=TRUE)
vis <- factor(c(rep(1,150),rep(2,100),rep(3,50)),ordered=TRUE)
val <- rnorm(300)
data <- data.frame(trt,vis,val)
data.sum <- ddply(data, .(vis, trt), summarise,
N=length(na.omit(val)))
mytheme <- theme_bw() + theme(panel.margin = unit(0, "lines"), strip.background = element_blank())
The below code produces a plot that has N labels where I want them. It does this by grabbing summary data from an auxiliary dataset I created. However, I couldn't figure out how to also label visit on the x-axis (ideally, below the individual box labels), or to delineate visits visually in other ways (e.g. lines separating them into panels).
plot1 <- ggplot(data) +
geom_boxplot(aes(x=vis:trt,y=val,group=vis:trt,colour=trt), show.legend=FALSE) +
scale_x_discrete(labels=paste(data.sum$trt,data.sum$N,sep="\n")) +
labs(x="Visit") + mytheme
The plot below is closer to what I want than the one above, in that it has a nice hierarchy of treatments and visits, and a pretty format delineating the visits. However, for each panel it grabs the Ns from the first row in the summary data that matches the treatment condition, because it doesn't "know" that each facet needs to use the row corresponding to that visit.
plot2 <- ggplot(data) + geom_boxplot(aes(x=trt,y=val,group=trt,colour=trt), show.legend=FALSE) +
facet_wrap(~ vis, drop=FALSE, switch="x", nrow=1) +
scale_x_discrete(labels=paste(data.sum$trt,data.sum$N,sep="\n")) +
labs(x="Visit") + mytheme
One workaround is to manipulate your dataset so your x variable is the interaction between trt and N.
Working off what you already have, you can add N to the original dataset via a merge.
test = merge(data, data.sum)
Then make a new variable that is the combination of trt and N.
test = transform(test, trt2 = paste(trt, N, sep = "\n"))
Now make the plot, using the new trt2 variable on the x axis and using scales = "free_x" in facet_wrap to allow for the different labels per facet.
ggplot(test) +
geom_boxplot(aes(x = trt2, y = val, group = trt, colour = trt), show.legend = FALSE) +
facet_wrap(~ vis, drop = FALSE, switch="x", nrow = 1, scales = "free_x") +
labs(x="Visit") +
mytheme
Since this functionality isn't built in a good work-around is grid.extra:
library(gridExtra)
p1 <- ggplot(data[data$vis==1,]) + geom_boxplot(aes(x=trt,y=val,group=trt,colour=trt), show.legend=FALSE) +
#facet_wrap(~ vis, drop=FALSE, switch="x", nrow=1) +
scale_x_discrete(labels=lb[1:2]) + #paste(data.sum$trt,data.sum$N,sep="\n")
labs(x="Visit") + mytheme
p2 <- ggplot(data[data$vis==2,]) + geom_boxplot(aes(x=trt,y=val,group=trt,colour=trt), show.legend=FALSE) +
#facet_wrap(~ vis, drop=FALSE, switch="x", nrow=1) +
scale_x_discrete(labels=lb[3:4]) + #paste(data.sum$trt,data.sum$N,sep="\n")
labs(x="Visit") + mytheme
p3 <- ggplot(data[data$vis==3,]) + geom_boxplot(aes(x=trt,y=val,group=trt,colour=trt), show.legend=FALSE) +
#facet_wrap(~ vis, drop=FALSE, switch="x", nrow=1) +
scale_x_discrete(labels=lb[5:6]) + #paste(data.sum$trt,data.sum$N,sep="\n")
labs(x="Visit") + mytheme
grid.arrange(p1,p2,p3,nrow=1,ncol=3) # fully customizable
Related:
Varying axis labels formatter per facet in ggplot/R
You can also make them vertical or do other transformations:

Varying factor order in each facet of ggplot2

I am trying to create a Cleveland Dot Plot given for two categories in this case J and K. The problem is the elements A,B,C are in both categories so R keeps farting. I have made a simple example:
x <- c(LETTERS[1:10],LETTERS[1:3],LETTERS[11:17])
type <- c(rep("J",10),rep("K",10))
y <- rnorm(n=20,10,2)
data <- data.frame(x,y,type)
data
data$type <- as.factor(data$type)
nameorder <- data$x[order(data$type,data$y)]
data$x <- factor(data$x,levels=nameorder)
ggplot(data, aes(x=y, y=x)) +
geom_segment(aes(yend=x), xend=0, colour="grey50") +
geom_point(size=3, aes(colour=type)) +
scale_colour_brewer(palette="Set1", limits=c("J","K"), guide=FALSE) +
theme_bw() +
theme(panel.grid.major.y = element_blank()) +
facet_grid(type ~ ., scales="free_y", space="free_y")
Ideally, I would want a dot plot for both categories(J,K) individually with each factor(vector x) decreasing with respect to the y vector. What ends up happening is that both categories aren't going from biggest to smallest and are erratic at the end instead. Please help!
Unfortunately factors can only have one set of levels. The only way i've found to do this is actually to create two separate data.frames from your data and re-level the factor in each. For example
data <- data.frame(
x = c(LETTERS[1:10],LETTERS[1:3],LETTERS[11:17]),
y = rnorm(n=20,10,2),
type= c(rep("J",10),rep("K",10))
)
data$type <- as.factor(data$type)
J<-subset(data, type=="J")
J$x <- reorder(J$x, J$y, max)
K<-subset(data, type=="K")
K$x <- reorder(K$x, K$y, max)
Now we can plot them with
ggplot(mapping = aes(x=y, y=x, xend=0, yend=x)) +
geom_segment(data=J, colour="grey50") +
geom_point(data=J, size=3, aes(colour=type)) +
geom_segment(data=K, colour="grey50") +
geom_point(data=K, size=3, aes(colour=type)) +
theme_bw() +
theme(panel.grid.major.y = element_blank()) +
facet_grid(type ~ ., scales="free_y", space="free_y")
which results in

Modify x-axis labels in each facet

I have this chart - I would like to add to each label the text N=xx to denote the number of observations. I know how to do this and I have done that on charts with no facets.
When I tried that on the faceted chart it did not work, (I got the same N on the open tick on all 3 charts, the same N on the Restricted, etc.)
I hope someone can point the way to a solution, how do I control the elements on a given facet?
library(ggplot2)
library(scales)
stat_sum_single <- function(fun, geom="point", ...) {
stat_summary(fun.y=fun, fill="red", geom=geom, size = 5, shape=24)
}
set.seed(1)
data1 <- data.frame(Physicians_In=sample(1:3,100,replace=T),Physicians_Out=sample(1:3,100,replace=T),share=runif(100,0,1))
data1$Physicians_In <- factor(data1$Physicians_In,levels=c(1,2,3),labels=c("Open","Restricted","Closed"))
data1$Physicians_Out <- factor(data1$Physicians_Out,levels=c(1,2,3),labels=c("Open","Restricted","Closed"))
access_ch3 <- ggplot(data1,aes(x=Physicians_In,y=share,fill=Physicians_In))+geom_boxplot()+stat_sum_single(mean)
access_ch3 <- access_ch3 +geom_jitter(position = position_jitter(width = .2),color="blue")+theme_bw()
access_ch3 <- access_ch3 + theme(legend.position="none") +scale_y_continuous("Gammagard Share",labels=percent)
gpo_labs5 <- paste(gsub("/","-\n",names(table(data1$Physicians_Out)),fixed=T),"\n(N=",table(data1$Physicians_Out),")",sep="")
access_ch3 <- access_ch3 + scale_x_discrete("Physician Access (In Hospital)",labels=gpo_labs5)
access_ch3 <- access_ch3 +facet_grid(.~Physicians_Out,labeller=label_both)
access_ch3
I tried creating the 9 labels and passing that vector to the scale_x_discrete element, that just recycled the first 3, so it also did not solve the issue.
With the same data I followed a four step approach.
First: subsetting the data
open <- subset(data1, Physicians_Out == "Open")
restr <- subset(data1, Physicians_Out == "Restricted")
closed <- subset(data1, Physicians_Out == "Closed")
Second: creating the labels for the different subsets
labs.open <- paste(gsub("/","-\n",names(table(open$Physicians_In)),fixed=T),
"\n(N=",table(open$Physicians_In),")",sep="")
labs.restr <- paste(gsub("/","-\n",names(table(restr$Physicians_In)),fixed=T),
"\n(N=",table(restr$Physicians_In),")",sep="")
labs.closed <- paste(gsub("/","-\n",names(table(closed$Physicians_In)),fixed=T),
"\n(N=",table(closed$Physicians_In),")",sep="")
Third: creating a theme for removing the y-axis labels & text for the 2nd & 3rd sub-graphs
mytheme <- theme(
axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank()
)
Finally: creating the graph
p1 <- ggplot(open,aes(x=Physicians_In,y=share,fill=Physicians_In)) +
geom_boxplot() + stat_sum_single(mean) +
geom_jitter(position = position_jitter(width = .2),color="blue") +
guides(fill=FALSE) +
ggtitle(paste("Physician Access (Out): Open\nN = (", nrow(open), ")\n")) +
scale_y_continuous("Gammagard Share",labels=percent) +
scale_x_discrete("\nPhysician Access (In Hospital)",labels=labs.open) +
theme_bw()
p2 <- ggplot(restr,aes(x=Physicians_In,y=share,fill=Physicians_In)) +
geom_boxplot() + stat_sum_single(mean) +
geom_jitter(position = position_jitter(width = .2),color="blue") +
guides(fill=FALSE) +
ggtitle(paste("Physician Access (Out): Restricted\nN = (", nrow(restr), ")\n")) +
scale_x_discrete("\nPhysician Access (In Hospital)",labels=labs.restr) +
theme_bw() + mytheme
p3 <- ggplot(closed,aes(x=Physicians_In,y=share,fill=Physicians_In)) +
geom_boxplot() + stat_sum_single(mean) +
geom_jitter(position = position_jitter(width = .2),color="blue") +
guides(fill=FALSE) +
ggtitle(paste("Physician Access (Out): Closed\nN = (", nrow(closed), ")\n")) +
scale_x_discrete("\nPhysician Access (In Hospital)",labels=labs.closed) +
theme_bw() + mytheme
library(gridExtra)
grid.arrange(p1, p2, p3, ncol=3)
Which gives the following result:
It is not exactly what you want to do , but I think this can be helpful ( at least a good start)
library(ggplot2)
library(plyr)
data1 <- ddply(data1,.(Physicians_Out,Physicians_In),transform,label = length(share))
ggplot(data1,aes(x=Physicians_In,y=share,fill=Physicians_In))+
geom_boxplot() +
stat_sum_single(mean) +
facet_grid(.~Physicians_Out,labeller=label_both,scales='free_x') +
stat_summary(fun.y=min,aes(label=paste0('N=',label)),geom='text',col='blue',cex=5)

Resources