Missing Dots and Line Style in Figure Legend with ggplot2 - r

After running the following commands:
Population <- c("A", "A", "A", "A", "B", "B", "B", "B")
Group <- rep(c("Experimental", "Experimental", "Control", "Control"), 2)
wave <- rep(c("Pretest", "Posttest"), 4)
outcome <- c(-.3, -.2, -.3, .4, -.6, -.5, -.6, .6)
ci <- rep(c(.13, .14), 4)
df <- data.frame(Population, Group, wave, outcome, ci)
df$wave <- factor(df$wave,levels = c('Pretest','Posttest'))
library(ggplot2)
pd <- position_dodge(0.1)
ggplot(df, aes(x = wave, y = outcome, color = interaction(Population, Group), shape = Group, group = interaction(Population, Group))) +
geom_errorbar(aes(ymin = outcome - ci, ymax = outcome + ci), width = .25, position = pd, size=.5) +
geom_line(aes(linetype = Group), position = pd, size=1, show.legend = FALSE) +
geom_point(position = pd, size = 3.5, fill = "white", stroke = 1.25, show.legend = FALSE) +
scale_color_manual(values = c("#000000", "#606060", "#000000", "#606060")) +
scale_shape_manual(values = c(23, 21)) +
coord_cartesian(xlim = c(1.4, 1.6), ylim = c(-.91, .91)) +
labs(title = "Outcomes by Population and Study Group", x = "Time", y = "Outcome\nLower scores denote fewer instances", color = "Population and Study Group") +
theme(plot.title = element_text(hjust = 0.5), axis.text.x = element_text(color = "black"), axis.text.y = element_text(color = "black"), panel.background = element_rect(fill = "#F0F0F0"))
I generate a figure that does not have dots symbols or correct line styles in the legend:
How can I:
add the dots shown in the figure itself into the legend and
have the legend lines reflect that some of dotted lines in the figure?
TYIA.

The simplest way is to create another variable that would reflect the interaction instead of creating it on the fly. If we build the plot step by step, this below gives the dots and errorbars:
library(ggplot2)
pd <- position_dodge(0.1)
df$grp = paste(df$Population,df$Group,sep=".")
g = ggplot(df, aes(x = wave, y = outcome, color = grp, shape = grp))+
geom_errorbar(aes(ymin = outcome - ci, ymax = outcome + ci), width = .25, position = pd, size=.5) +
geom_point(position = pd, size = 3.5, fill = "white", stroke = 1.25) +
scale_color_manual(values = c("#000000", "#000000","#606060", "#606060")) +
scale_shape_manual(values = c(23,21,23,21)) +
coord_cartesian(xlim = c(1.4, 1.6), ylim = c(-.91, .91)) +
labs(title = "Outcomes by Population and Study Group", x = "Time", y = "Outcome\nLower scores denote fewer instances") +
theme(plot.title = element_text(hjust = 0.5), axis.text.x = element_text(color = "black"),
axis.text.y = element_text(color = "black"), panel.background = element_rect(fill = "#F0F0F0"))
print(g)
Then add the line while specifying the legend:
g +
geom_line(inherit.aes=FALSE,aes(x = wave, y = outcome,group=grp,linetype=grp)) +
scale_linetype_manual(values=c("solid","dashed","solid","dashed"))

Related

Changing legend symbol ggplot2 with multiple legends

