I have a multiple plot within a plot, generated by ggpubr::ggarrange(). However the legends only appears for the first plot i.e., A and B. I wanted to get the legends for rest of the colours, C, D, E on the top. Setting common.legend = TRUE only gives the first two legends.
Thanks for the help!
library(ggpubr)
arranged_plot <- ggarrange(
plot_list[[1]] + rremove("ylab") + rremove("xlab") + rremove("x.text"),
plot_list[[2]] + rremove("ylab") + rremove("xlab") + rremove("axis.text"),
plot_list[[3]] + rremove("ylab") + rremove("xlab"),
plot_list[[4]] + rremove("ylab") + rremove("xlab") + rremove("y.text"),
labels = NULL, ncol = 2, nrow = 2,align = "hv",
font.label = list(size = 10, color = "black", face = "bold", family = NULL, position = "top"),
common.legend=TRUE)
I'm not sure how to do this with ggarrange, but if you're willing to look at other methods, here are two options:
Using patchwork (and collecting legends).
# sample data where each elem has cyl=4 and another cyl
mtcars$cyl <- factor(mtcars$cyl)
mtdat1 <- lapply(c(6, 8), function(CY) {
subset(mtcars, cyl %in% c(4, CY)) |>
transform(CY = CY)
})
plot_list <- lapply(mtdat1, function(dat) {
ggplot(dat, aes(mpg, disp, color = cyl)) +
geom_point() +
scale_color_manual(values = setNames(c("gray", "red", "blue"), c(4, 6, 8)), drop = FALSE)
})
library(patchwork)
plot_list[[1]] + plot_list[[2]] +
plot_layout(nrow = 1, guides = "collect") &
theme(legend.position = "top")
Facets.
# sample data, starting with `mtdat1` from above
mtdat2 <- do.call(rbind, args = mtdat1)
ggplot(mtdat2, aes(mpg, disp, color = cyl)) +
facet_wrap(~ CY) +
geom_point() +
scale_color_manual(values = setNames(c("gray", "red", "blue"), c(4, 6, 8)), drop = FALSE) +
theme(legend.position = "top")
If you prefer to not have the facet strips, we can remove those in a theme:
ggplot(mtdat2, aes(mpg, disp, color = cyl)) +
facet_wrap(~ CY) +
geom_point() +
scale_color_manual(values = setNames(c("gray", "red", "blue"), c(4, 6, 8)), drop = FALSE) +
theme(legend.position = "top", strip.text.x = element_blank())
I think there are two advantages to facets:
Simpler code, more efficient, allowing ggplot to handle everything in one step.
Since we don't explicitly free the scales (e.g., not doing scales="free"), the axes are all on the same scale, no need to explicitly control them. For comparisons as in your graph, this can be a big difference in visualizing the differences between levels. (Compare this plot with the first plot using patchwork, though those axis limits can easily be fixed as well.)
Related
I have 16 plots that I want to arrange together for illustration purposes. Below I show one graph as an example.
The code for creating each plot is next:
P1<- ggplot(Fish_acc_C.D.Mean_bottom_invierno_P16, aes(C.D.Mean_bottom,meanAcc)) +
geom_point(aes(C.D.Mean_bottom,meanAcc, color = C.I.Mean_bottom),show.legend = FALSE) +
scale_colour_gradientn(colours=c("green","black")) +
theme_bw() +
geom_smooth(aes(linetype = "Activity"),fill = "lightblue",color="red", alpha = 0.99) +
ggtitle("Activity ~ Curr Direct Mean bottom WINTER (Hourly data)") +
theme(plot.title = element_text(size=10,hjust = 0.5)) +
geom_smooth(aes(C.D.Mean_bottom, C.I.Mean_bottom * max(range(Fish_acc_C.D.Mean_bottom_invierno_P16$C.I.Mean_bottom)), linetype = "C.I.M.B"), se=FALSE, colour = "blue",show.legend = FALSE) +
scale_y_continuous(sec.axis = sec_axis(trans = ~ . /max(range(Fish_acc_C.D.Mean_bottom_invierno_P16$C.I.Mean_bottom)), name = "C.I.Mean bottom")) +
scale_linetype_manual(values = c(1,1),guide = guide_legend(override.aes = list(colour = c("red", "blue")))) +
coord_cartesian(ylim = c(0, 1.25)) +
theme(legend.justification = c(1,1),
legend.position = c(1,1))
My problem is that when I use grid.arrange() to group them all together, the legends of each plot increase their size with regard to their size in the individual ones. Here an example:
Here I show the code for creating the grid_arrange:
grid.arrange(P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12,P13,P14,P15,P16,ncol=4)
Does anyone know how to reduce the size of the legends in the grid_arranged graph in order to see the lines? I thought that maybe I could place the legends in the upper-middle position of the graph, delete the word "linetype" from the legend and display the linetypes in one line (side by side instead of up and down). However I don't know how to do it.
Does anyone have any recommendation?
You can look at the ggarrange function from the ggpubr package.
It allows you to group your plots as you need and have a common legend for all of them, for which you can specify the position.
e.g.
library(ggpubr)
ggarrange(g1, g2, ncol = 2, common.legend = T, legend = 'bottom')
# or
ggarrange(plotlist = my_list, ncol = 2, common.legend = T, legend = 'bottom')
#Dekike then you can extract the legend and put the legend on top/bottom of the main plot.
Here are my psude code
library(ggplot2)
library(cowplot)
P1 <- ggplot() + ... + guides(lineType=guide_legend(ncol=2))
legend_extracted <- get_legend(P1)
P1 <- P1 + theme(legend.position="none")
P2 <- ggplot() + ... + theme(legend.position="none")
...
P16 <- ggplot() + ... + theme(legend.position="none")
main_plot <- grid.arrange(P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12,P13,P14,P15,P16,ncol=4)
main_plot_wLegend <- grid.arrange(legend_extracted, main_plot, ncol=1, nrow=2)
Please try and feedback on this!
I am using ggplot2 to plot points from a .csv file that is just a column used a x values and a column used a y values. I am a little confused as to how ggplot decides what to make a legend for and haven't found any good examples online.
I would like the legend to show that geom_point is stress vs strain, and my geom_smooth is the best fit line.
Here is my code:
library(ggplot2)
imported = read.csv("data.csv")
Strain = imported$Strain
Stress = imported$Stress..N.m.2.
err = .0005
gg <-
ggplot(imported, aes(x=Strain, y=Stress)) +
geom_point(aes(group = "Points"), shape = 79, colour = "black", size = 2, stroke = 4) +
geom_smooth(method = "lm", se = FALSE, color = "orange") +
geom_errorbarh(xmin = Strain - err, xmax = Strain + err, show.legend = TRUE) +
theme_gray() + ggtitle("Stress vs Strain") +
theme(legend.position = "top")
gg
And it is producing the following plot:
my plot
Edit: added approach at top to create legend for each geom, by creating dummy mapping to separate aesthetics.
library(ggplot2)
ggplot(mtcars, aes(mpg, wt)) +
geom_point(aes(color = "point")) + # dummy mapping to color
geom_smooth(method = "lm", se = FALSE, color = "orange",
aes(linetype = "best fit")) + # dummy mapping to linetype
geom_errorbarh(aes(xmin = mpg - 2, xmax = mpg + 1)) +
scale_color_manual(name = "Stress vs. Strain", values = "black") +
scale_linetype_manual(name = "Best fit line", values = "solid")
original answer:
Note the difference in legend here:
library(ggplot2)
ggplot(mtcars, aes(mpg, wt, color = as.character(cyl))) +
geom_point() +
geom_errorbarh(aes(xmin = mpg - 2, xmax = mpg + 1),
show.legend = TRUE) # error bars reflected in legend
ggplot(mtcars, aes(mpg, wt, color = as.character(cyl))) +
geom_point() +
geom_errorbarh(aes(xmin = mpg - 2, xmax = mpg + 1),
show.legend = FALSE) # error bars not shown in legend
I want to highlight 4 single points in a scatter plot with a box surrounding the name associated with the plot. I am using ggrepel to create the boxes surrounding the plots and to repel them.
This is the code I have:
library(ggplot2)
gg <- ggplot(X, aes(x = XX, y = XY)) +
geom_point(col = "steelblue", size = 3) +
geom_smooth(method = "lm", col = "firebrick", se = FALSE) +
labs(title = "XX vs XY", subtitle = "X", y = "XX", x = "XY") +
scale_x_continuous(breaks = seq(76, 82, 1)) +
scale_y_continuous(breaks = seq(15, 19, 1))
library(ggrepel)
gg + geom_text_repel(aes(label = Female), size = 3, data = X)
gg + geom_label_repel(aes(label = Female), size = 2, data = X)
With that code, I obtain boxes surrounding all the plots. However, I only want to have the boxes in 4 specific plots and no boxes in the other plots. How can I do that?
Thanks in advance! Regards,
TD
Using this SO solution I created a facet with two "empty" plots, with the aim of combining with another group of facet_wrap plots, as shown below. The purpose is to have two y-axis labels for different unit measurements. How can I make the grid layout look like the top image, which produces the arrangement I want, but not the axis labels? This was accomplished with plot_grid with individual plots. My current output does not scale correctly and overlaps the other plots, as seen in the second image, but provides the axis labels.
I have example data below, just copy and run the code to input it.
library(ggplot2)
library(grid)
library(cowplot)
clipboard <- readClipboard()
test.data <- read.table(file = "clipboard", sep = ",", header=TRUE)
test.data1 <- test.data[1:24, ]
test.data2 <- test.data[25:32, ]
testplot1 <- ggplot(test.data1, aes(Station, value)) +
geom_point() +
labs(x = "Stations", y = "Scale A") +
theme(legend.position = "none", legend.title = element_blank()) +
facet_wrap( ~ constituent, ncol = 3, scales = "free_y")
testplot2 <- ggplot(test.data2, aes(Station, value)) +
geom_point() +
labs(x = "Stations", y = "Scale B") +
theme(legend.position = "none", legend.title = element_blank(), axis.title.y = element_text(hjust = 0.2)) +
facet_wrap( ~ constituent, ncol = 1, scales = "free_y")
blankplots <- ggplotGrob(testplot2)
rm_grobs <- blankplots$layout$name %in% c("panel-1-1", "panel-2-1", "strip-t-1-1", "strip-t-1-2")
blankplots$grobs[rm_grobs] <- NULL
blankplots$layout <- blankplots$layout[!rm_grobs, ]
grid.newpage()
emptygrids <- grid.draw(blankplots)
plot_grid(emptygrids, MPLOOplot1)
Example date is below:
Station,constituent,value
A1,A,1
B1,A,1
A1,B,2
B1,B,2
A1,C,3
B1,C,3
A1,D,4
B1,D,4
A1,E,5
B1,E,5
A1,F,6
B1,F,6
A1,G,7
B1,G,7
A1,H,8
B1,H,8
A1,I,9
B1,I,9
A1,J,10
B1,J,10
A1,K,11
B1,K,11
A1,L,1.4
B1,L,1.4
A1,Blank1,NA
B1,Blank1,NA
A1,Blank2,NA
B1,Blank2,NA
A1,XX,0.52
B1,XX,0.52
A1,YY,0.355
B1,YY,0.355
I'm not sure I understand exactly what you're trying to do, so let me know if this is what you had in mind. I wasn't sure what you wanted colour to be mapped to, so I just used constituent for this example.
library(gridExtra)
library(ggplot2)
library(dplyr)
library(cowplot)
theme_set(theme_classic())
testplot1 <- ggplot(test.data1, aes(Station, value, colour=constituent)) +
geom_point() +
labs(x = "Stations", y = "Scale A") +
theme(legend.title = element_blank()) +
facet_wrap( ~ constituent, ncol = 3, scales = "free_y") +
guides(colour=guide_legend(ncol=2))
testplot2 <- ggplot(test.data2 %>% filter(!grepl("Blank", constituent)),
aes(Station, value, colour=constituent)) +
geom_point() +
labs(x = "Stations", y = "Scale B") +
theme(legend.title = element_blank(),
axis.title.y = element_text(hjust = 0.2)) +
facet_wrap( ~ constituent, ncol = 1, scales = "free_y")
leg1 = get_legend(testplot1)
leg2 = get_legend(testplot2)
testplot1 = testplot1 + guides(colour=FALSE)
testplot2 = testplot2 + guides(colour=FALSE)
Now we lay out the plots and legends with grid.arrange. This requires some manual tweaking of the heights and widths.
grid.arrange(
arrangeGrob(
arrangeGrob(nullGrob(), leg2, leg1, nullGrob(), ncol=4, widths=c(1,4,4,1)),
testplot2, ncol=1, heights=c(4.2,5)
),
testplot1, ncol=2, widths=c(1.1,3))
I'm using the code below:
# Libs
require(ggplot2); require(gridExtra); require(grid)
# Generate separate charts
chrts_list_scts <- list()
# Data
data("mtcars")
# A
chrts_list_scts$a <- ggplot(mtcars) +
geom_point(size = 2, aes(x = mpg, y = disp,
colour = as.factor(cyl))) +
geom_smooth(aes(x = mpg, y = disp),
method = "auto") +
xlab("MPG") +
ylab("Disp") +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.position = "none")
# B
chrts_list_scts$b <- ggplot(mtcars) +
geom_point(size = 2, aes(x = mpg, y = drat,
colour = as.factor(cyl))) +
geom_smooth(aes(x = mpg, y = drat),
method = "auto") +
xlab("MPG") +
ylab("Drat") +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.position = "none")
# C
chrts_list_scts$c <- ggplot(mtcars) +
geom_point(size = 2, aes(x = mpg, y = qsec,
colour = as.factor(cyl))) +
geom_smooth(aes(x = mpg, y = qsec),
method = "auto") +
xlab("MPG") +
ylab("QSEC") +
guides(colour = guide_legend(title = "cyl")) +
theme_bw() +
theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
legend.position = "bottom",
legend.key = element_rect(colour = NA))
# Arrange grid
png(filename = "chrts.PNG", width = 6,
height = 10, units = 'in', res = 300)
title_text <- c("mtcars")
chrts_list_scts$all_scts <- grid.arrange(chrts_list_scts$a,
chrts_list_scts$b,
chrts_list_scts$c,
top =
textGrob(label = title_text,
gp = gpar(
fontsize = 14,
font = 2)))
dev.off()
rm(title_text)
To generate the following chart:
I'm interested in adding border around that chart, as in the picture below:
Attempts
I tried to address this request via adding polygonGrob in the code:
chrts_list_scts$all_scts <- grid.arrange(chrts_list_scts$dep_work,
chrts_list_scts$chld_work,
chrts_list_scts$pens,
polygonGrob(x = c(0,0.5,1.05),
y = c(0,0.5,1.05)
),
top =
textGrob(label = title_text,
gp = gpar(
fontsize = 14,
font = 2)))
but this generates a pointless chart with one line across in the bottom. I had a look at the seeming similar discussion on SO but it wasn't clear to me how to arrive at a working solution.
Side requirements
In addition to generating the border, I would like to:
Be able to exercise some control over the border aesthetics, like changing size and colour of the border.
Ideally, I would like to encapsulate this solution within the arrange.grid call. So at the object chrts_list_scts$all_scts has all elements including charts and neat border around all of them.
I will be happy to accept solutions that address the major requirements with respect to the border only, if there is a suggested solution that matches the remaining two points it will be even nicer.
1) Using the iris example (but further simplified) from the link provided in the question just add the last line. Modify the gpar(...) components (and possibly the width and height) to get different aesthetics. (This is not encapsulated in the grid.arrange call.)
library(ggplot2)
library(grid)
library(gridExtra)
g <- ggplot(iris, aes(Sepal.Width, Sepal.Length)) + geom_point()
grid.arrange(g, g, ncol=2)
# next line adds border
grid.rect(width = .98, height = .98, gp = gpar(lwd = 2, col = "blue", fill = NA))
(continued after plot)
2) This is a variation of solution (1) in which on the plus side encapsulates both the graphics and border in the gt gTree by creating grobs to hold each. On the other hand it does involve some additional complexity:
grid.newpage()
ga <- arrangeGrob(g, g, ncol = 2)
gb <- rectGrob(height = .98, width = .98, gp = gpar(lwd = 2, col = "blue", fill = NA)) # border, no fill
gt <- gTree(children = gList(ga, gb))
grid.draw(gt)
you can add a rectGrob to the gtable
grid.draw(gtable::gtable_add_grob(arrangeGrob(g, g, ncol=2),
rectGrob(gp=gpar(lwd=5, fill=NA)), 1, 1, 1, 2))
NOTE: fill=NA or fill='transparent' is required otherwise the rectangle can mask the objects below it.