ggplot2's line legends appear "crossed-out" - r

I'm creating a ggplot with two lines, each from separate geoms. As an example:
df = data.frame(
x.v = seq(0, 1, 0.025),
y.v = runif(41)
)
straight.line = data.frame(
Inter = c(0),
Slope = c(1)
)
p = ggplot() +
geom_point(
mapping = aes(
x = x.v,
y = y.v
),
data = df,
colour = "blue"
) +
geom_smooth(
mapping = aes(
x = x.v,
y = y.v,
colour = "line of best fit"
),
data = df,
method = "lm",
show.legend = NA
) +
geom_abline(
mapping = aes(
intercept = Inter,
slope = Slope,
colour = "y = x"
),
data = straight.line,
show.legend = NA
) +
guides(
fill = "none",
linetype = "none",
shape = "none",
size = "none"
)
This gives the output:
As you can see, the legend has weird diagonal lines through it. An answer to a similar question says this can be fixed by using show.legend = NA. However, as you can see in the code above, I did this and it did not change the result.
Does anybody know what is adding the diagonal lines in the legend and how else I can fix it please? Thanks.
EDIT: A question of if this is a duplicate of this. This may be the answer but how do I apply this when the answer in the link uses fill, and I use colour, please?
If I try
+ guides(colour = guide_legend(override.aes = list(colour = NULL)))
I get the error
Error in check.length("col") : 'gpar' element 'col' must not be length 0
and if I try
+ guides(colour = guide_legend(override.aes = listfill = NULL)))
I get the error
Error in `$<-.data.frame`(`*tmp*`, "fill", value = character(0)) :
replacement has 0 rows, data has 1

The following works:
library(ggplot2)
ggplot() +
geom_point(mapping = aes(x = x.v, y = y.v),
data = df, colour = "blue") +
geom_smooth(mapping = aes(x = x.v, y = y.v, colour = "line of best fit"),
data = df, method = "lm", show.legend = NA) +
geom_abline(mapping = aes(intercept = Inter, slope = Slope, colour = "y = x"),
data = straight.line, show.legend = FALSE) +
guides(fill = "none", linetype = "none", shape = "none", size = "none")
The code can be made a little bit less repetitive and we can leave out some things (liek the guide-call):
ggplot(data = df, mapping = aes(x = x.v, y = y.v)) +
geom_point(colour = "blue") +
geom_smooth(aes(colour = "line of best fit"), method = "lm") +
geom_abline(mapping = aes(intercept = Inter, slope = Slope, colour = "y = x"),
data = straight.line, show.legend = FALSE)
Why do we need to use show.legend = FALSE here and not show.legend = NA?
From the documentation:
show.legend
logical. Should this layer be included in the legends? NA, the default, includes if any aesthetics are mapped. FALSE never includes, and TRUE always includes. It can also be a named logical vector to finely select the aesthetics to display
This means that is we use show.legend = NA for the geom_abline-call we use this layer in the legend. However, we don't want to use this layer and therefore need show.legend = FALSE. You can see that this does not influence, which colors are included in the legend, only the layer.
Data
set.seed(42) # For reproducibilty
df = data.frame(x.v = seq(0, 1, 0.025),
y.v = runif(41))
straight.line = data.frame(Inter = 0, Slope = 1)

Related

Change ggplot2 legend order without changing the manually specified aesthetics