I have a graph like attached and want to change the 'colour' legend to be a symbol of a square or circle not a line.
ggplot(sharkanovadata, aes(x=mean, y=sd), colour="Sharks")+
geom_point(alpha=0.2)+
geom_point(data=birdanovadata, colour= "#D22B2B", alpha=0.1)+
geom_abline(data=sharkanovadata, mapping=aes(colour="Sharks", slope=0.15, intercept=3.33, linetype= "Genetic"), size=1.2)+
geom_abline(data=sharkanovadata, mapping= aes( colour="Sharks", slope= 0.22, intercept=4.12, linetype= "Imputed"), size=1.2) +
geom_abline(data=birdanovadata, mapping=aes(colour= "Birds", slope=0.17, intercept=0.31, linetype= "Genetic"), size=1.2)+
geom_abline(data=birdanovadata, mapping=aes(colour= "Birds", slope=0.30, intercept=0.53, linetype="Imputed"), size=1.2)+
scale_colour_manual(values= c("Sharks" = "black", "Birds" = "#B03A2E"))+
xlab("Mean ED") +
ylab("Standard deviation of ED") +
ggtitle("Imputed species have a greater linear relationship of standard devation and mean", (size=30))+
theme(panel.background = element_rect(fill = "white"))+
theme(panel.grid.major = element_line(size=0.4, colour= "grey"))+
scale_linetype_manual(values= c("Genetic"= "longdash", "Imputed" = "solid"))+
theme(legend.key.size = unit(1, 'cm')) +
theme(plot.title = element_text(face = "bold"))
As the legend symbols aka key glyphs are determined by the geoms one option would be to switch the key_glyph for one of your geom_ablines. to point which will add a point to each legend. Afterwards we can tweak the the color and linetype legends via the override.aes argument of guide_legend where I opted for a square shape.
As you provided no data I use some fake random data to mimic your real data:
set.seed(123)
birdanovadata <- sharkanovadata <- data.frame(
mean = runif(1000, 0, 150),
sd = runif(1000, 0, 30)
)
library(ggplot2)
ggplot(sharkanovadata, aes(x = mean, y = sd), colour = "Sharks") +
geom_point(alpha = 0.2) +
geom_point(data = birdanovadata, colour = "#D22B2B", alpha = 0.1) +
geom_abline(data = sharkanovadata, mapping = aes(colour = "Sharks", slope = 0.15, intercept = 3.33, linetype = "Genetic"), size = 1.2, key_glyph = "point") +
geom_abline(data = sharkanovadata, mapping = aes(colour = "Sharks", slope = 0.22, intercept = 4.12, linetype = "Imputed"), size = 1.2) +
geom_abline(data = birdanovadata, mapping = aes(colour = "Birds", slope = 0.17, intercept = 0.31, linetype = "Genetic"), size = 1.2) +
geom_abline(data = birdanovadata, mapping = aes(colour = "Birds", slope = 0.30, intercept = 0.53, linetype = "Imputed"), size = 1.2) +
scale_colour_manual(values = c("Sharks" = "black", "Birds" = "#B03A2E")) +
xlab("Mean ED") +
ylab("Standard deviation of ED") +
ggtitle("Imputed species have a greater linear relationship of standard devation and mean", (size <- 30)) +
theme(panel.background = element_rect(fill = "white")) +
theme(panel.grid.major = element_line(size = 0.4, colour = "grey")) +
scale_linetype_manual(values = c("Genetic" = "longdash", "Imputed" = "solid")) +
theme(legend.key.size = unit(1, "cm")) +
theme(plot.title = element_text(face = "bold")) +
guides(color = guide_legend(override.aes = list(linetype = "blank", size = 8, shape = 15)),
linetype = guide_legend(override.aes = list(shape = NA)))

ggplot poinstrange with multiple categories - change fill color

I have the following plot:
ggplot() +
geom_pointrange(data=data_FA, mapping=aes(x=snr, y=median, ymin=p25, ymax=p75, colour=factor(method), group=method), position = pd) +
geom_hline(yintercept=FA_GT, linetype="dashed", color = "blue") +
theme(legend.title = element_blank(), legend.position = "none", panel.border = element_rect(colour = "gray", fill=NA, size=1),
plot.margin = unit( c(0,0.5,0,0) , units = "lines" )) +
labs( title = "", subtitle = "")
obtained from the following dataset:
For each group (red and blue) codified by the factor method, I want to see red/blue dots and lines with different transparency according to the factor subset. Does anyone know how to do that? In addition, how can I add more separation space between the two groups (red and blue)?
Thank you!
You can just map alpha to subset inside aes:
ggplot(data_FA) +
geom_pointrange(aes(snr, median, ymin = p25, ymax = p75,
colour = factor(method), group = method,
alpha = subset),
position = pd) +
geom_hline(yintercept = FA_GT, linetype = "dashed", color = "blue") +
scale_alpha_manual(values = c(0.3, 1)) +
theme_bw() +
theme(legend.position = 'none',
panel.border = element_rect(colour = "gray", fill = NA, size = 1),
plot.margin = unit( c(0,0.5,0,0), units = "lines" )) +
labs(title = "", subtitle = "")
Data
data_FA <- data.frame(X = c("X1", "X1.7", "X1.14", "X1.21"),
snr = "snr10",
subset = c("full", "full", "subset5", "subset5"),
method= c("sc", "trunc", "sc", "trunc"),
median = c(0.4883985, 0.4883985, 0.4923685, 0.4914260),
p25 = c(0.4170183, 0.4170183, 0.4180174, 0.4187472),
p75 = c(0.5617713, 0.5617713, 0.5654203, 0.5661565))
FA_GT <- 0.513
pd <- position_dodge2(width = 1)

