Single Legend When Using Group, Linetype and Colour in Ggplot2

How to combine linetype and color in legend when they are set by the same variables

Thanks to Roland, here is the answer:

require(ggplot2)
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442",
"#0072B2", "#D55E00", "#CC79A7")
cbPalette <- cbPalette[-1]

ggplot(subset(AR.df)) +
geom_line(aes(x = AR.coef, y = value, linetype = Type, colour = Type),size=1.25) +
xlab("AR(1) coefficient") +
ylab("Proportion") +
theme_bw() +
theme(legend.justification=c(1,-0.2),
legend.position=c(0.4,0.3),
legend.text=element_text(size=10),
legend.title=element_text(size=10),
axis.title.x=element_text(size=10),
axis.title.y=element_text(size = 10),
legend.key = element_blank(),
legend.background = element_rect(color="black",size = 0.1)) +
scale_colour_manual(values=cbPalette, name="Analysis Type") +
scale_linetype_manual(values=c("solid", "dashed"),name="Analysis Type") +
theme(legend.key.width=unit(3,"line"))

Sample Image

Single legend when using group, linetype and colour in ggplot2?

This might help:

 ggplot(X, aes(x=horizon, y=value, group=TPP, col=TPP, linetype=TPP))+geom_line(size=1)+
scale_linetype_manual(name="X", values = c("solid","dashed", "dotted"),labels=c("Low", "5","High")) +
scale_color_manual(name ="X", values = c("black", "red", "blue"),labels=c("Low", "5","High"))

Sample Image

If the labels defined in scale_color_manual and in scale_linetype_manual are different, or if they are specified in only one of them, you will obtain two different legends.

How to merge color, line style and shape legends in ggplot

Here is the solution in the general case:

# Create the data frames
x <- seq(0, 10, by = 0.2)
y1 <- sin(x)
y2 <- cos(x)
y3 <- cos(x + pi / 4)
y4 <- sin(x + pi / 4)
y5 <- sin(x - pi / 4)
df1 <- data.frame(x, y = y1, Type = as.factor("sin"), Method = as.factor("method1"))
df2 <- data.frame(x, y = y2, Type = as.factor("cos"), Method = as.factor("method1"))
df3 <- data.frame(x, y = y3, Type = as.factor("cos"), Method = as.factor("method2"))
df4 <- data.frame(x, y = y4, Type = as.factor("sin"), Method = as.factor("method2"))
df5 <- data.frame(x, y = y5, Type = as.factor("sin"), Method = as.factor("method3"))

# Merge the data frames
df.merged <- rbind(df1, df2, df3, df4, df5)

# Create the interaction
type.method.interaction <- interaction(df.merged$Type, df.merged$Method)

# Compute the number of types and methods
nb.types <- nlevels(df.merged$Type)
nb.methods <- nlevels(df.merged$Method)

# Set the legend title
legend.title <- "My title"

# Initialize the plot
g <- ggplot(df.merged, aes(x,
y,
colour = type.method.interaction,
linetype = type.method.interaction,
shape = type.method.interaction)) + geom_line() + geom_point()
# Here is the magic
g <- g + scale_color_discrete(legend.title)
g <- g + scale_linetype_manual(legend.title,
values = rep(1:nb.types, nb.methods))
g <- g + scale_shape_manual(legend.title,
values = 15 + rep(1:nb.methods, each = nb.types))
# Display the plot
print(g)

The result is the following:

The solution

  • Sinus curves are drawn as solid lines and cosinus curves as dashed lines.
  • "method1" data use filled circles for the shape.
  • "method2" data use filled triangle for the shape.
  • "method3" data use filled diamonds for the shape.
  • The legend matches the curve

To summarize, the tricks are :

  • Use the Type/Method interaction for all data representations (colour, shape,
    linetype, etc.)
  • Then manually set both the curve styles and the legends styles with
    scale_xxx_manual.
  • scale_xxx_manual allows you to provide a values vector that is longer than the actual number of curves, so it's easy to compute the style vector values from the sizes of the Type and Method factors

Combining linetype and color in ggplot2 legend

Here is one approach for you. I created a sample data given your data above was not enough to reproduce your graphic. I'd like to give credit to the SO users who posted answers in this question. The key trick in this post was to assign identical groups to shape and line type. Similarly, I needed to do the same for color and linetype in your case. In addition to that there was one more thing do to. I manually assigned specific colors and line types. Here, there are four levels (i.e., treatment1.AC, treatment1.AE, treatment2.EC, treatment2.EF) in the end. But I used interaction() and created eight levels. Hence, I needed to specify eight colors and line types. When I assigned a name to the legend, I realized that I need to have an identical name in both scale_color_manual() and scale_linetype_manual().

library(ggplot2)

set.seed(111)

