Using arrangeGrob to add a sub plot as a legend - r

I'm trying to graph two plots together, where one I a main plot and the other is a sub plot that I'd like to be located in the legend area of the main plot.
This code produces this plot:
gLegend <- function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
return(legend)
}
set.seed(1)
main.df <- data.frame(group=rep(LETTERS[1:4],3),
y=rnorm(12),x=c(rep(1,4),rep(2,4),rep(3,4)),col=rep(c("gray","blue","red","magenta"),3))
main.df$group <- factor(main.df$group,levels=LETTERS[1:4])
sub.df <- data.frame(group=c("B","C","D"),x=1:3,effect=runif(3,0,1),col=c("blue","red","magenta"))
sub.df$group <- factor(sub.df$group,levels=LETTERS[2:4])
main.plot <- ggplot(main.df,aes(x=x,y=y,color=factor(group)))+geom_point(size=3)+facet_wrap(~group,ncol=4)+scale_fill_manual(values=c("gray","blue","red","magenta"),labels=c("A","B","C","D"),name="group")+scale_colour_manual(values=c("gray","blue","red","magenta"),labels=c("A","B","C","D"),name="group")+scale_x_continuous(breaks=unique(main.df$x))
sub.plot <- ggplot(sub.df,aes(x=x,y=effect,color=factor(group)))+geom_point(size=2)+scale_fill_manual(values=c("blue","red","magenta"),labels=c("B","C","D"),name="group",guide=FALSE)+scale_colour_manual(values=c("blue","red","magenta"),labels=c("B","C","D"),name="group",guide=FALSE)+labs(x="group",y="effect")+ggtitle("effect summary")+scale_x_continuous(breaks=unique(sub.df$x),labels=c("B","C","D"))
sub.plot.grob <- ggplotGrob(sub.plot)
combined.plot <- arrangeGrob(main.plot+theme(legend.position="none"),widths=c(0.75,0.25),arrangeGrob(20,sub.plot.grob),ncol=2)
What I'd like to do is get rid of the legend of the main plot.
If I add guide=FALSE to scale_fill_manual scale_colour_manual gLegend throws the error:
Error in tmp$grobs[[leg]] :
attempt to select less than one element in get1index
Obviously because it cannot fine "guide-box"
Any idea how to solve this?

Here is an alternative solution using cowplot
library(ggplot2)
library(cowplot)
theme_set(theme_bw()) #cowplot automatically sets theme_classic
set.seed(1)
main.df <- data.frame(group=rep(LETTERS[1:4],3),
y=rnorm(12),x=c(rep(1,4),rep(2,4),rep(3,4)),col=rep(c("gray","blue","red","magenta"),3))
main.df$group <- factor(main.df$group,levels=LETTERS[1:4])
sub.df <- data.frame(group=c("B","C","D"),x=1:3,effect=runif(3,0,1),col=c("blue","red","magenta"))
sub.df$group <- factor(sub.df$group,levels=LETTERS[2:4])
sub.plot <- ggplot(sub.df,aes(x=x,y=effect,color=factor(group)))+
geom_point(size=2)+
scale_fill_manual(values=c("blue","red","magenta"),labels=c("B","C","D"),name="group",guide=FALSE)+
scale_colour_manual(values=c("blue","red","magenta"),labels=c("B","C","D"),name="group",guide=FALSE)+
labs(x="group",y="effect")+
ggtitle("effect summary")+
scale_x_continuous(breaks=unique(sub.df$x),labels=c("B","C","D"))
main.plot <- ggplot(main.df,aes(x=x,y=y,color=factor(group)))+
geom_point(size=3)+facet_wrap(~group,ncol=4)+
scale_fill_manual(values=c("gray","blue","red","magenta"),labels=c("A","B","C","D"),name="group")+
scale_colour_manual(values=c("gray","blue","red","magenta"),labels=c("A","B","C","D"),name="group")+
scale_x_continuous(breaks=unique(main.df$x)) +
theme(legend.position = "none")
ggdraw() +
draw_plot(main.plot, x = 0, y = 0, width = 0.75, height = 1) +
draw_plot(sub.plot, 0.75, 0, .25, .5)

try this,
grid.arrange(ggplot(), ggplot(),
layout_matrix=matrix(c(1,1,NA,2), 2), widths=c(1.5,1))

