ggplot single-value factor remove slashes from legend - r

I would like to show a simple geom_point, geom_smooth, and geom_abline with helpful legends. Unfortunately, the simple combination of geom_point and geom_smooth places a horizontal line across the point legend, adding geom_abline places a diagonal slash across all legends.
How can I create a simple visual where legend boxes include only a "point", a "line", and a "dashed line"?
Thanks
Examples:
Geom_point and geom_smooth
mtcars %>%
ggplot() +
geom_point(aes(x = carb, y = mpg, color = "Points")) +
geom_smooth(aes(x = carb, y = mpg, color = "Trendline")) +
theme(legend.position="bottom") +
labs(x = "carb",
y = "mpg",
color = "LEGEND")
Geom_point, geom_smooth, and geom_abline
mtcars %>%
ggplot() +
geom_point(aes(x = carb, y = mpg, color = "Points")) +
geom_smooth(aes(x = carb, y = mpg, color = "Trendline")) +
geom_abline(aes(slope = 1, intercept = 10, color = "ZCustom"), linetype = "dashed") +
theme(legend.position="bottom") +
labs(x = "carb",
y = "mpg",
color = "LEGEND")
Fixed geom_point legend, but slashes remain on other legends
mtcars %>%
ggplot() +
geom_point(aes(x = carb, y = mpg, color = "Points")) +
geom_smooth(aes(x = carb, y = mpg, color = "Trendline")) +
geom_abline(aes(slope = 1, intercept = 10, color = "ZCustom"), linetype = "dashed") +
scale_color_manual(values = c("red", "blue", "black"),
label = c("Points", "Trendline", "Custom"),
guide = guide_legend(override.aes = list(
linetype = c("blank", "solid", "dashed"),
shape = c(16, NA, NA)))) +
theme(legend.position="bottom") +
labs(x = "carb",
y = "mpg",
color = "LEGEND")
I have looked at these questions but did not understand how to apply to my situation:
ggplot legend slashes
ggplot2 legend for abline and stat_smooth
Include manually-added lines to ggplot2 guide legend

To me, the easiest way to get around this is to simply not list everything as color. You can use size, shape, alpha, etc. to break up the legend.
mtcars %>%
ggplot() +
geom_point(aes(x = carb, y = mpg, shape = "")) +
geom_smooth(aes(x = carb, y = mpg, alpha = "")) +
geom_abline(aes(slope = 1, intercept = 10, color = ""), linetype = "dashed") +
theme(legend.position="bottom") +
labs(x = "carb",
y = "mpg",
shape = "Points",
alpha = "Trendline",
color = "ZCustom")

My guess: you are using color for both geom_point and geom_smooth, so the legend attempts to combine both geoms.
When you use a different aes, the legend takes them as separate attribute/layers.
mtcars %>%
ggplot( aes(x = carb, y = mpg) ) +
geom_point( aes(fill = "Points") ) + # use 'fill' rather than 'color'
geom_smooth( aes(color = "Trendline") ) +
theme(legend.position = "bottom") +
labs(x = "carb", y = "mpg", color = "", fill = "")
Hope it helps!

Related

How to create an legend for ggplot for just one series of data and then add legend for additional horizontal lines?

I'm wanting to prepare a simple plot with some points and horizontal lines with a legend. The code below generates the desired plot and a legend but the legend symbols are combinations of the shape and line, when I would just like a shape for the shape and a line for the line.
dat <- iris %>% select(Sepal.Length)
dat$Type <- "Sepal.Length"
ggplot() +
geom_point(data = dat, aes(x = as.numeric(row.names(dat)), y = Sepal.Length, colour = Type), shape = 10, size = 2) +
geom_hline(aes(yintercept = 6, colour = "Some line"), linetype = "dashed")
Custom linetypes and shapes are assigned using scale_*_manual, like so:
dat %>%
ggplot() +
geom_point(aes(x = as.numeric(row.names(dat)), y = Sepal.Length, shape = Type), size = 2) +
geom_hline(aes(yintercept = 6, linetype = 'Some line')) +
scale_linetype_manual(values = c('Some line' = 'dashed')) +
scale_shape_manual(values = c('Sepal.Length' = 10))

How do I add legends when there are two y variables?

