how to merge two legends with shapes in ggplot2? - r

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)))

Related

How to highlight specific lines in specific groups with ggplot2?

I'm trying to highlight (change the color) specific lines in a plot.
The input data looks like this:
dt <- data.frame(Marker = paste0('m', rep(seq(1,10), 10)),
Year = rep(1990:1999, each = 10),
Ahat = rnorm(100, 0.5, 0.1)) %>%
mutate(Group = if_else(Marker %in% c("m1", "m2", "m3"), "A",
if_else(Marker %in% c("m4", "m5", "m6"), "B",
if_else(Marker %in% c("m7", "m8"), "C", "D")) ) )
And the general plot can be created by:
ggplot(dt, aes(x = Year, y = Ahat, group = interaction(as.factor(Group), Marker), color = as.factor(Group) ) ) +
geom_line(alpha = 0.5, size = 0.5) +
theme_classic() +
scale_y_continuous(name = "Predicted Value", breaks = pretty_breaks()) +
scale_colour_manual(name = "Groups", values = c("black", "red", "blue", "orange")) +
facet_wrap(~Group)
What I'd like to do is to highlight (e.g. make some lines black) some specific lines in specific groups (e.g. "m1" and "m9").
I've tried using something like this gghighlight(Marker %in% c("m1", "m9")), but it doesn't work.
I'd like to have something like this (sorry for my poor drawing skills):
Any suggestion?
P.S: My real data has 50K markers.
Thank you!
One option would be to first group data in subgroups (nesting in the dataframe) and then build the plots...
library(tidyverse)
library(scales)
library(patchwork)
# 1. Create dataframe ----
dt <- data.frame(Marker = as.factor(paste0('m', rep(seq(1,10), 10))),
Year = rep(1990:1999, each = 10),
Ahat = rnorm(100, 0.5, 0.1)) %>%
mutate(Group = case_when(
Marker %in% c("m1", "m2", "m3") ~ "A",
Marker %in% c("m4", "m5", "m6") ~ "B",
Marker %in% c("m7", "m8") ~ "C",
TRUE ~ "D"))
# 2. Function to choose which Market of sub_df should be Highlight
getHighlightMarketBasedOntAhatValue <- function(sub_dt) {
sub_dt <- sub_dt %>%
group_by(Marker) %>%
mutate(mean_Ahat = mean(Ahat))
# using mean to choose Ahat is just a doomed example... also instead of a single value you could get an array of values.
# Here I am not using the index...1, 2... any more (as was in first solution), but the factor itself.
highlightMarket <- first(sub_dt$Marker[sub_dt$mean_Ahat == max(sub_dt$mean_Ahat)])
}
# 3. Function to build plot for sub_df
my_plot <- function(sub_dt, highlighted_one) {
custom_pallete = rep("grey", length(levels(sub_dt$Marker)))
names(custom_pallete) <- levels(sub_dt$Marker)
custom_pallete[highlighted_one] = "blue"
dt %>% ggplot(aes(x = Year,
y = Ahat,
color = as.factor(Marker))) +
geom_line(alpha = 0.5, size = 0.5) +
theme_classic() +
scale_y_continuous(name = "Predicted Value", breaks = pretty_breaks()) +
scale_colour_manual(name = "Marker", values = custom_pallete)
}
# 4. Main ----
# 4.1 Nesting ----
nested_dt <- dt %>%
group_by(Group) %>%
nest()
# 4.2 Choosing highlight Market for each subgroup ----
nested_dt <- nested_dt %>%
mutate(highlighted_one = getHighlightMarketBasedOntAhatValue(data[[1]]))
# 4.3 Build plots ----
nested_dt <- nested_dt %>%
mutate(plot = map2(.x = data,
.y = highlighted_one,
.f = ~ my_plot(.x, .y)))
# 4.4 Use patchwork ... ----
# to combine plots ... see patchwork help to find out how to
# manage titles, labels, etc.
nested_dt %>% pull(plot) %>% patchwork::wrap_plots()
```
One way could be to set color as Marker.
Then you can change the color of the Marker in this line
scale_colour_manual(name = "Groups", values = c("black", "red", "blue", "orange", "green", "black", "red", "blue", "orange", "green")) +
Change the colors as you like:
ggplot(dt, aes(x = Year, y = Ahat, group = interaction(as.factor(Group), Marker), color = Marker ) ) +
geom_line(alpha = 0.5, size = 0.5) +
theme_classic() +
scale_y_continuous(name = "Predicted Value", breaks = pretty_breaks()) +
scale_colour_manual(name = "Groups", values = c("black", "red", "blue", "orange", "green",
"black", "red", "blue", "orange", "green")) +
facet_wrap(~Group)

Using arrangeGrob to add a sub plot as a legend

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)

How do I create a legend for both color and shape with ggplotly

I have find on stackoverflow how to create a legend for both color and shape in ggplot2. This example works perfect :
state1 <- c(rep(c(rep("N", 7), rep("Y", 7)), 2))
year <- rep(c(2003:2009), 4)
group1 <- c(rep("C", 14), rep("E", 14))
group2 <- paste(state1, group1, sep = "")
beta <- c(0.16,0.15,0.08,0.08,0.18,0.48,0.14,0.19,0.00,0.00,0.04,0.08,0.27,0.03,0.11,0.12,0.09,0.09,0.10,0.19,0.16,0.00,0.11,0.07,0.08,0.09,0.19,0.10)
lcl <- c(0.13,0.12,0.05,0.05,0.12,0.35,0.06,0.13,0.00,0.00,0.01,0.04,0.20,0.00,0.09,0.09,0.06,0.06,0.07,0.15,0.11,0.00,0.07,0.03,0.05,0.06,0.15,0.06)
ucl <- c(0.20,0.20,0.13,0.14,0.27,0.61,0.28,0.27,0.00,1.00,0.16,0.16,0.36,0.82,0.14,0.15,0.13,0.13,0.15,0.23,0.21,0.00,0.15,0.14,0.12,0.12,0.23,0.16)
data <- data.frame(state1,year,group1,group2,beta,lcl,ucl)
pd <- position_dodge(.65)
essai <- ggplot(data = data,aes(x= year, y = beta, colour = group2,shape = group2)) +
geom_point(position = pd, size = 4) +
geom_errorbar(aes(ymin = lcl, ymax = ucl), colour = "black", width = 0.5, position = pd) +
scale_colour_manual(name = "Treatment & State",
labels = c("Control, Non-F", "Control, Flwr", "Exclosure, Non-F", "Exclosure, Flwr"),
values = c("blue", "red", "blue", "red")) +
scale_shape_manual(name = "Treatment & State",
labels = c("Control, Non-F", "Control, Flwr", "Exclosure, Non-F", "Exclosure, Flwr"),
values = c(19, 19, 17, 17))
essai
But when I try make it ggplotly, legend is duplicated and labels are displayed...
library(ggplotly)
ggplotly(essai)
Thank you for help
Have you considered using the plot_ly() function to accomplish this? If I've read this correctly, need one legend of both color and shape to work with an interactive plot. I've linked their reference/attribute page.
plottest <- plot_ly(data = data,x= year, y = beta,
type = "scatter", symbol = group2,
symbols = c("circle", "square", "triangle-down","triangle-up"),mode = "markers",
error_y = list(
type = "data",
symmetric = FALSE,
array = c(ucl),
arrayminus = c(lcl))
)

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)))

ggplot2 - Assign symbol fill based on factors

I am relatively new to R and new to ggplot. I have a large data set and would like to make plots where the symbol fill for any given data series on the plot is governed by a factor in the data set. An example data frame is shown below where both "Station" and "Flag" are factors
Day Station Value Flag
1 1 0.0 b
2 1 1.0 a
3 1 2.0 a
1 2 2.3 a
2 2 1.0 a
3 2 0.2 b
1 3 0.5 b
2 3 0.5 b
3 3 0.5 b
I can control symbol shape and color using the factor "Station" but also want to control symbol fill using the factor "Flag" where, for example, filled symbols are used for Flag = "a" and open symbols are used for Flag = "b" - any given station will have a mix of filled and open symbols. I can do this in the base plotting functions of R but can't get it to work in ggplot2. This would be very tedious to do for the real data set with many more stations or other factors of interest.
As I am new, the system won't let me post images (not enough reputation points) but here is the code I use to generate the figure I am looking for and the failed ggplot attempt - not shown are the numerous variations on "scale_fill_manual" that have not worked.
library(ggplot2)
data <- read.table("dfquestion.csv", header = TRUE, sep = ",", na.strings = "",
colClasses = c("Day" = "numeric",
"Station" = "factor",
"Value" = "numeric",
"Flag" = "factor"
)
)
##---------------------------------------------------
# Select parameter to graph
x.value <- "Day"
y.value <- "Value"
##---------------------------------------------------
# Subset data by Station
##---------------------------------------------------
Sta1 <- subset(data, Station == "1")
Sta2 <- subset(data, Station == "2")
Sta3 <- subset(data, Station == "3")
##---------------------------------------------------
#
# Set symbol colors and background.
# open symbols/ clear background = value below reporting limit
# Black = Station 1
# Red = Station 2
# Green = Station 3
#
##---------------------------------------------------
bg.list1 <- rep(0,length(Sta1$Flag))
bg.list1[Sta1$Flag == "a"] <- "black"
bg.list1[Sta1$Flag == "b"] <- NA
#
bg.list2 <- rep(0,length(Sta2$Flag))
bg.list2[Sta2$Flag == "a"] <- "red"
bg.list2[Sta2$Flag == "b"] <- NA
#
bg.list3 <- rep(0,length(Sta3$Flag))
bg.list3[Sta3$Flag == "a"] <- "green"
bg.list3[Sta3$Flag == "b"] <- NA
##---------------------------------------------------
#
# Symbol type
# circle = Sta1; pch = 21
# square = Sta2; pch = 22
# triangle = Sta3; pch = 24
#
##---------------------------------------------------
opar <- par(no.readonly=TRUE)
par(oma = c(0,1,0,2.5))
plot(Sta1$Day, Sta1$Value, type = "b", pch = 24, bg = c(bg.list1), cex = 1.2, col = "black",
xlim = c(min(Sta1$Day), max(Sta1$Day)),
ylim = c(range(na.omit(Sta1$Value),
na.omit(Sta2$Value),
na.omit(Sta3$Value)
)),
xlab = x.value,
ylab = y.value,
cex.lab = 1.25, cex.axis = 1.25,
)
points(Sta2$Day, Sta2$Value, type = "b", pch = 21, bg = c(bg.list2), cex = 1.2, col = "red")
points(Sta3$Day, Sta3$Value, type = "b", pch = 22, bg = c(bg.list3), cex = 1.2, col = "green")
###
##---------------------------------------------------
# Creates legend outside primary graph
par(fig = c(0, 1, 0, 1), oma = c(0,0,0,0), mar = c(0,0,0,0), new = TRUE)
plot(0,0, type = "n", xaxt = "n", yaxt = "n")
legend("topright", legend = c(paste("Sta1"),
paste("Sta2"),
paste("Sta3"),
paste(""),
paste("above"),
paste("below")
),
pch = c(21, 22, 24,
NA,
21, 21),
lty = c(NA), lwd = c(NA),
col = c("black", "red", "green",
NA,
"red", "red"),
pt.bg = c("black", "red", "green",
NA,
"red", NA
),
text.col = "black",
bty = "n", cex = 0.95,
inset=c(-0.01,0.14))
#
par(opar)
#
##---------------------------------------------------
# Attempt to do the same thing in ggplot
#
p <- ggplot(data = data, aes(x=Day, y=Value, shape = Station, color = Station, fill = Flag)) +
geom_line(size = 1) +
geom_point(size = 4)
p <- p + scale_shape_manual(values = c(21, 22, 23))
p <- p + scale_color_manual(values = c("black", "red", "green"))
# Adjust legend fills - filled = detect, open = non-detect
p <- p + guides(fill = guide_legend(override.aes = list(shape = 21, fill = c("black", NA))))
print(p)
I think that alpha might be of use here. I came up with a solution, but I'm not entirely happy with it. I thought alpha would just impact fill for points that need both color and fill but it changed the transparency of both. I ended up adding a second geom_point layer as a work around.
ggplot(data = dat, aes(x=Day, y=Value, shape = Station, fill = Station, color = Station)) +
geom_line(size = 1, show_guide = FALSE) +
geom_point(size = 4, aes(fill = NULL)) +
geom_point(size = 4, aes(alpha = Flag)) +
scale_shape_manual(values = c(21, 22, 23)) +
scale_color_manual(values = c("black", "red", "green")) +
scale_fill_manual(values = c("black", "red", "green")) +
scale_alpha_manual(values = c(1, 0)) +
guides(alpha = guide_legend(override.aes = list(shape = 21, fill = c("black", NA), alpha = c(1,1))))

Resources