Here is solution using grid package, if you want a blank space where is the current legend:
library(grid)
set.seed(1)
main.df <- data.frame(group=rep(LETTERS[1:4],3),
y = rnorm(12),
x = c(rep(1,4), rep(2,4), rep(3,4)),
col = rep(c("gray", "blue", "red", "magenta"), 3))
main.df$group <- factor(main.df$group,levels=LETTERS[1:4])
sub.df <- data.frame(group=c("B", "C", "D"),
x = 1:3,
effect = runif(3, 0, 1),
col = c("blue", "red", "magenta"))
sub.df$group <- factor(sub.df$group, levels=LETTERS[2:4])
main.plot <- ggplot(main.df, aes(x = x,y = y, color = factor(group))) +
geom_point(size = 3) + facet_wrap(~group, ncol = 4) +
scale_fill_manual(values = c("gray", "blue", "red", "magenta"),
labels = c("A", "B", "C", "D"),name = "group") +
scale_colour_manual(values = c("gray", "blue", "red", "magenta"),
labels = c("A", "B", "C", "D"), name = "group") +
scale_x_continuous(breaks = unique(main.df$x)) + guides(color = F)
sub.plot <- ggplot(sub.df, aes(x = x, y = effect, color = factor(group))) +
geom_point(size = 2) +
scale_fill_manual(values = c("blue", "red", "magenta"),
labels = c("B", "C", "D"), name= "group" ,guide = FALSE) +
scale_colour_manual(values = c("blue", "red", "magenta"),
labels = c("B", "C", "D"), name = "group", guide=FALSE) +
labs(x = "group", y = "effect") +
ggtitle("effect summary") +
scale_x_continuous(breaks = unique(sub.df$x), labels = c("B", "C", "D"))
blank = grid.rect(gp = gpar(col = "white"))
sub.plot.grob <- arrangeGrob(blank, sub.plot, ncol = 1)
combined.plot <- arrangeGrob(main.plot, sub.plot.grob, ncol=2, widths=c(0.75,0.25))
grid.arrange(combined.plot)

Related

how to merge two legends with shapes in ggplot2?

If i plot this data here:
data <- data.frame(Xdata = rnorm(6),
Ydata = rnorm(6),
Group1 = c("ld-01", "ld-02", "ld-03",
"ld-04", "ld-05", "ld-06"),
Group2 = c("ld", "ld", "l",
"ld4", "l", "ld6"))
ggplot(data, aes(Xdata, Ydata, color =
Group2, shape = Group1)) +
geom_point(size = 7)
I want to replace the upper legend by the one "this on"
One option would be to map Group1 on the color aes too and use scale_color_manual to assign your desired colors like so:
set.seed(123)
data <- data.frame(
Xdata = rnorm(6),
Ydata = rnorm(6),
Group1 = c(
"ld-01", "ld-02", "ld-03",
"ld-04", "ld-05", "ld-06"
),
Group2 = c(
"ld", "ld", "l",
"ld4", "l", "ld6"
)
)
library(ggplot2)
colors <- scales::hue_pal()(4)
pal_color <- colors[c(2, 2, 1, 3, 1, 4)]
ggplot(data, aes(Xdata, Ydata,
color =
Group1, shape = Group1
)) +
geom_point(size = 7) +
scale_color_manual(values = pal_color)
EDIT if you want to keep both legends and only color the shape legend one way to go is to use the override.aes argument of guide_legend which allows to set the colors like so:
library(ggplot2)
#colors <- scales::hue_pal()(4)
colors <- c("black", "blue", "red", "green")
pal_color <- colors[c(2, 2, 1, 3, 1, 4)]
ggplot(data, aes(Xdata, Ydata,
color = Group2, shape = Group1
)) +
geom_point(size = 7) +
scale_color_manual(values = colors) +
guides(shape = guide_legend(override.aes = list(color = pal_color)))

How do I keep all the legends in ggplot