This is the code I used:
ggplot(Delays2006, aes(Month)) + geom_point(aes(y = delay), color = "blue", size = 2)
+ geom_point(aes(y=delay2)) + scale_x_discrete(limits=(month.name))
+ labs(x= "Months", y = "Delays in minutes") + ggtitle(" Flight Data 2006")
This is the output:
May I ask how do I add legends to delay and delay2 in this graph. Thank you.
If you want to have a legend you have to map on aesthetics, i.e. do geom_point(aes(y = delay, color = "delay")) + geom_point(aes(y=delay2, color = "delay2")). Colors and labels could then be set via scale_color_manual.
Using mtcars as example data:
library(ggplot2)
ggplot(mtcars, aes(mpg)) +
geom_point(aes(y = hp, color = "hp")) +
geom_point(aes(y = cyl, color = "cyl")) +
scale_color_manual(values = c(hp = "blue", cyl = "green"), labels = c(hp = "Horse Power", cyl = "Cylinders"))

How to add a legend manually for line chart

i need the plan legend
How to add a legend manually for geom_line
ggplot(data = impact_end_Current_yr_m_actual, aes(x = month, y = gender_value)) +
geom_col(aes(fill = gender))+theme_classic()+
geom_line(data = impact_end_Current_yr_m_plan, aes(x=month, y= gender_value, group=1),color="#288D55",size=1.2)+
geom_point(data = impact_end_Current_yr_m_plan, aes(x=month, y=gender_value))+
theme(axis.line.y = element_blank(),axis.ticks = element_blank(),legend.position = "bottom", axis.text.x = element_text(face = "bold", color = "black", size = 10, angle = 0, hjust = 1))+
labs(x="", y="End Beneficiaries (in Num)", fill="")+
scale_fill_manual(values=c("#284a8d", "#00B5CE","#0590eb","#2746c2"))+
scale_y_continuous(labels = function(x) format(x, scientific = FALSE)
The neatest way to do it I think is to add colour = "[label]" into the aes() section of geom_line() then put the manual assigning of a colour into scale_colour_manual() here's an example from mtcars (apologies that it uses stat_summary instead of geom_line but does the same trick):
library(tidyverse)
mtcars %>%
ggplot(aes(gear, mpg, fill = factor(cyl))) +
stat_summary(geom = "bar", fun = mean, position = "dodge") +
stat_summary(geom = "line",
fun = mean,
size = 3,
aes(colour = "Overall mean", group = 1)) +
scale_fill_discrete("") +
scale_colour_manual("", values = "black")
Created on 2020-12-08 by the reprex package (v0.3.0)
The limitation here is that the colour and fill legends are necessarily separate. Removing labels (blank titles in both scale_ calls) doesn't them split them up by legend title.
In your code you would probably want then:
...
ggplot(data = impact_end_Current_yr_m_actual, aes(x = month, y = gender_value)) +
geom_col(aes(fill = gender))+
geom_line(data = impact_end_Current_yr_m_plan,
aes(x=month, y= gender_value, group=1, color="Plan"),
size=1.2)+
scale_color_manual(values = "#288D55") +
...
(but I cant test on your data so not sure if it works)

How to provide multiple fill values using the same aesthetic

My goal is to pass separate values to change the colors used for fill aesthetics in different geoms.
For example:
ggplot(iris, aes(x = Species, y = Sepal.Length)) +
stat_summary(aes(fill = Species), color = 'black', geom = 'bar', fun.y = mean) +
geom_point(aes(fill = Species), color = 'black', shape = 21) +
scale_fill_manual(values = c('royal blue', 'red2', 'limegreen'))
In this plot, I would like to be able to use separate colors to fill the bars and points. Is this possible? I'm aware of using scale_fill_manual()
to set the colors to whatever values I want, but this will change the fills of both the bars and the points to the same colors.
Here is a semi-working example of what I am trying to do, however, the legend is off...
iris_j <- iris %>%
mutate(Species_bar = factor(paste0(Species, '_bar')))
color.groups <- c('royal blue', 'red2', 'limegreen', NA, 'royal blue', 'white')
names(color.groups) <- c(levels(iris_j$Species), levels(iris_j$Species_bar))
ggplot(iris_j, aes(x = Species, y = Sepal.Length)) +
stat_summary(aes(fill = Species_bar), color = 'black', geom = 'bar', fun.y = mean) +
geom_point(aes(fill = Species), color = 'black', shape = 21) +
scale_fill_manual(values = color.groups)
This is one of the limitations of ggplot—an aesthetic can only be mapped to a single variable. Generally speaking, I find it a reasonable limitation, as it forestalls a lot of confusing and hard-to-read graphs. That said, with some creativity, it can be worked around, e.g. by coloring the points with the color aesthetic, and then overplotting to add a stroke:
library(ggplot2)
ggplot(iris, aes(x = Species, y = Sepal.Length)) +
stat_summary(aes(fill = Species), color = 'black', geom = 'bar', fun.y = mean) +
geom_point(aes(color = Species)) + # add colored points
geom_point(color = 'black', shape = 21, show.legend = TRUE) + # add point strokes (including in legend)
scale_color_manual(values = c('royal blue', 'red2', 'limegreen')) + # define point colors
scale_fill_manual(values = c(NA, 'royal blue', 'white')) # define bar colors
To separate the legends, specify a different name for each. To add a stroke to the points in the legend, you'll need to effectively rebuild it in guide_legend. (According to the docs, supplying a named vector to show.legend should work, but in practice it fails.)
ggplot(iris, aes(x = Species, y = Sepal.Length)) +
stat_summary(aes(fill = Species), color = 'black', geom = 'bar', fun.y = mean) +
geom_point(aes(color = Species)) +
geom_point(color = 'black', shape = 21) +
scale_color_manual('points', values = c('royal blue', 'red2', 'limegreen'),
guide = guide_legend(override.aes = list(shape = 21, color = 'black',
fill = c('royal blue', 'red2', 'limegreen')))) +
scale_fill_manual('bars', values = c(NA, 'royal blue', 'white'))
Such an approach will not generalize to a plot where color is already being used otherwise.
Here are a few little things to try that you could build off of.
First up, if you don't need to use a filled shape, you can just map color to the species in geom_point, so you have a color scale and a fill scale. In this case, I changed the label for fill to mark it as being the means, to show how you can split these into two legends.
library(tidyverse)
light_colors <- c("#87CEEB", "#FFB6C1", "#FF8C69")
dark_colors <- c("#22A0D6", "#E33650", "#BF411B")
ggplot(iris, aes(x = Species, y = Sepal.Length)) +
stat_summary(aes(fill = Species), geom = "bar", fun.y = mean) +
geom_point(aes(color = Species)) +
scale_fill_manual(values = light_colors) +
scale_color_manual(values = dark_colors) +
labs(fill = "Mean by Species")
Second, if you do need a filled shape, let geom_point get a fill scale and hack the bars to have a color instead. One way to do that is by making what look like bars but are actually really big geom_segments. I changed the size in the legend to make the legend keys not ridiculously huge.
ggplot(iris, aes(x = Species, y = Sepal.Length)) +
stat_summary(aes(xend = Species, yend = 0, color = Species), geom = "segment", fun.y = mean, size = 30, lineend = "butt") +
geom_point(aes(fill = Species), color = "black", shape = 21) +
scale_fill_manual(values = light_colors) +
scale_color_manual(values = dark_colors, guide = guide_legend(override.aes = list(size = 4)))
Third way, make a data frame of averages and give it a variable to denote that it's got averages, then add a variable to the original data frame to denote that it's observations. Then you can map the interaction of type with species to get separate colors in one fill scale.
avgs <- iris %>%
group_by(Species) %>%
summarise(Sepal.Length = mean(Sepal.Length)) %>%
mutate(type = "Mean")
iris %>%
select(Species, Sepal.Length) %>%
mutate(type = "Observation") %>%
ggplot(aes(x = Species, y = Sepal.Length, fill = interaction(Species, type))) +
geom_col(data = avgs) +
geom_point(color = "black", shape = 21)
Not quite a perfect solution, but it may be a sufficient workaround
cols_1 <- c("red", "green", "blue")
cols_2 <- c("orange", "purple", "yellow")
ggplot(iris, aes(x = Species, y = Sepal.Length)) +
geom_point(aes(color = Species)) + # Using color instead of fill
stat_summary(aes(fill = Species), color = 'black', geom = 'bar', fun.y = mean, alpha = c(0.5, 0.05, 1)) +
scale_color_manual(values = cols_1) + # colors your points
scale_fill_manual(values = cols_2) # fills your Summary Bars
Adjust the colors, alpha, and other graphical parameters as you see fit.

Conditional text color in geom_text, scale_color_manual

Here I set colorRed to TRUE so the text is red. But when I set it to FALSE, the color is still red.
How to make the text color conditional on the value of colorRed?
library(ggplot2)
ann_text = data.frame(x = 1.5, y = max(mtcars$mpg), LABEL = "TEXT", colorRed = FALSE)
ggplot(mtcars, aes(x = factor(am), y = mpg)) + geom_boxplot() +
geom_text(data = ann_text, aes(x = x, y = y, label = LABEL, color = colorRed)) +
scale_color_manual(values = c('red', 'black'), guide = "none")
There is an important lesson here. Always pass a named vector to values and labels in the scales in order to ensure the intended mapping.
ggplot(mtcars, aes(x = factor(am), y = mpg)) +
geom_boxplot() +
geom_text(data = ann_text, aes(x = x, y = y, label = LABEL, color = colorRed)) +
scale_color_manual(values = c('TRUE' = 'red', 'FALSE' = 'black'), guide = "none")

Resources