mydf <- data.frame(year = rep(1999:2010, time = 4),
treatment.type = rep(c("AC", "AF", "EC", "EF"), each = 12),
treatment = rep(c("treatment1", "treatment2"), each = 24),
mean = c(runif(min = 0.3, max = 0.55, 12),
rep(NA, 5), runif(min = 0.3, max = 0.55, 7),
runif(min = 0.3, max = 0.55, 12),
rep(NA, 5), runif(min = 0.3, max = 0.55, 7)),
se = c(runif(min = 0.01, max = 0.03, 12),
rep(NA, 5), runif(min = 0.01, max = 0.03, 7),
runif(min = 0.01, max = 0.03, 12),
rep(NA, 5), runif(min = 0.01, max = 0.03, 7)),
stringsAsFactors = FALSE)

ggplot(data = mydf, aes(x = year, y = mean,
color = interaction(treatment, treatment.type),
linetype = interaction(treatment, treatment.type))) +
geom_point(show.legend = FALSE) +
geom_line() +
geom_errorbar(aes(ymin = mean-se, ymax = mean+se),width = 0.1, size = 0.5) +
scale_color_manual(name = "Treatment conditions", values = rep(c("blue", "blue", "red", "red"), times = 2)) +
scale_linetype_manual(name = "Treatment conditions", values = rep(c(1,2), times = 4))

Sample Image

match color, line type AND shape in a SINGLE legend ggplot2

  1. You’re getting different legends because you set the name and label args within scale_color_manual(), but not in your other scale specifications. You could fix this by copying label = c(A = "TYPE A", B = "TYPE B"), name = "Type:" to the other scale_*_manual() calls; however it’s easier and more succinct to just change the variable name and labels in the data before plotting, as below.
  2. You have color = "black" set within geom_point(), which is overriding the color aesthetic you set in ggplot(aes()). I also think alpha = 0.1 might be too light to show up, on my device at least (though that surprised me).
library(dplyr)
library(ggplot2)

data %>%
mutate(`Type:` = paste("TYPE", TYPE)) %>%
ggplot(aes(x = IND_TEST_SCORE, y = SCORE,
color = `Type:`, linetype = `Type:`, shape = `Type:`)) +
geom_point(alpha = 0.25) +
scale_shape_manual(values = c(16, 17)) + ## change shape type
stat_smooth(formula = y ~ x, method = lm, se = T) +
scale_linetype_manual(values = c("solid", "dashed")) +
scale_color_manual(values = c("yellow", "cadetblue2")) +
labs(x = "IND_TEST_SCORE",
y = "SCORE",
title = "Effect of IND_TEST_SCORE and TYPE on SCORE") +
theme_bw()

Sample Image

PS - you can also color the error bands by adding fill = Type:, setting an alpha level in stat_smooth(), and using your manual color scale for both color and fill by adding aesthetics = c("color", "fill"):

data %>%
mutate(`Type:` = paste("TYPE", TYPE)) %>%
ggplot(aes(x = IND_TEST_SCORE, y = SCORE,
color = `Type:`, fill = `Type:`, linetype = `Type:`, shape = `Type:`)) +
geom_point(alpha = 0.25) +
scale_shape_manual(values = c(16, 17)) + ## change shape type
stat_smooth(formula = y ~ x, method = lm, se = T, alpha = .15) +
scale_linetype_manual(values = c("solid", "dashed")) +
scale_color_manual(values = c("yellow", "cadetblue2"), aesthetics = c("color", "fill")) +
labs(x = "IND_TEST_SCORE",
y = "SCORE",
title = "Effect of IND_TEST_SCORE and TYPE on SCORE") +
theme_bw()

Sample Image

Combining color and linetype legends in ggplot

As you noted, ggplot loves long format data. So I recommend sticking with that.

Here I generate some made up data:

library(tibble)
library(dplyr)
library(ggplot2)
library(tidyr)

set.seed(42)

tibble(x = rep(1:10, each = 10),
y = unlist(lapply(1:10, function(x) rnorm(10, x)))) -> tbl_long

which looks like this:

# A tibble: 100 x 2
x y
<int> <dbl>
1 1 2.37
2 1 0.435
3 1 1.36
4 1 1.63
5 1 1.40
6 1 0.894
7 1 2.51
8 1 0.905
9 1 3.02
10 1 0.937
# ... with 90 more rows

Then I group_by(x) and calculate quantiles of interest for y in each group:

tbl_long %>% 
group_by(x) %>%
mutate(q_0.0 = quantile(y, probs = 0.0),
q_0.1 = quantile(y, probs = 0.1),
q_0.5 = quantile(y, probs = 0.5),
q_0.9 = quantile(y, probs = 0.9),
q_1.0 = quantile(y, probs = 1.0)) -> tbl_long_and_wide

and that looks like:

# A tibble: 100 x 7
# Groups: x [10]
x y q_0.0 q_0.1 q_0.5 q_0.9 q_1.0
<int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 2.37 0.435 0.848 1.38 2.56 3.02
2 1 0.435 0.435 0.848 1.38 2.56 3.02
3 1 1.36 0.435 0.848 1.38 2.56 3.02
4 1 1.63 0.435 0.848 1.38 2.56 3.02
5 1 1.40 0.435 0.848 1.38 2.56 3.02
6 1 0.894 0.435 0.848 1.38 2.56 3.02
7 1 2.51 0.435 0.848 1.38 2.56 3.02
8 1 0.905 0.435 0.848 1.38 2.56 3.02
9 1 3.02 0.435 0.848 1.38 2.56 3.02
10 1 0.937 0.435 0.848 1.38 2.56 3.02
# ... with 90 more rows