How do I keep all the legends in plot?
Below is an example of scatter plot. It prints only if x > 0.5. But I want to show all the legends.
library(tidyverse)
for (iter in 1:5)
{
# generate 5 random points
tbl <- tibble(x = runif(5),
y = runif(5),
class = c("A", "B", "C", "D", "E"))
# print if x > 0.5
p <- ggplot(data = tbl %>% filter(x > 0.5),
aes(x = x,
y = y,
color = class)) +
geom_point(size = 5) +
scale_fill_manual(labels = c("A", "B", "C", "D", "E"),
values = c("Grey", "Red", "Green", "Blue", "Yellow"),
drop = FALSE) +
theme_bw() +
theme(aspect.ratio = 1) +
xlim(0, 1) +
ylim(0, 1)
ggsave(p,
filename = paste0(iter, ".png"))
}
You can do it if:
you set the class variable to factor
use scale_colour_manual instead of scale_fill_manual. If you want to use the default colour from the ggplot palette you can use scale_colour_descrete, as in my code.
library(tidyverse)
set.seed(1) # for reproducibility
plots <- lapply(1:5, function(iter){
# generate 5 random points
tbl <- tibble(x = runif(5),
y = runif(5),
class = factor(c("A", "B", "C", "D", "E")))
# print if x > 0.5
p <- ggplot(data = tbl %>% filter(x > 0.5),
aes(x = x,
y = y,
color = class)) +
geom_point(size = 5) +
scale_colour_discrete(drop = FALSE) +
theme_bw() +
theme(aspect.ratio = 1) +
xlim(0, 1) +
ylim(0, 1)
ggsave(p, filename = paste0(iter, ".png"))
p
})
# visualize them all together
cowplot::plot_grid(plotlist = plots)
PS: I've used lapply instead of a for loop, usually it is more appreciated by R users when possible.
Instead of filtering out the data you can change the values to NA:
library(tidyverse)
for (iter in 1:5)
{
# generate 5 random points
tbl <- tibble(x = runif(5),
y = runif(5),
class = c("A", "B", "C", "D", "E"))
# print if x > 0.5
p <- ggplot(data = tbl %>% mutate(y = if_else(x > 0.5, y, NA_real_)),
aes(x = x,
y = y,
color = class)) +
geom_point(size = 5) +
scale_fill_manual(labels = c("A", "B", "C", "D", "E"),
values = c("Grey", "Red", "Green", "Blue", "Yellow"),
drop = FALSE) +
theme_bw() +
theme(aspect.ratio = 1) +
xlim(0, 1) +
ylim(0, 1)
ggsave(p,
filename = paste0(iter, ".png"))
}

changing legend of faceted boxplot in ggplot2 to have groups with similar names inside

