I'm trying to produce a figure containing multiple ggplot2 plots arranged by ggarrange from the package ggpubr and annotated on the left side. The ggplot2 plots are transparent or have a transparent background.
The issue arose lately (i.e. I did not confront it this year earlier) where a light grey line appears between the plots and the annotation text on the left side as shown in the image below. And it is more prominent when the figure is inserted in LaTeX.
I tried to play with the individual plots, but there is no plot.border only panel.border, thus, all my trials were not successful!
My maschine information:
R 3.6.3 on Ubuntu 20.04.2 LTS
ggplot2 3.3.5
ggpubr 0.4.0
The code I used to produce it (and was inspired by this):
library(ggplot2)
library(ggpubr)
# Box plot (bp)
bxp <- ggboxplot(ToothGrowth, x = "dose", y = "len",
color = "dose", palette = "jco")+
theme_bw(base_size = 8)+
theme(legend.position = "bottom",
legend.title = element_blank(),
legend.text = element_text(size = 10),
legend.background = element_blank(),
legend.box.background = element_rect(colour = "black",fill=NA,
size=0.1, linetype="solid"),
legend.key.width = unit(5, "mm"),
legend.spacing = unit(1, 'mm'),
legend.key.size = unit(0.5, "mm"),
legend.margin = margin(3,1,3,1, unit = "mm"))+
theme(panel.grid.major =element_line(colour = "black",linetype = "dotted",size = 0.05),
panel.grid.minor = element_blank(),
plot.margin = margin( 2.25,4.5,2.25,1, unit = "mm"),
plot.title = element_text(margin = margin(1,0,1,0, unit = "mm"),size = 10),
axis.line = element_blank(),
axis.text = element_text(colour = "black"), axis.title= element_blank(),
panel.border = element_rect(colour = "black", fill = NA, size=0.75),
panel.background = element_rect(color = NA),
rect = element_rect(fill = "transparent") )+ # all rectangles
labs(title = expression("(a)"))+
theme(axis.text = element_text(colour = "black"))
# Dot plot (dp)
dp <- ggdotplot(ToothGrowth, x = "dose", y = "len",
color = "dose", palette = "jco", binwidth = 1)+
theme_bw(base_size = 8)+
theme(legend.position = "bottom",
legend.title = element_blank(),
legend.text = element_text(size = 10),
legend.background = element_blank(),
legend.box.background = element_rect(colour = "black",fill=NA,
size=0.1, linetype="solid"),
legend.key.width = unit(5, "mm"),
legend.spacing = unit(1, 'mm'),
legend.key.size = unit(0.5, "mm"),
legend.margin = margin(3,1,3,1, unit = "mm"))+
theme(panel.grid.major =element_line(colour = "black",linetype = "dotted",size = 0.05),
panel.grid.minor = element_blank(),
plot.margin = margin( 2.25,4.5,2.25,1, unit = "mm"),
plot.title = element_text(margin = margin(1,0,1,0, unit = "mm"),size = 10),
axis.line = element_blank(),
axis.text = element_text(colour = "black"), axis.title= element_blank(),
panel.border = element_rect(colour = "black", fill = NA, size=0.75),
panel.background = element_rect(color = NA),
rect = element_rect(fill = "transparent") )+ # all rectangles
labs(title = expression("(b)"))+
theme(axis.text = element_text(colour = "black"))
bxp1<-bxp+labs(title = expression("(c)"))
dp1<-dp+labs(title = expression("(d)"))
figure <- ggarrange(bxp,bxp1,dp,dp1,
ncol = 2, nrow = 2,align = c("hv"))
figure <-annotate_figure(figure,left = text_grob("left side ",
color = "black",size = 10, rot = 90))
ggsave(plot = figure,
filename = paste0("question.png"),
height = 180, width =180, units = "mm", dpi = 300, device = "png",limitsize = FALSE)
Edit:
I can forget about the transparency by removeing rect = element_rect(fill = "transparent") ,however, the light grey gridline appears between the arragned plots and the antonatioed text of the left side.
The question can be: How to change the background of textgrob() to white instaed of transparent?
The issue with transparent arranged plots still exists. However, a progressive solution can be:
Producing ggplot2 plots with a white background by removing rect = element_rect(fill = "transparent") .
Using another approach to annotate and arrange plots:
library(gridExtra)
library(grid)
library(gridtext)
yleft <- textGrob("left side",
rot = 90, gp = gpar(fontsize = 10, fill= "white",color="black"))
figure <- grid.arrange(bxp,bxp1,
dp,dp1,
ncol = 2, nrow = 2,
left=yleft)
Update (March 2022):
using the approach in the question and changing the background bg to white.
ggsave(plot = figure,
filename = paste0("question.png"),
height = 180, width =180, units = "mm", dpi = 300,
device = "png",limitsize = FALSE,bg="white")
Related
For the following sample df
df = data.frame(x = c(2,3,4),y = c(4,5,6),group.a= c("1","1","2"),group.b = c("a","b","b"))
I want to just add a horizontal line in-between the y axis facet grids and after browsing different posts here I have tried using the panel.border = element_rect() argument however that gives me all four borders (top, right, bottom, left)
ggplot(df,aes(x=x,y=y)) + facet_grid(group.a~group.b) + theme_minimal() +
theme(legend.position = "bottom",
legend.title = element_blank(),
legend.direction = "horizontal",
legend.margin = margin(-20,0,0,0),
panel.grid = element_blank(),
panel.border = element_rect(color = "black", fill = NA, size = .5)
axis.text.x = element_blank(),
axis.line.y = element_line(size = .5),
axis.line.x = element_line(size = .5),
strip.placement = "outside")
Is there a way to just have the bottom and left border of the panel borders? Thanks!
There aren't any theme elements that fit the bill here; you would need custom annotations. Fortunately, annotation_custom applies to each panel, so this can be done with a couple of lines of code
library(ggplot2)
df = data.frame(x = c(2,3,4),y = c(4,5,6),
group.a= c("1","1","2"),group.b = c("a","b","b"))
ggplot(df,aes(x=x,y=y)) +
facet_grid(group.a~group.b) +
geom_point(col = "white") +
theme_minimal() +
annotation_custom(grid::linesGrob(y = c(0, 0), gp = grid::gpar(lwd = 3))) +
annotation_custom(grid::linesGrob(x = c(0, 0), gp = grid::gpar(lwd = 3))) +
theme(panel.grid = element_blank(),
strip.placement = "outside",
axis.text.x = element_blank())
My question is very similar to this question, but it's not the same.
I am looking for a way to create an empty ggplot with just the legend. However, in contrast to the autohor of the question I linked at the top, I actually need to create just the legend with no plot area included in the image.
I tried the following code:
ggplot(NULL, aes(color = ""))+
geom_blank()+
scale_color_manual(values = "black", labels = "Something")+
guides(color = guide_legend())+
theme(legend.box.background = element_rect(color = "black"))
But I'm getting the opposite of what I want - I am getting an empty plot area with no legend, like this:
And I would like my end result to look like this (I drew this in Paint):
Any help would be appreciated!
You can make a normal plot, then play with theme to achieve the desired result.
library(ggplot2)
ggplot(data.frame(x = 1, y = 1, colour = 'Something'), aes(x, y, fill = colour))+
geom_point(alpha=0, shape = 0)+ # completely transparent rectangular point
scale_fill_manual(values='black', drop=FALSE) +
guides(fill = guide_legend(override.aes = list(alpha=1, size = 40)))+ # showing the point in the legend
theme(axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
legend.position = c(0.5, 0.5), # move the legend to the center
legend.title = element_blank(),
legend.text = element_text(size = 40),
legend.key = element_rect(fill='NA'),
panel.grid = element_blank(),
panel.border = element_rect(colour = "black", fill='white', size=1)
)
Get the legend how you want it to look, then extract it with cowplot::get_legend:
library(grid)
library(cowplot)
library(ggplot2)
grid.newpage()
grid.draw(get_legend(
ggplot(data.frame(x = 1, y = 1), aes(x, y, fill = "Something")) +
geom_col(size = 20)+
scale_fill_manual(values = "white", labels = "Something", name = "") +
theme_bw() +
theme(legend.box.background = element_rect(color = "black"),
legend.title = element_text(size = 30),
legend.key.size = unit(60, "points"),
legend.text = element_text(size = 24),
legend.key = element_rect(colour = "black"),
legend.box.margin = margin(20, 20, 20, 20))))
I would like to add two (same) legends in ggplot and also want to change legend title and labels. I have tried this:
library(ggplot2)
ggplot(ToothGrowth, aes(x = len, color=factor(dose), fill= factor(dose))) +
geom_density(alpha=0.4) +
theme(panel.background = element_rect(fill = "khaki1", colour = "darkorchid3", size = 2, linetype = "solid"),
panel.grid.major = element_line(size = 0.5, linetype = 'solid', colour = "white"),
panel.grid.minor = element_line(size = 0.25, linetype = 'solid', colour = "white"),
plot.background = element_rect(fill = "bisque2"),
text = element_text(colour="blue4"), axis.title = element_text(size = rel(1.25)), axis.text = element_text(colour="blue4", size = 12),
legend.position=c(.90,.85), legend.background =
element_rect(fill="lightsalmon", colour = "tomato3", size = 1.25),
legend.title = element_text(colour="navy", face="bold"),
legend.text = element_text( colour="midnightblue", face="bold"), strip.background = element_rect(fill="olivedrab1", colour = "darkorchid3", size = 2, linetype = "solid"),
strip.text = element_text(colour="coral4", size=12, angle=0, face="bold")) +
scale_fill_discrete(name = "Dose", labels = c("A", "B", "C")) +
facet_wrap(~supp)
but I got this plot:
I want this plot:
Can somebody help me? Thank you.
As #erocoar and others have suggested, grid.arrange from gridExtra is useful here. Borrowing heavily from from the linked question:
library(gridExtra)
out <- by(data = ToothGrowth, INDICES = ToothGrowth$supp, FUN = function(m) {
m <- droplevels(m)
m <- ggplot(m, aes(x = len, fill= factor(dose)), color=factor(dose)) +
geom_density(alpha=0.4) +
theme(panel.background = element_rect(fill = "khaki1", colour = "darkorchid3",
size = 2, linetype = "solid"),
panel.grid.major = element_line(size = 0.5, linetype = 'solid',
colour = "white"),
panel.grid.minor = element_line(size = 0.25, linetype = 'solid',
colour = "white"),
plot.background = element_rect(fill = "bisque2"),
text = element_text(colour="blue4"),
axis.title = element_text(size = rel(1.25)),
axis.text = element_text(colour="blue4", size = 12),
legend.position=c(.90,.85),
legend.background = element_rect(fill="lightsalmon",
colour = "tomato3", size = 1.25),
legend.title = element_text(colour="navy", face="bold"),
legend.text = element_text( colour="midnightblue", face="bold"),
strip.background = element_rect(fill="olivedrab1",
colour = "darkorchid3", size = 2,
linetype = "solid"),
strip.text = element_text(colour="coral4", size=12, angle=0,
face="bold")) +
scale_fill_discrete(name = "Dose", labels = c("A", "B", "C")) +
xlim(0,35) +
ylim(0,0.2) +
ggtitle(m$supp)
})
do.call(grid.arrange, list(grobs = out, ncol = 2))
Some things to note.
I moved the color argument outside of the aes() call and this removed the extra legend.
I manually set the x and y limits for a consistent look.
I needed to add a title.
To get the plots side by side I had to add a second argument to do.call(). When supplying more than one argument it needs to be in a list.
I hope this helps.
I'm making scatterplots in ggplot2 and then using ggsave to export PDFs of specific widths and heights. However, the figure legend never gets properly resized with ggsave (its borders don't stay within the plot). Is there another way to simultaneously resize all parts of a ggplot for easy exporting? I've also tried using pdf(), but the same problem occurs.
Here is an example of what's going on using iris:
data(iris)
test <- ggplot(iris, aes(x = iris$Sepal.Length, y = iris$Sepal.Width,
colour = iris$Species)) +
geom_point(size = .1) +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(), panel.border = element_blank(),
panel.background = element_blank(),
axis.line = element_line(colour = "black", size = .8),
legend.key=element_blank(),
axis.ticks = element_line(colour = "black", size = .6),
axis.text=element_text(size=6, colour = "black"),
plot.title = element_text(hjust = 0.0, size = 6, colour = "black"),
axis.title=element_text(size= 6),
legend.text=element_text(size = 6),
legend.title=element_text(size = 6, face = "bold"),
legend.position = c(.9, .15)) + labs(colour = "Species")
##saving plot with no resizing
ggsave(plot = test, file = "NoResize.pdf", dpi = 600, family = "ArialMT")
##saving plot with resizing results in relatively larger legend
ggsave(plot = test, file = "Resize.pdf", device = "pdf", width = 3.5,
height = 3, units = "in", dpi = 600, family = "ArialMT")
The legend seems to be changing, but it seems to get relatively bigger than the other aspects of the plot, such that it no longer fits within the plot axes.
The size argument is in points. If you have a preferred size for the default 7 inch by 7 inch graphic then you'll need to scale the size accordingly for a different size graphic.
Two other notes.
Don't use $ in the ggplot2::aes call. This will cause you problems in the future. It is sufficient to use ggplot(iris) + aes(x = Sepal.Length).
absolute legend.position in this example can be difficult to use in the different size graphics. I would recommend legend.position = "bottom" instead.
Here are two ways to control the relative size of the font in your example.
library(ggplot2)
data(iris)
# Okay solution, using a scaling value in an expression. I would not recommend
# this in general, but it will work. A better solution would be to use a
# function
test <-
expression({
ggplot(iris) +
aes(x = Sepal.Length, y = Sepal.Width, colour = Species) +
geom_point(size = .1) +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.line = element_line(colour = "black", size = .8),
legend.key = element_blank(),
axis.ticks = element_line(colour = "black", size = .6),
axis.text = element_text(size = 6 * scale_value, colour = "black"),
plot.title = element_text(hjust = 0.0, size = 6 * scale_value, colour = "black"),
axis.title = element_text(size = 6 * scale_value),
legend.text = element_text(size = 6 * scale_value),
legend.title = element_text(size = 6, face = "bold"),
legend.position = c(.9, .15)) +
labs(colour = "Species")
})
scale_value <- 1
ggsave(eval(test), width = 7 * scale_value, height = 7 * scale_value, file = "sotest1.pdf")
scale_value <- 3/7
ggsave(eval(test), width = 7 * scale_value, height = 7 * scale_value, file = "sotest2.pdf")
# Define a function to do the work.
iris_plot <- function(scale_value = 1, filename) {
g <- ggplot(iris) +
aes(x = Sepal.Length, y = Sepal.Width, colour = Species) +
geom_point(size = .1) +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.line = element_line(colour = "black", size = .8),
legend.key = element_blank(),
axis.ticks = element_line(colour = "black", size = .6),
axis.text = element_text(size = 6 * scale_value, colour = "black"),
plot.title = element_text(hjust = 0.0, size = 6 * scale_value, colour = "black"),
axis.title = element_text(size = 6 * scale_value),
legend.text = element_text(size = 6 * scale_value),
legend.title = element_text(size = 6, face = "bold"),
legend.position = c(.9, .15)) +
labs(colour = "Species")
ggsave(g, width = 7 * scale_value, height = 7 * scale_value, file = filename)
}
iris_plot(filename = "iris7x7.pdf")
iris_plot(4/7, filename = "iris4x4.pdf")
EDIT
Using the package magick will give you a nice programming interface for resizing and editing graphics via imagemagick
For example
library(ggplot2)
library(magick)
g <-
ggplot(iris) +
aes(x = Sepal.Length, y = Sepal.Width, colour = Species) +
geom_point(size = .1) +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
panel.background = element_blank(),
axis.line = element_line(colour = "black", size = .8),
legend.key = element_blank(),
axis.ticks = element_line(colour = "black", size = .6),
axis.text = element_text(size = 6, colour = "black"),
plot.title = element_text(hjust = 0.0, size = 6, colour = "black"),
axis.title = element_text(size = 6),
legend.text = element_text(size = 6),
legend.title = element_text(size = 6, face = "bold"),
legend.position = c(.9, .15)) +
labs(colour = "Species")
ggsave(g, file = "iris7x7.pdf", width = 7, height = 7)
iris_g <- image_read("iris7x7.pdf")
iris_3x3 <- image_scale(iris_g, "216x216")
image_write(iris_3x3, path = "iris3x3.pdf", format = "pdf")
Note, the resized graphic may require some edits to deal with pixelation or blurriness.
Again, I would recommend against using an absolute legend.position value. Perhaps, if you know you need a 3in by 3in graphic you can open a dev window with those dimensions to build your graphic in and then save appropriately. For example, open a 3in by 3in X Window via X11(width = 3, height = 3).
I want to create space between the titles (the axis title and the plot title) and the edge of the plot. I tried vjust on axis.title and plot.title with no luck. Nothing really changed in the plot when I tried various values for vjust. I also tried plot.margin, but nothing seemed to happen with it either.
Data:
data = data.frame(Category = c(0,1), value = c(40000, 120000))
data$Category = factor(data$Category, levels = c(0,1), labels = c("One-time", "Repeat"))
Plot:
p = ggplot(data, aes(Category, Value)) +
geom_bar(stat = "identity", width = 0.5, position=position_dodge(width=0.9)) +
geom_text(aes(label=Value), position=position_dodge(width=0.9), family = "mono", vjust=-0.5) +
ggtitle("Title") +
scale_y_continuous(expand = c(0,0), limits = c(0,150000)) +
scale_x_discrete(expand = c(0,0), limits = c("One-time", "Repeat")) +
xlab("X axis Title") +
ylab("Y axis Title")
Theme:
p + theme(
panel.grid.major = element_line(linetype = "blank"),
panel.grid.minor = element_line(linetype = "blank"),
axis.title = element_text(family = "sans", size = 15),
axis.text = element_text(family = "mono", size = 12),
plot.title = element_text(family = "sans", size = 18),
panel.background = element_rect(fill = NA)
)
You want to do this using margin
p + theme(
panel.grid.major = element_line(linetype = "blank"),
panel.grid.minor = element_line(linetype = "blank"),
axis.title.x = element_text(family = "sans", size = 15, margin=margin(30,0,0,0)),
axis.title.y = element_text(family = "sans", size = 15, margin=margin(0,30,0,0)),
axis.text = element_text(family = "mono", size = 12),
plot.title = element_text(family = "sans", size = 18, margin=margin(0,0,30,0)),
panel.background = element_rect(fill = NA)
)
Note that margin requires a four inputs and they specify the space in the order top,right,bottom,left. Also note I'm using the developmental ggplot2 version so my title default is left justified. 'hjust' and 'vjust' worked in older versions of ggplot2.
You can also give a "-ve" value to margin argument to pull the title closer to the plot.
gg_1 +
theme(plot.title = element_text(size = 12,
hjust = 0.5,
family = fam_3,
face = "bold",
margin = margin(0,0,-10,0))