Then I gather up all the columns except for x, y, and the 10- and 90-percentile variables into two variables: key and value. The new key variable takes on the names of the old variables from which each value came from. The other variables are just copied down as needed.

tbl_long_and_wide %>% 
gather(key, value, -x, -y, -q_0.1, -q_0.9) -> tbl_super_long

and that looks like:

# A tibble: 300 x 6
# Groups: x [10]
x y q_0.1 q_0.9 key value
<int> <dbl> <dbl> <dbl> <chr> <dbl>
1 1 2.37 0.848 2.56 q_0.0 0.435
2 1 0.435 0.848 2.56 q_0.0 0.435
3 1 1.36 0.848 2.56 q_0.0 0.435
4 1 1.63 0.848 2.56 q_0.0 0.435
5 1 1.40 0.848 2.56 q_0.0 0.435
6 1 0.894 0.848 2.56 q_0.0 0.435
7 1 2.51 0.848 2.56 q_0.0 0.435
8 1 0.905 0.848 2.56 q_0.0 0.435
9 1 3.02 0.848 2.56 q_0.0 0.435
10 1 0.937 0.848 2.56 q_0.0 0.435
# ... with 290 more rows

This format will allow you to use both geom_ribbon() and geom_smooth() like you want to do because the variables for the lines are contained in value and grouped by key whereas the variables to be mapped to ymin and ymax are separate from value and are all the same within each x group.

tbl_super_long %>% 
ggplot() +
geom_ribbon(aes(x = x,
ymin = q_0.1,
ymax = q_0.9,
fill = "80% of observations"),
alpha = 0.2) +
geom_line(aes(x = x,
y = value,
color = key,
linetype = key)) +
scale_fill_manual(name = element_text("Statistic"),
guide = guide_legend(order = 1),
values = viridisLite::viridis(1)) +
scale_color_manual(name = element_blank(),
labels = c("Minimum", "Median", "Maximum"),
guide = guide_legend(reverse = TRUE, order = 2),
values = viridisLite::viridis(3)) +
scale_linetype_manual(name = element_blank(),
labels = c("Minimum", "Median", "Maximum"),
guide = guide_legend(reverse = TRUE, order = 2),
values = c("dotted", "dashed", "solid")) +
labs(x = "x", y = "y")

example plot

This data format with the long but grouped x and y variables plus the independent but repeated ymin, and xmin variables will allow you to use both geom_ribbon() and geom_smooth() and allow the linetypes to show up properly in the legend.

merging ggplot legends with linetype, shape and color with different aes

Since you've got only 4 colours but 7 shapes, and you want to merge these in the same legend, use the same aesthetic (variable) and repeat the first four colour values in the manual override. For the linetypes, you probably don't need them in the legend as you have the shapes (and colour) to distinguish the 7 different models.

ggplot(mock.df, aes(x=vec.dat, y=value, shape=variable, color=variable, linetype=variable)) +
geom_line()+
scale_linetype_manual(values = c(rep("dotted",4),"longdash","solid","solid"))+
geom_point(size=3) +
scale_color_manual(values=c(rep("#999999",4), "#E69F00", "#56B4E9","#008f39"))+
scale_shape_manual(values=c(15,16,17,18,19,3,8))

Sample Image

How can I combine legends for color and linetype into a single legend in ggplot?

Try this. Make a hash of the two fields you're using (mutate(hybrid = paste(Meteorological_Season_Factor, Treatment))), and then use that for both color and line type (aes(...col = hybrid, linetype = hybrid)), and then use named vectors for your values in both scale functions.

Is that what you're looking for? (P.S. I believe I used the labels and values you used in the order in which you used them... maybe that was the only problem is that you'd transposed a couple of them!)

SOtestplot <- test %>%
mutate(hybrid = paste(Meteorological_Season_Factor, Treatment)) %>%
filter(Species == "MEK", binned_alt <= 8000) %>%
ggplot(aes(y = binned_alt/1000, x = `50%`, col = hybrid, linetype = hybrid)) +
geom_path(size = 1.2) +
theme_tufte(base_size = 22) +
geom_errorbarh(aes(xmin =`25%`, xmax = `75%`), height = 0) +
theme(axis.title.x = element_text(vjust=-0.5),
axis.title.y = element_text(vjust=1.5),
panel.grid.major = element_line(colour = "grey80"),
axis.line = element_line(size = 0.5, colour = "black")) +
scale_color_manual(name = "Treatment & Season",
values = c(
`Summer Clean Marine` = "cornflowerblue",
`Winter Clean Marine` = "goldenrod3",
`Summer All Data` = "cornflowerblue",
`Winter All Data` = "goldenrod3"
)) +
scale_linetype_manual(name = "Treatment & Season",
values = c(
`Summer Clean Marine` = "solid",
`Winter Clean Marine` = "dashed",
`Summer All Data` = "solid",
`Winter All Data` = "dashed")
) +
xlab("MEK (ppt)") +
ylab("Altitude (km)")


Related Topics



Leave a reply



Submit