I need to make a graph with multiple kinds of data on it, and I'm plotting one type of data with lines and one type with points. I've added a manually specified legend to show which type is points and which is lines (admittedly, my approach is a bit hacky), and that's working except for the legend order. Here's a dummy example:
DF1 <- data.frame(X = 1:10,
Y = c(1:10*0.5, 1:10*0.25),
Fruit = rep(c("mango", "kiwi"), each = 10))
DF2 <- data.frame(X = 1:10,
Y = c(1:10*2, 1:10*4),
Cat = rep(c("tabby", "calico"), each = 10))
Empty <- data.frame(X = mean(DF$X),
Y = as.numeric(NA),
# Q = c(0, 1))
Type = c("Cat", "Fruit"))
Mygraph <- ggplot(DF1, aes(x = X, y = Y, color = Fruit)) +
geom_point() +
geom_line(data = DF2, aes(x = X, y = Y, linetype = Cat),
inherit.aes = F) +
labs(color = NULL, linetype = NULL) +
geom_point(data = Empty, aes(x = X, y = Y, alpha = Type),
inherit.aes = F) +
geom_line(data = Empty, aes(x = X, y = Y, alpha = Type),
inherit.aes = F) +
scale_alpha_manual(
name = "Type of item", values = c(1, 1),
breaks = c("Fruit", "Cat"),
guide = guide_legend(override.aes =
list(linetype = c("blank", "solid"),
shape = c(16, NA)))) +
theme_bw()
Mygraph
This graph looks pretty good:
But check out what happens to the "Type of item" bit when I try to specify the order:
Mygraph +
guides(alpha = guide_legend(order = 1),
linetype = guide_legend(order = 2),
color = guide_legend(order = 3))
Why do my specified aesthetics go away? How can I both specify what that part of the legend should look like and also specify that the order of the three parts of the legend should be 1. alpha, 2. linetype, and then 3. color?
You were attempting to override aesthetics for alpha in two places (ie guides() and scale_alpha...()), and ggplot was choosing to just interpret one of them. I suggest including your shape override with your legend order override, like this:
library(ggplot2)
ggplot(DF1, aes(x = X, y = Y, color = Fruit)) +
geom_point() +
geom_line(data = DF2, aes(x = X, y = Y, linetype = Cat), inherit.aes = F) +
labs(color = NULL, linetype = NULL) +
geom_point(data = Empty, aes(x = X, y = Y, alpha = Type), inherit.aes = F) +
geom_line(data = Empty, aes(x = X, y = Y, alpha = Type), inherit.aes = F) +
scale_alpha_manual(name = "Type of item", values = c(1, 1), breaks = c("Fruit", "Cat")) +
guides(alpha = guide_legend(order = 1,
override.aes=list(linetype = c("blank", "solid"),
shape = c(16,NA))),
linetype = guide_legend(order = 2),
color = guide_legend(order = 3)) +
theme_bw()
data:
DF1 <- data.frame(X = 1:10,
Y = c(1:10*0.5, 1:10*0.25),
Fruit = rep(c("mango", "kiwi"), each = 10))
DF2 <- data.frame(X = 1:10,
Y = c(1:10*2, 1:10*4),
Cat = rep(c("tabby", "calico"), each = 10))
Empty <- data.frame(X = mean(DF1$X),
Y = as.numeric(NA),
Type = c("Cat", "Fruit"))

How to plot geom_point alone plus geom_point with position_dodge

