I'd like to add a ggplot legend when we have separately added elements.
I'm aware of previous answers to this question, but for some reason I haven't been able to sort out my issue (Add legend to ggplot2 line plot). Thanks!
Fake data
library(ggplot2)
Month <- c(1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8,
8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12)
RR <- c(81.30271, 56.97511, 88.95428, 78.43363, 51.39398, 83.53967,
95.21302, 63.74089, 94.27137, 92.36272, 69.30449, 100.34571,
128.52172, 89.84833, 133.61527, 128.23847, 91.21498, 145.45016,
124.72499, 85.96523, 153.82113, 123.22878, 97.14116, 154.57004,
111.59289, 83.26763, 105.47873, 95.55557, 88.77395, 87.31212,
93.03579, 65.35055, 93.22413, 103.43140, 70.40292, 91.58487)
MI <- c(66.729379, 41.891775, 52.137614, 59.376967, 30.717318, 49.339675,
62.469691, 25.667561, 60.211374, 48.902722, 18.764486, 65.565712,
69.985054, 27.418330, 89.231939, 55.685138, 11.484980, 90.666826,
44.101654, -8.448102, 87.637798, 55.978782, 15.431156, 92.310042,
69.596228, 34.897628, 56.505393, 69.008904, 61.627285, 36.935451,
76.392457, 47.493886, 53.750796, 88.204738, 54.806257, 54.358201)
PE <- c(14.57333, 15.08333, 36.81667, 19.05667, 20.67667, 34.20000, 32.74333,
38.07333, 34.06000, 43.46000, 50.54000, 34.78000, 58.53667, 62.43000,
44.38333, 72.55333, 79.73000, 54.78333, 80.62333, 94.41333, 66.18333,
67.25000, 81.71000, 62.26000, 41.99667, 48.37000, 48.97333, 26.54667,
27.14667, 50.37667, 16.64333, 17.85667, 39.47333, 15.22667, 15.59667,
37.22667)
tt <- data.frame(Month, RR, MI, PE)
What I've done without sucess
ggplot(data = tt,
aes(x = factor(Month))) +
geom_boxplot(aes(y = RR, x = factor(Month)),
fill = "dodgerblue4", colour = "dodgerblue4",
alpha = 0.6) +
stat_summary(aes(y = RR, x = Month),
fun.y = mean,
geom = "smooth",
colour = "dodgerblue4") +
geom_boxplot(aes(y = MI, group = Month),
fill = "dimgray", colour = "dimgray",
alpha = 0.6) +
stat_summary(aes(y = MI, x = Month),
fun.y = mean,
geom = "smooth",
colour = "dimgray") +
geom_boxplot(aes(y = PE, group = Month),
fill = "firebrick", colour = "firebrick",
alpha = 0.6) +
stat_summary(aes(y = PE, x = Month),
fun.y = mean,
geom = "smooth",
colour = "firebrick") +
labs(x = "Months",
y = "Flux, mm") +
scale_fill_manual("",
breaks = c("dodgerblue4", "dimgray", "firebrick"),
labels = c("dodgerblue4", "dimgray", "firebrick")) +
theme_bw(base_size = 18)
You can fix your code with help of the a named vector to define color. Make sure to use color from named vector with in scope of aes for each element.
One of such option can be as:
# Named vector for color
lineColors <- c("RR" = "dodgerblue4", "MI" = "dimgray", "PE" = "firebrick")
ggplot(data = tt,
aes(x = factor(Month))) +
geom_boxplot(aes(y = RR, x = factor(Month), fill = "RR"),
colour = "dodgerblue4",
alpha = 0.6) +
stat_summary(aes(y = RR, x = Month, colour = "RR"),
fun.y = mean,
geom = "smooth") +
geom_boxplot(aes(y = MI, group = Month, fill = "MI"),
colour = "dimgray",
alpha = 0.6) +
stat_summary(aes(y = MI, x = Month, colour = "MI"),
fun.y = mean,
geom = "smooth"
) +
geom_boxplot(aes(y = PE, group = Month, fill = "PE"),
colour = "firebrick",
alpha = 0.6) +
stat_summary(aes(y = PE, x = Month, colour = "PE"),
fun.y = mean,
geom = "smooth"
) +
labs(x = "Months",
y = "Flux, mm") +
scale_colour_manual(name = "Type", values = lineColors) +
scale_fill_manual(name = "Type", values = lineColors) +
theme_bw(base_size = 18)
A more standard ggplot way, where data is provided in a long format. The boxes are dodged and overplotting is thereby avoided.
cols <- c("dimgray", "firebrick", "dodgerblue4")
ggplot(data.table::melt(setDT(tt), id = "Month", variable.factor = FALSE), aes(x = Month, y = value)) +
geom_boxplot(aes(group = interaction(Month, variable),
fill = variable), alpha = 0.6) +
stat_summary(aes(color = variable), fun.y = mean,
geom = "smooth") +
scale_fill_manual(values = cols) +
scale_color_manual(values = cols) +
scale_x_continuous(breaks = 1:12)
Related
I am trying to compare data from three groups and I would like to have a mean bar on every group and some jitter.
first <- c(1, 1.2, 2, 3, 4)
second <- c(5, 6, 7, 8, 9)
third <- c(10, 16, 17, 18, 19)
df <- data.frame(Value = c(first,second),
Cat = c(rep("first",length(first)), rep("second",length(second))),
xseq = c(seq_along(first),seq_along(second)))
library(ggplot2)
ggplot(df, aes(x = Cat, y = Value, color = Cat)) + geom_point()+xlab("")
df <- data.frame(Value = c(first,second, third),
Cat = c(rep("first",length(first)),
rep("second",length(second)),
rep("third",length(third))),
xseq = c(seq_along(first),
seq_along(second),
seq_along(third)))
library(ggplot2)
ggplot(df, aes(x = Cat, y = Value, color = Cat)) + geom_point()+xlab("")
Something like this?
library(ggplot2)
ggplot(df, aes(x = Cat, y = Value, color = Cat)) +
geom_errorbar(stat = "summary", width = 0.1, color = "black", alpha = 0.5) +
stat_summary(geom = "point", fun = mean, color = "black") +
geom_point(position = position_jitter(width = 0.1), shape = 18, size = 4) +
scale_color_brewer(palette = "Set2") +
theme_light(base_size = 16)
I am trying to draw a forest plot with different groups. The code I'm using looks like the following:
d = data.frame(Estimate = c(1.8,1.9,2.1,2.4,2.7,2.5),
Group = rep(c('Group A', 'Group B'), each = 3),
Method = rep(c('Method 1', 'Method 2', 'Method 3'), 2))
d$Lower = d$Estimate - 0.3
d$Upper = d$Estimate + 0.3
ggplot(data = d, aes(y = Group, x = Estimate, xmin = Lower, xmax = Upper, color = Method)) +
geom_point(size = 2, position=position_dodge(width = 0.5)) +
geom_linerange(position=position_dodge(width = 0.5)) +
geom_vline(xintercept = c(2, 2.5), linetype = "dashed")
And the resulting plot:
The vertical lines (2, 2.5) are the true group means. I want to limit these vertical lines to be within each group (i.e., the first one from bottom to the middle, the second one middle to top). Anyone know how to do this?
I've tried geom_segment() function but I think it requires a numerical y input, while it's a factor here.
Factors plotted on an axis are "really" numeric, but with labels added, so you can go ahead and add numeric segments:
ggplot(data = d, aes(y = Group, x = Estimate, xmin = Lower, xmax = Upper,
color = Method)) +
geom_point(size = 2, position=position_dodge(width = 0.5)) +
geom_linerange(position=position_dodge(width = 0.5)) +
geom_segment(data = data.frame(y = c(0.67, 1.67), x = c(2, 2.5),
xend = c(2, 2.5), yend = c(1.33, 2.33)),
aes(x, y, xend = xend, yend = yend),
inherit.aes = FALSE, linetype = 2)
Or, with a few tweaks:
ggplot(data = d, aes(y = Group, x = Estimate, xmin = Lower, xmax = Upper,
color = Method)) +
geom_linerange(position=position_dodge(width = 0.5), size = 1) +
geom_point(size = 3, position=position_dodge(width = 0.5), shape = 21,
fill = "white") +
geom_segment(data = data.frame(y = c(0.67, 1.67), x = c(2, 2.5),
xend = c(2, 2.5), yend = c(1.33, 2.33)),
aes(x, y, xend = xend, yend = yend),
inherit.aes = FALSE, linetype = 2) +
annotate("text", c(2, 2.5), c(1.5, 2.5), size = 6,
label = c("Group mean = 2", "Group mean = 2.5")) +
theme_minimal(base_size = 20) +
scale_color_brewer(palette = "Set1")
I would like to use my own filling colors (ex: c("red", "blue", "grey50", "black")) when using function scale_fill_binned() withing a ggplot code. How can I do this?
Here is a minimal reproducible example:
library(tidyverse)
dat <- mtcars %>%
group_by(cyl) %>%
summarise(n = n(),
mean_hp = mean(hp)) %>%
ungroup
ggplot(data = dat, aes(x = cyl, y = mean_hp, size = n, fill = n)) +
geom_point(shape = 21) +
scale_size_binned(breaks = c(8, 10, 12), guide = guide_bins(show.limits = T)) +
scale_fill_binned(breaks = c(8, 10, 12), guide = guide_bins(show.limits = T), type = "viridis") +
labs(x = "Cylinder", y = "Mean hp", fill = "Nb of cars", size = "Nb of cars") +
theme_minimal()
Here is what the output looks like:
To use this family of functions you need to provide a function that returns a an object with class "ScaleContinuous" "Scale" "ggproto" "gg" (i.e. the equivalent output to scale_fill_viridis_c)!
scale_fill_custom <- function (..., alpha = 1, begin = 0, end = 1, direction = 1,
option = "D", values = NULL, space = "Lab", na.value = "grey50",
guide = "colourbar", aesthetics = "fill") {
continuous_scale(aesthetics, scale_name = "custom",
palette = scales:::gradient_n_pal(c("red", "blue", "grey50", "black"),
values, space), na.value = na.value,
guide = guide, ...)
}
ggplot(data = dat, aes(x = cyl, y = mean_hp, size = n, fill = n)) +
geom_point(shape = 21) +
scale_size_binned(breaks = c(8, 10, 12), guide = guide_bins(show.limits = T)) +
scale_fill_binned(breaks = c(8, 10, 12), guide = guide_bins(show.limits = T),
type = scale_fill_custom) +
labs(x = "Cylinder", y = "Mean hp", fill = "Nb of cars", size = "Nb of cars") +
theme_minimal()
Note that you are using colour as a scale to be translated by the eye into numerically meaningful difference. The colours are interpolated between the manually applied points, so will not actually be your exact colours. If you wish to band your averages by colour it would be preferable to create a factor, then manually apply your theme.
ggplot(data = mutate(dat, n = cut(n, breaks = c(0, 8, 10, 12, 20))),
aes(x = cyl, y = mean_hp, size = n, fill = n)) +
geom_point(shape = 21) +
scale_size_discrete() +
scale_fill_manual(values = c("red", "blue", "grey50", "black")) +
labs(x = "Cylinder", y = "Mean hp", fill = "Nb of cars", size = "Nb of cars") +
theme_minimal()
With the comment of #teunbrand, I was able to come up with something.
cols <- c("red", "blue", "grey50", "black")
ggplot(data = dat, aes(x = cyl, y = mean_hp, size = n, fill = n)) +
geom_point(shape = 21) +
scale_size_binned(breaks = c(8, 10, 12), guide = guide_bins(show.limits = T)) +
labs(x = "Cylinder", y = "Mean hp", fill = "Nb of cars", size = "Nb of cars") +
theme_minimal() +
binned_scale(aesthetics = "fill", scale_name = "custom",
palette = ggplot2:::binned_pal(scales::manual_pal(values = cols)),
guide = "bins",
breaks = c(8, 10, 12), limits = c(min(dat$n), max(dat$n)), show.limits = T)
Here is what the output looks like:
I have timeseries data plotted and separated by timepoints that I'd like to label with subscripts. Below is the code I'm using to generate the figure and timepoint labels. I'd like for the -1, 3 and 6 to be subscripts. Thanks in advance!
timepoints=data.frame(date=as_datetime(c("2016-08-15" ,"2016-11-22",
"2017-02-25")), timepoint=c("T-1", "T3", "T6"))
TimeseriespH = ggplot(FinalSeaphox, aes(x=DTTM)) +
geom_line(aes(y=MpH, color = "Outer Bay", group = grp), size = 0.5) +
geom_line(aes(y=CpH, color = "Inner Bay", group = grp), size = 0.5) +
scale_x_datetime(labels = date_format("%b '%y"), date_breaks = "1
month", limits = as_datetime(c("2016-07-01","2017-04-19"))) +
labs(x = "", y = "pH") +
scale_y_continuous(limits = c(7.4,8.2)) +
geom_vline(xintercept = as_datetime("2016-12-01"), linetype = 2, color
= "black") +
geom_vline(xintercept = as_datetime("2016-08-26"), linetype = 2, color
= "black") +
geom_vline(xintercept = as_datetime("2017-03-06"), linetype = 2, color
= "black") +
geom_text(data=timepoints, mapping=aes(x=date, y=c(8.18, 8.18, 8.18),
label=timepoint), size=5, vjust=-0.4, hjust=0, inherit.aes = FALSE,
color = "black")
For the subscripts, you need to enclose between brackets:
timepoint = c("T[-1]", "T[3]", "T[6]")
Then use parse = TRUE in geom_text:
library(ggplot2)
library(lubridate)
timepoints=data.frame(
date = as_datetime(c("2016-08-15" ,"2016-11-22", "2017-02-25")),
Y = c(8, 8.1, 8)
timepoint = c("T[-1]", "T[3]", "T[6]")
)
ggplot(timepoints) +
geom_point(aes(x = date, y=Y), size = 3) +
geom_text(data=timepoints,
mapping=aes(x=date, y=c(8.18, 8.18, 8.18),
label = timepoint),
size=5, vjust=0.4, hjust=0, inherit.aes = FALSE,
color = "black", parse = TRUE)
I am trying to create a plot in R using ggplot that shows the difference between my two bars in a nice way.
I found an example that did part of what I wanted, but I have two major problems:
It is based on comparing groups of bars, but I only have two, so I added one group with both of them.
I would like to draw the arrow in nicer shape. I attached an image.
Code:
transactions <- c(5000000, 1000000)
time <- c("Q1","Q2")
group <- c("A", "A")
data <- data.frame(transactions, time, group)
library(ggplot2)
fun.data <- function(x){
print(x)
return(data.frame(y = max(x) + 1,
label = paste0(round(diff(x), 2), "cm")))
}
ylab <- c(2.5, 5.0, 7.5, 10)
gg <- ggplot(data, aes(x = time, y = transactions, fill = colors_hc[1], label = round(transactions, 0))) +
geom_bar(stat = "identity", show.legend = FALSE) +
geom_text(position = position_dodge(width = 0.9),
vjust = 1.1) +
geom_line(aes(group = group), position = position_nudge(0.1),
arrow = arrow()) +
stat_summary(aes(x = group, y = transactions),
geom = "label",
fun.data = fun.data,
fontface = "bold", fill = "lightgrey",
inherit.aes = FALSE) +
expand_limits(x = c(0, NA), y = c(0, NA)) +
scale_y_continuous(labels = paste0(ylab, "M"),
breaks = 10 ^ 6 * ylab)
gg
The arrows I am aiming for:
Where I am (ignore the ugliness, didn't style it yet):
This works, but you still need to play around a bit with the axes (or rather beautify them)
library(dplyr)
library(ggplot2)
transactions <- c(5000000, 1000000)
time <- c("Q1","Q2")
group <- c("A", "A")
my_data <- data.frame(transactions, time, group)
fun.data <- function(x){
return(data.frame(y = max(x) + 1,
label = as.integer(diff(x))))
}
my_data %>%
ggplot(aes(x = group, y = transactions, fill = time)) +
geom_bar(stat = 'identity', position = 'dodge') +
geom_text(aes(label = as.integer(transactions)),
position = position_dodge(width = 0.9),
vjust = 1.5) +
geom_line(aes(group = group), position = position_nudge(0.1),
arrow = arrow()) +
stat_summary(aes(x = group, y = transactions),
geom = "label",
size = 5,
position = position_nudge(0.05),
fun.data = fun.data,
fontface = "bold", fill = "lightgrey",
inherit.aes = FALSE)
Edit2:
y_limit <- 6000000
my_data %>%
ggplot(aes(x = time, y = transactions)) +
geom_bar(stat = 'identity',
fill = 'steelblue') +
geom_text(aes(label = as.integer(transactions)),
vjust = 2) +
coord_cartesian(ylim = c(0, y_limit)) +
geom_segment(aes(x = 'Q1', y = max(my_data$transactions),
xend = 'Q1', yend = y_limit)) +
geom_segment(aes(x = 'Q2', y = y_limit,
xend = 'Q2', yend = min(my_data$transactions)),
arrow = arrow()) +
geom_segment(aes(x = 'Q1', y = y_limit,
xend = 'Q2', yend = y_limit)) +
geom_label(aes(x = 'Q2',
y = y_limit,
label = as.integer(min(my_data$transactions)- max(my_data$transactions))),
size = 10,
position = position_nudge(-0.5),
fontface = "bold", fill = "lightgrey")