How to plot multiple time series with a reverse barplot on the secondary axis using ggplot2?

I have a data frame having four columns as shown below (here I just put header of my actual data frame):
df <- tibble(Date=c("2007-05-01", "2007-05-02","2007-05-03", "2007-05-04", "2007-05-05"), Obs = c(0.16,0.15,0.17,0.19,0.14), Sim = c(0.17, 0.11, 0.21, 0.15, 0.13), Rain = c(0.1, 0.11, 0.04,0.21,0.5))
How can I plot the data such that the variables Obs and Sim are plotted on the primary y-axis and Rain is plotted as bars on a reverse secondary axis?
Here is the code I have tried thus far:
ggplot(df, aes(x=as.Date(Date))) +
geom_line(aes(y=Obs, color="red")) +
geom_line(aes(y=Sim, color="green")) +
geom_bar(mapping = aes(y = Rain), stat = "identity") +
scale_y_continuous(name = expression('Soil moisture, m'^"3"*' m'^"-3"),
sec.axis = sec_axis(~ 3 - .*0.5, name = "Precipitation (inch)"))
Here is my expected output:
Edit: Additionally, how can I insert a legend that corresponds to each line (i.e. Obs, Sim, and Rain)?
You can also make two separate plots and stack them on top of each other. This would be useful for people (myself included) who prefer not to use dual-axis plots.
library(tidyverse)
library(lubridate)
library(scales)
df <- tibble(Date = c("2007-05-01", "2007-05-02", "2007-05-03", "2007-05-04", "2007-05-05"),
Obs = c(0.16, 0.15, 0.17, 0.19, 0.14),
Sim = c(0.17, 0.11, 0.21, 0.15, 0.13),
Rain = c(0.10, 0.11, 0.04, 0.21, 0.5))
# convert data to long format
df_long <- df %>%
mutate(Date = as.Date(Date)) %>%
pivot_longer(-Date,
names_to = 'key',
values_to = 'value')
Soil moisture plot
sm1 <- ggplot(data = df_long %>% filter(key != 'Rain'),
aes(x = Date, y = value,
group = key,
shape = key,
linetype = key,
col = key)) +
xlab("") +
ylab(expression('Soil moisture, m'^"3"*' m'^"-3")) +
geom_line(lwd = 0.5) +
geom_point(size = 3, alpha = 0.6) +
scale_color_brewer("", palette = 'Dark2') +
scale_linetype_manual("", values = c(NA, 'solid')) +
scale_shape_manual("", values = c(19, NA)) +
theme_bw(base_size = 16) +
theme(legend.position = "bottom") +
theme(panel.border = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line()) +
theme(axis.title.x = element_blank()) +
theme(legend.key.size = unit(3, 'lines')) +
guides(color = guide_legend(override.aes = list(linetype = c(NA, 1),
alpha = 1.0,
shape = c(19, NA)),
nrow = 1, byrow = TRUE))
Precipitation plot
prec_long <- df_long %>%
filter(key == 'Rain') %>%
rename(Precipitation = matches("Rain"))
maxPrec <- 1.1 * max(prec_long$value, na.rm = TRUE)
p1 <- ggplot(data = prec_long, aes(x = Date, y = value)) +
# use `geom_linerange` to mimic `type = h` in Base R plot
# https://stackoverflow.com/questions/26139878/needle-plot-in-ggplot2
geom_linerange(aes(x = Date,
ymin = 0,
ymax = value),
color = "#2c7fb8",
size = 10) +
xlab("") +
ylab(paste("Precipitation (mm)", sep = "")) +
scale_x_date(position = "top") +
scale_y_reverse(expand = c(0, 0), limits = c(maxPrec, 0)) +
theme_bw(base_size = 16) +
theme(panel.border = element_blank(),
panel.grid.minor = element_blank(),
axis.line = element_line()) +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank()) +
theme(legend.position = "none")
Stack two plots on top of each other
### `cowplot` or `egg` package would work too
# install.packages("patchwork", dependencies = TRUE)
library(patchwork)
p1 / sm1 +
plot_layout(nrow = 2, heights = c(1, 2)) +
plot_annotation(title = "My plot",
subtitle = "Precipitation and Soil moisture")
Created on 2020-07-26 by the reprex package (v0.3.0)
Here's an approach using geom_rect.
Calculate the ratio between the maximum of the primary and secondary axes.
Store the maximum of the secondary reverse axis.
Plot the rectangles using the ymin as the maximum minus the value times the ratio.
Set the secondary axis ticks as the maximum minus the values divided by the ratio.
I added a BottomOffset parameter you could tweak if you want some extra space at the bottom on the secondary axis. I also went ahead and added the code to change the colors of the axes.
Edit: Now with a legend.
Ratio <- max(c(df$Obs, df$Sim), na.rm = TRUE) / max(df$Rain)
RainMax <- max(df$Rain,na.rm = TRUE)
BottomOffset <- 0.05
ggplot(df, aes(x=as.Date(Date))) +
geom_line(aes(y=Obs, color="1")) +
geom_line(aes(y=Sim, color="2")) +
geom_rect(aes(xmin=as.Date(Date) - 0.1,
xmax = as.Date(Date) + 0.1,
ymin = (BottomOffset + RainMax - Rain) * Ratio,
ymax = (BottomOffset + RainMax) * Ratio,
color = "3"),
fill = "red", show.legend=FALSE) +
geom_hline(yintercept = (BottomOffset + RainMax) * Ratio, color = "red") +
geom_hline(yintercept = 0, color = "black") +
labs(x = "Date", color = "Variable") +
scale_y_continuous(name = expression('Soil moisture, m'^"3"*' m'^"-3"),
sec.axis = sec_axis(~ BottomOffset + RainMax - . / Ratio, name = "Precipitation (inch)"),
expand = c(0,0)) +
scale_color_manual(values = c("1" = "blue", "2" = "green", "3" = "red"),
labels = c("1" = "Obs", "2" = "Sim", "3"= "Rain")) +
theme(axis.line.y.right = element_line(color = "red"),
axis.ticks.y.right = element_line(color = "red"),
axis.text.y.right = element_text(color = "red"),
axis.title.y.right = element_text(color = "red"),
axis.line.y.left = element_line(color = "blue"),
axis.ticks.y.left = element_line(color = "blue"),
axis.text.y.left = element_text(color = "blue"),
axis.title.y.left = element_text(color = "blue"),
legend.position = "bottom")