I struggling on how I can plot my real values, present in the real_values vector, next to the estimates values. My problem here is that the estimates values have a range (via the geom_errorbar), and for the real values I would like to plot just the point, in black, on the left side of each of the 10 estimates.
Here's an example of what I tried:
est_values = rnorm(20)
real_values = rnorm(10)
dat_ex = data.frame(
xvalues = 1:10,
values = est_values,
method = c(rep("A",10),rep("B",10)),
ic_0.025 = c(est_values - rnorm(20,1,0.1)),
ic_0.975 = c(est_values + rnorm(20,1,0.1)))
ggplot(dat_ex) +
#geom_point(aes(x = 1:10, y= real_values), size = 2) +
geom_point(aes(x = xvalues, y= values, group = method, colour = method), position=position_dodge(.9), size = 3) +
geom_errorbar(aes(x = xvalues, y= values, group = method, colour = method,ymin = ic_0.025, ymax = ic_0.975), size = 1.3,position=position_dodge(.9), width = .2)
ggplot generally works best with data in data frames. So we put your real_values in a data frame and plot them in a separate layer, and "nudge" them to the left, as requested:
ggplot(dat_ex) +
geom_point(aes(x = xvalues, y= values, group = method, colour = method), position=position_dodge(.9), size = 3) +
geom_errorbar(aes(x = xvalues, y= values, group = method, colour = method,ymin = ic_0.025, ymax = ic_0.975), size = 1.3,position=position_dodge(.9), width = .2) +
geom_point(
data = data.frame(values = real_values, xvalues = dat_ex$xvalues),
aes(x = xvalues, y = values),
position = position_nudge(x = -.4),
color = "black")
A nicer method might be to put them all in the same data frame. This can simplify the code and will automatically put them in the legend.
library(dplyr)
dat_ex = data.frame(
xvalues = 1:10,
values = real_values,
method = "real"
) %>%
bind_rows(dat_ex) %>%
mutate(method = factor(method, levels = c("real", "A", "B")))
ggplot(dat_ex, aes(x = xvalues, y = values, color = method)) +
geom_point(position=position_dodge(.9), size = 3) +
geom_errorbar(aes(ymin = ic_0.025, ymax = ic_0.975, group = method),
size = 1.3, position=position_dodge(.9), width = .2) +
scale_color_manual(values = c("real" = "black", "A" = "orange", "B" = "blue"))
I would add real_values to your data as another level of method, so they will be dodged along with "A" and "B" (and included in the legend):
library(ggplot2)
dat_ex <- rbind(
dat_ex,
data.frame(
xvalues = 1:10,
values = real_values,
method = "Real",
ic_0.025 = NA_real_,
ic_0.975 = NA_real_
)
)
# arrange so "Real" is on the left
dat_ex$method <- factor(dat_ex$method, levels = c("Real", "A", "B"))
ggplot(dat_ex) +
geom_point(aes(x = xvalues, y= values, group = method, colour = method), position=position_dodge(.9), size = 3) +
geom_errorbar(aes(x = xvalues, y= values, group = method, colour = method,ymin = ic_0.025, ymax = ic_0.975), size = 1.3,position=position_dodge(.9), width = .2) +
scale_colour_manual(values = c("black", "forestgreen", "royalblue"))

How to supress/combine the legend for two geoms in R

Im making a scatterplot which shows a value plotted against the date since symptom onset. These patients are categorised based on disease severity, and i wanted to show how the values change over time in each severity category. I have coloured the dots based on severity score, but i prefer to use shape =21 so i can have a border. I also draw a line to see the trend, and i want that coloured in the same way, however, this has added another legend and it looks complicated. This issue doesnt happen if use a different shape that isnt filled, because scale_colour_manual can be used for both the lines and the dots, but i dont think it looks as nice. Any idea how i can fix this?
IC50SymObySS <- ggplot(data = isaric) +
geom_point(mapping = aes(x = Days_since_onset, y = log2IC50, fill = Severity_score), size = 2, colour = "black", shape = 21)+
geom_smooth(mapping = aes(x = Days_since_onset, y = log2IC50, colour = Severity_score), se = FALSE)+
scale_fill_manual(breaks=c("1","2","3","4","5"),
values=c("1" = "lightblue1","2" = "lightblue3","3" = "lightblue4","4" = "lightcoral","5" = "firebrick2"),
labels=c("1","2","3","4","5"),
name = "Severity Score")+
scale_colour_manual(values=c("1" = "lightblue1","2" = "lightblue3","3" = "lightblue4","4" = "lightcoral","5" = "firebrick2"))+
theme_minimal()+
JTheme+
ylab("Serum Log2 IC50")+
xlab("Days Since Symptom Onset")+
guides(colour = guide_legend(title.position = "top", title.hjust = 0.5))
IC50SymObySS
As per this answer, you need to use identical name and labels values for both fill and colour scale.
library(ggplot2)
library(dplyr)
isaric <- transmute(iris,
Days_since_onset = (Sepal.Length - 4)^3,
log2IC50 = Sepal.Width * 3,
Severity_score = cut(Petal.Length, breaks = quantile(Petal.Length, prob = 0:5 / 5), labels = 1:5))
ggplot(data = isaric) +
geom_smooth(mapping = aes(x = Days_since_onset, y = log2IC50, colour = Severity_score), se = FALSE)+
geom_point(mapping = aes(x = Days_since_onset, y = log2IC50, fill = Severity_score), size = 2, colour = "black", shape = 21)+
scale_colour_manual(
name = "Severity Score",
values=c("1" = "lightblue1","2" = "lightblue3","3" = "lightblue4","4" = "lightcoral","5" = "firebrick2"),
labels=c("1","2","3","4","5"))+
scale_fill_manual(
name = "Severity Score",
breaks=c("1","2","3","4","5"),
values=c("1" = "lightblue1","2" = "lightblue3","3" = "lightblue4","4" = "lightcoral","5" = "firebrick2"),
labels=c("1","2","3","4","5"))+
theme_minimal()+
ylab("Serum Log2 IC50")+
xlab("Days Since Symptom Onset")+
guides(colour = guide_legend(title.position = "top", title.hjust = 0.5))