This question builds off of enter link description here but is in the context of faceted boxplots.
So, I have the following code:
set.seed(20210714)
dd <- data.frame(Method = rep(c("A", "B", "C"), each = 60), Pattern = rep(c("X", "Y", "Z"), times = 30), X1 = runif(180), Complexity = rep(c("High", "Low"), times = 90), nsim = rep(rep(1:10, times = 9), each = 2), n = 10)
dd1 <- data.frame(Method = rep(c("A", "B", "C"), each = 60), Pattern = rep(c("X", "Y", "Z"), times = 30), X1 = runif(180), Complexity = rep(c("High", "Low"), times = 90), nsim = rep(rep(1:10, times = 9), each = 2), n = 5)
dd <- rbind(dd, dd1)
library(ggplot2)
# create dummy dataframe.
dummy.df <- dd
dummy.df[nrow(dd) + 1:2,"Pattern"] <- unique(dd$Pattern)[-3]
dummy.df[nrow(dd) + 1:2,"Method"] <- "ZZZ"
dummy.df[nrow(dd) + 1:2,"Complexity"] <- c("High","Low")
dummy.df$dummy <- interaction(dummy.df$Method,dummy.df$Pattern)
ggplot(dummy.df, aes(x = dummy, y = X1, fill = Method)) +
geom_boxplot(aes(fill = Method)) +
facet_grid(~Complexity) +
theme_light() +
theme(legend.position = 'bottom') +
guides(fill = guide_legend(nrow=1)) +
geom_line(aes(x = dummy,
group=interaction(Pattern,nsim)),
size = 0.35, alpha = 0.35, colour = I("#525252")) +
geom_point(aes(x = dummy,
group=interaction(Pattern,nsim)),
size = 0.35, alpha = 0.25, colour = I("#525252")) +
scale_x_discrete(labels = c("","X", "", "", "", "Y", "", "", "", "Z","","")) +
xlab("Pattern") +
scale_fill_brewer(breaks=c("A", "B", "C"), type="qual", palette="Paired")
dummy.df <- dd
dummy.df[nrow(dd) + 1:2,"Pattern"] <- unique(dd$Pattern)[-3]
dummy.df[nrow(dd) + 1:2,"Method"] <- "ZZZ"
dummy.df[nrow(dd) + 1:2,"Complexity"] <- c("High","Low")
dummy.df$dummy <- interaction(dummy.df$Method,dummy.df$Pattern)
dummy.df$fill <- interaction(dummy.df$Method, dummy.df$n)
dummy.df$dummy <- interaction(dummy.df$fill, dummy.df$Pattern)
dummy.df$dummy <- factor(dummy.df$dummy, levels = levels(dummy.df$dummy)[-c(4, 12, 20, 24)])
dummy.df$dummy[361:362] <- "A.10.Z" ## dummy variables to get rid of NAs
theme_set(theme_bw(base_size = 14))
ggplot(dummy.df, aes(x = dummy, y = X1, fill = fill)) +
geom_boxplot(aes(fill = fill),lwd=0.1,outlier.size = 0.01) +
facet_grid(~Complexity) +
theme(legend.position = 'bottom') +
guides(fill = guide_legend(nrow=1)) +
geom_line(aes(x = dummy,
group=interaction(Pattern,nsim,n)),
size = 0.35, alpha = 0.35, colour = I("#525252")) +
geom_point(aes(x = dummy,
group=interaction(Pattern,nsim,n)),
size = 0.35, alpha = 0.25, colour = I("#525252")) +
scale_x_discrete(labels = c("X", "Y", "Z"), breaks = paste("A.10.", c("X", "Y", "Z"), sep = ""),drop=FALSE) +
xlab("Pattern") +
scale_fill_brewer(breaks= levels(dummy.df$fill)[-c(4,8)], type="qual", palette="Paired")
This yields the following plot.
All is well, except with the legend. I would like the following: the dark colors to be in the First group titled "n=5" on the left, with "A", "B", "C" for the three dark colors, and the light colors to be to the right, in a Second group titled "n=10" on the right, with "A", "B", "C" for the three light colors. Sort of like in the link enter link description here above.
What I can not figure out is how to call the boxplot twice to mimic the solution there.
Is there a way to do this? Please feel free to let me know if the question is not clear.
Thanks again, in advance, for any help!
Adapting my answer on your former question this could be achieved like so:
library(ggplot2)
fill <- levels(dummy.df$fill)[-c(4,8)]
fill <- sort(fill)
labels <- gsub("\\.\\d+", "", fill)
labels <- setNames(labels, fill)
colors <- scales::brewer_pal(type="qual", palette="Paired")(6)
colors <- setNames(colors, fill)
library(ggnewscale)
ggplot(dummy.df, aes(x = dummy, y = X1, fill = fill)) +
geom_boxplot(aes(fill = fill), lwd=0.1,outlier.size = 0.01) +
scale_fill_manual(name = "n = 5", breaks= fill[grepl("5$", fill)], labels = labels[grepl("5$", fill)], values = colors,
guide = guide_legend(title.position = "left", order = 1)) +
new_scale_fill() +
geom_boxplot(aes(fill = fill), lwd=0.1,outlier.size = 0.01) +
scale_fill_manual(name = "n = 10", breaks = fill[grepl("10$", fill)], labels = labels[grepl("10$", fill)], values = colors,
guide = guide_legend(title.position = "left", order = 2)) +
facet_grid(~Complexity) +
theme(legend.position = 'bottom') +
guides(fill = guide_legend(nrow=1)) +
geom_line(aes(x = dummy,
group=interaction(Pattern,nsim,n)),
size = 0.35, alpha = 0.35, colour = I("#525252")) +
geom_point(aes(x = dummy,
group=interaction(Pattern,nsim,n)),
size = 0.35, alpha = 0.25, colour = I("#525252")) +
scale_x_discrete(labels = c("X", "Y", "Z"), breaks = paste("A.10.", c("X", "Y", "Z"), sep = ""),drop=FALSE) +
xlab("Pattern")
#> Warning: Removed 2 rows containing non-finite values (new_stat_boxplot).