make a common legend and include unique legend attributes

I am attempting to combine a legend for two plots with a shared blue line and unique bar colors (read and yellow). When plotting the code below only two of the three colored id's show up in the combined legend (red and blue) but I want the third color to show up as well in the combined legend.
For example
red as beaver temp1
yellow as beaver temp2
blue as line
Here the line is a stand in for what I am using for a moving average. I don't think facet wrapping this will work as there are additional variable parameters in my code (i.e. scale_y_continuous that have different secondary axis scales) but below is a reproducible example of the basic idea.
library(datasets)
library(ggplot2)
library(cowplot)
data(beavers)
colour <- c("bevTemp1" = "red", "line" = "blue", "bevTemp2" = "yellow" )
bev1 <- ggplot(beaver1, aes(x=time, y = temp)) +
geom_bar(stat = "identity", aes(colour = "bevTemp1"), fill = "red")+
coord_cartesian(ylim = c(35, 38)) +
geom_line(size = 1.5, aes(color = "line"))+
scale_color_manual(values = colour,
guide = guide_legend(override.aes = list(
border=c(NA, NA),
fill=c("red","blue"))))+
theme(legend.title = element_blank(), legend.position = "none")
bev2<- ggplot(beaver2, aes(x=time, y = temp)) +
geom_bar(stat = "identity", aes(colour = "bevTemp2"), fill = "yellow")+
coord_cartesian(ylim = c(35, 38)) +
geom_line(size = 1.5, aes(color = "line"))+
scale_color_manual(values = colour,
guide = guide_legend(override.aes = list(
border=c(NA, NA),
fill=c("yellow","blue"))))+
theme(legend.title = element_blank(), legend.position = "none")
cowplot::plot_grid(
cowplot::plot_grid(
bev1 ,
bev2,
align = 'h'),
cowplot::get_legend(bev1 + theme(legend.position = "bottom")),
nrow = 2, rel_heights = c(4, 1))
Suggestions...
You could build a dummy plot just to get the desired legend and add it to the final plot. I would also suggest using the line legend for the geom_line.
library(ggplot2)
# Create dummy data
df <- data.frame(class = c("bevTemp1","bevTemp2"),
x = 1:2,
y = 2:3)
# Create dummy plot just to extract the desired legend
p1<- ggplot(df, aes(x=x,y=y)) +
geom_col(aes(fill = class))+
geom_line(aes(col = "line"), size = 1.5)+
scale_fill_manual(values = c("red","yellow")) +
scale_color_manual(values = "blue")+
theme(legend.title = element_blank())
cowplot::plot_grid(
cowplot::plot_grid(
bev1 ,
bev2,
align = 'h'),
# Add the legend of the dummy plot
cowplot::get_legend(p1 + theme(legend.position = "bottom")),
nrow = 2, rel_heights = c(4, 1))
get_legend just retrieves the legend from the plot passed to this function, here bev1. So you need to also show bev2Temp in the legend of plot 1. You can add an additional id column to your dataset, make it a factor variable but in both datasets include bevTemp1/bevTemp2 as levels. In ggplot in scale_fill_manual, if you set drop = FALSE, all levels are shown:
library(datasets)
library(ggplot2)
library(cowplot)
data(beavers)
colour <- c("bevTemp1" = "red", "bevTemp2" = "yellow")
beaver1$id <- "bevTemp1"
beaver1$id <- factor(beaver1$id, levels = c("bevTemp1", "bevTemp2"))
beaver2$id <- "bevTemp2"
beaver2$id <- factor(beaver2$id, levels = c("bevTemp1", "bevTemp2"))
bev1 <- ggplot(beaver1, aes(x=time, y = temp, fill = id)) +
geom_bar(stat = "identity")+
coord_cartesian(ylim = c(35, 38)) +
geom_line(size = 1.5, aes(color = "line"))+
theme(legend.title = element_blank(), legend.position = "none") +
scale_fill_manual(values = c("bevTemp1" = "red", "bevTemp2" = "yellow"),
drop = FALSE) +
scale_colour_manual(values = c("line" = "blue"))
bev2<- ggplot(beaver2, aes(x=time, y = temp, fill = id)) +
geom_bar(stat = "identity")+
coord_cartesian(ylim = c(35, 38)) +
geom_line(size = 1.5, aes(color = "line"))+
theme(legend.title = element_blank(), legend.position = "none") +
scale_fill_manual(values = c("bevTemp1" = "red", "bevTemp2" = "yellow"),
drop = FALSE) +
scale_colour_manual(values = c("line" = "blue"))
cowplot::plot_grid(
cowplot::plot_grid(
bev1 ,
bev2,
align = 'h'),
cowplot::get_legend(bev1 + theme(legend.position = "bottom")),
nrow = 2, rel_heights = c(4, 1))