Break legend in two columns while keeping shape override

I have a ggplot problem. Here is the example data:
df <- data.frame(x = rep(1:5,5),
type2 = c(rep(letters[1:2],each = 10),rep("c",5)),
type1 = rep(LETTERS[1:5],each = 5),
value = unlist(lapply(-2:2,function(a){rnorm(5,mean = a, sd = 1)})))
library(ggplot2)
plotcolor <- c( "#99d8c9","#2ca25f","#cbc9e2","#9e9ac8","#e34a33")
p <- ggplot(df,aes(x,value,color = type1,fill = type1,shape = type2))+
geom_point(size = 5)+
theme_light()+
labs(title = "",
color = "Method",
fill = "Method",
shape = "")+
geom_hline(yintercept = 0)+
guides(colour = guide_legend(override.aes = list(shape = c(21,21,24,24,22),
linetype = c(rep("blank",5)),
fill = plotcolor,
color = plotcolor)))+
scale_shape(guide = FALSE)+
scale_colour_manual(values = plotcolor)
p
which gives
Now I want to split the legend into two columns, for space reasons. I tried
p + guides(color=guide_legend(ncol=2))
but it remove the override part of my legend, letting just points:
p + guides(color=guide_legend(ncol=2),
fill =guide_legend(ncol=2) ,
shape = guide_legend(ncol=2))
didn't work either. Does anyone have an idea on how to deal with this particular problem?
You can specify ncol within the existing guide_legend (do not use it multiple times):
guides(colour = guide_legend(override.aes = list(shape = c(24,24,22,22,21),
linetype = c(rep("blank",5)),
fill = plotcolor,
color = plotcolor),
ncol = 2))+

R add legend for multiple layers

I want to add a legend for the plot, but it doesn't work,
can anyone please help me to see where it went wrong.
this is the code.
ggplot(data = dfNorm1, aes(x = X)) +
geom_col(aes(y = Government_suppliment),
fill = "#0000FF", color = "white", alpha = 0.8) +
geom_smooth(data = subset(dfNorm1,X >= 24), aes(y = Government_suppliment),
method = "lm", se = FALSE, color = "#FF4040",
linetype = "dashed", size = 0.7) +
geom_smooth(data = subset(dfNorm1, X <= 24), aes(y = Government_suppliment),
method = "lm", se = FALSE, color = "#FF4040",
linetype = "dashed", size = 0.7) +
geom_vline(xintercept = 24.5, size = 0.8, alpha = 0.8) +
geom_line(aes(y = Poverty_funds),
color = "#FF0000", size = 1, alpha = 0.7) +
geom_line(aes(y = MLI), color = "#EF3EFF", size = 1,
alpha = 0.8) +
scale_fill_manual(name = "",values = c("bar.label" = "#0000FF")) +
scale_color_manual(name = "", values = c("line.label1" = "#FF0000", "line.label2" = "#EF3EFF",
"line.labeld" = "#FF4040"))
You usually can produce a legend by setting aes(color = column_title) in one of your geom layers. This code doesn't particularly make sense because you are referencing more than one y-axis without creating a second y-axis (a bad habit if you are trying to do so). Is there a way you can post more relevant code or a reproducible example so people can see exactly what you're trying to do?

Resources