Connecting points from two datasets with lines in ggplot2 in R

I want to connect datapoints from two datasets with a vertical line. The points that should be connected vertically have the same identifier (V), but I was hoping to keep the datasets separate.
Here is my figure so far:
d1 <- data.frame (V = c("A", "B", "C", "D", "E", "F", "G", "H"),
O = c(9,2.5,7,8,7,6,7,7.5),
S = c(6,5,3,5,3,4,5,6))
d2 <- data.frame (V = c("A", "B", "C", "D"),
O = c(10,3,7.5,8.2),
S = c(6,5,3,5))
scaleFUN <- function(x) sprintf("%.0f", x)
p<-ggplot(data=d1, aes(x=S, y=O), group=factor(V), shape=V) +
geom_point(size = 5, aes(fill=V),pch=21, alpha = 0.35)+
theme_bw()+
geom_point(data = d2, size=5, aes(fill=V), pch=22,colour="black")+
theme(legend.title=element_blank())+
xlab(expression(italic("S"))) + theme(text = element_text(size=25))+
ylab(expression(italic("O")))+ theme(text = element_text(size=25))+
theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())+
theme(axis.text.y=element_text(angle=90, hjust=1))+
theme(legend.position="none") # remove legend
print(p)
So the final figure would look something like this:
Can I do this with geom_line() without combining datasets (so the other formatting can be separate for each dataset)?
As bouncyball pointed out, you can use a separate data set (merged from d1 and d2) with geom_segment.
See the following:
ggplot(data = d1, aes(x = S, y = O), group = factor(V), shape = V) +
geom_point(size = 5, aes(fill = V), pch = 21, alpha = 0.35) +
geom_point(data = d2, size = 5, aes(fill = V), pch = 22, colour = "black") +
geom_segment(data = merge(d1, d2, by = 'V'),
aes(x = S.x, xend = S.y, y = O.x, yend = O.y)) +
guides(fill = FALSE)
Which yields:
You can add your themes also.

geom_point with different legend for fill and shape

Hmmm, maybe it's the temprature or I'm once again do not see the obvious ...
Here is my code:
library(ggplot2)
p <- ggplot()
p <- p + geom_point(aes(x = 1, y=1,bg = "I", group = "B"),pch = 21, size = 20, color=NA)
p <- p + geom_point(aes(x = 1, y=1.125,bg = "I", group = "B" ),pch = 22, size = 20, color=NA)
p <- p + geom_point(aes(x = 0.75, y=1.125,bg = "II", group = "A" ),pch = 22, size = 20, color=NA)
p <- p + geom_point(aes(x = 0.85, y=1.125,bg = "III", group = "A" ),pch = 22, size = 20, color=NA)
p <- p + scale_fill_manual(values= c("darkred", "darkblue", "darkgreen"), guide=guide_legend(override.aes = list(shape = 23)))
#p <- p + scale_fill_manual(values= c("darkred", "darkblue", "darkgreen"), guide=guide_legend(inherit.aes = FALSE))
p <- p + scale_shape_manual(labels = c("circle", "rectangle"),values = c(21, 22))
p
What I'm trying to achieve are basically two legends, one that reflects the color, in this example there are three different colors ("I", "II", and "III"), and two different types of shapes "rectangle" and "circle", there will never be more than these two different shapes.
Unfortunately there are some additional constraints ... I can't use the aesthetic color due to the fact that I'm also using geom_segment to somehow connect the shapes, and that is the second constraint, I have to use ggplot2.
But I'not able to produce these two legends, any help is appreciated ...
Why don't you store all your points in a data frame? It suits perfectly:
df <- data.frame(x = c(1, 1, 0.75, 0.85),
y = c(1, 1.125, 1.125, 1.125),
nr = c("I", "I", "II", "III"),
sh = c("B", "B", "A", "A"))
And now you can easily see the required mapping:
ggplot(df, aes(x, y, fill = nr, shape = sh)) +
geom_point(size = 20, color = NA) +
scale_shape_manual(labels = c("circle", "rectangle"), values = c(21, 22),
guide = guide_legend(override.aes = list(colour = 1))) +
scale_fill_manual(values = c("darkred", "darkblue", "darkgreen"),
guide = guide_legend(override.aes = list(shape = 23)))

Resources