Adding an additional point to ggplot

This question is a follow-up to this post: previous post
I have 28 variables, M1, M2, ..., M28, for which I compute certain statistics x and y.
library(ggplot2)
df = data.frame(model = factor(paste("M", 1:28, sep = ""), levels=paste("M", 1:28, sep = "")), a = runif(28, 1, 1.05), b = runif(28, 1, 1.05))
levels = seq(0.8, 1.2, 0.05)
Here is the plot:
ggplot(data=df) +
geom_polygon(aes(x=model, y=a, group=1), color = "black", fill = NA) +
geom_polygon(aes(x=model, y=b, group=1), color = "blue", fill = NA) +
coord_polar() +
scale_y_continuous(limits=range(levels), breaks=levels, labels=levels) +
theme(axis.text.y = element_blank(), axis.ticks = element_blank(), axis.title.x = element_blank(), axis.title.y = element_blank())
I would like to add a point the the plot, with y-value = 1 for M1 (model1). I tried adding:
geom_point(aes(y = 1, x = "M1"), color = "red", cex = 0.5)
but it doesn't work. Any idea what I am doing wrong?
Thanks for your help!
cex is not an argument for geom_point. Try size, e.g.
geom_point(aes(y = 1, x = "M1"), color = "red", size = 10)

Resources