I am using ggplot2 to create a scatter plot of 2 variables. I want to have these printed out on the caption portion of ggplot:
linear regression equation
r2 value
p-value
I am using brackets, new lines and stored values to concatenate everything together. I have attempted using expression(), parse() and bquote() functions but it only prints out the variable name and not the stored values.
This is the graph I have now. Everything looks great other than the R^2 part. Brackets seem to cause a lot of problems but I want to keep them (looks better in my opinion).This is my ggplot script. I am only concerned about the caption section at the end.
Difficult to work with the code you have provided as an example (see comment re: reproducible example), but I had my students complete a similar exercise for their homework recently, and can provide an example which you can likely generalize from. My approach is to use the TeX() function from the latex2exp package.
A psychologist is interested in whether she can predict GPA in graduate school from students' earlier scores on the Graduate Record Exam (GRE).
Setup the Toy Data and Regression Model
GPA <- c(3.70,3.18,2.90,2.93,3.02,2.65,3.70,3.77,3.41,2.38,
3.54,3.12,3.21,3.35,2.60,3.25,3.48,2.74,2.90,3.28)
GRE <- c(637,562,520,624,500,500,700,680,655,525,
593,656,592,689,550,536,629,541,588,619)
gpa.gre <- data.frame(GPA, GRE)
mod <- lm(GPA ~ GRE, data = gpa.gre)
mod.sum <- summary(mod)
print(cofs <- round(mod$coefficients, digits = 4))
aY <- cofs[[1]]
bY <- cofs[[2]]
print(Rsqr <- round(cor(GPA,GRE)^2, digits = 2))
Generate the Plot
require(ggplot2)
require(latex2exp)
p <- ggplot(data = gpa.gre, aes(x = GRE, y = GPA)) +
geom_smooth(formula = 'y ~ x', color ="grey40", method = "lm",
linetype = 1, lwd = 0.80, se = TRUE, alpha = 0.20) +
geom_point(color = "grey10", size = 1) +
labs(y = "Grade Point Average", x = "GRE Score") +
coord_cartesian(ylim = c(2.28, 3.82), xlim = c(498, 702), clip = "off") +
scale_y_continuous(breaks = seq(2.30, 3.80, 0.25)) +
scale_x_continuous(breaks = seq(500, 700, 50)) +
theme_classic() +
theme(axis.title.x = element_text(margin = unit(c(3.5,0,0,0), "mm"), size = 11.5),
axis.title.y = element_text(margin = unit(c(0,3.5,0,0), "mm"), size = 11.5),
axis.text = element_text(size = 10),
plot.margin = unit(c(0.25,4,1,0.25), "cm"))
# Use TeX function to use LaTeX
str_note <- TeX("\\textit{Note. ***p} < .001")
str_eq <- TeX("$\\hat{\\textit{y}} = 0.4682 + 0.0045 \\textit{x}$")
str_rsq <- TeX("$\\textit{R}^2 = .54***$")
# Create annotations
p + annotate("text", x = 728, y = 3.70, label = str_eq, size = 3.5,
hjust = 0, na.rm = TRUE) +
annotate("text", x = 728, y = 3.57, label = str_rsq, size = 3.5,
hjust = 0, na.rm = TRUE) +
annotate("text", x = 490, y = 1.80, label = str_note, size = 3.5,
hjust = 0, na.rm = TRUE)
Get Result
ggsave(filename = '~/Documents/gregpa.png', # your favourite file path here
width = unit(5, "in"), # width of plot
height = unit(4, "in"), # height of plot
dpi = 400) # resolution in dots per inch
Related
I don't want to display the Mean Absolute Values on my SHAP Summary Plot in R. I want an output similar to the one produced in python. What line of code will help remove the mean absolute values from the summary plot in R?
I'm currently using this line of code:
shap.plot.summary.wrap1(xgb_model, X = x, top_n = 10)
You can do this by sligtly modifying the source code of shap.plot.summary() as below:
shap.plot.summary.edited <- function(data_long,
x_bound = NULL,
dilute = FALSE,
scientific = FALSE,
my_format = NULL){
if (scientific){label_format = "%.1e"} else {label_format = "%.3f"}
if (!is.null(my_format)) label_format <- my_format
# check number of observations
N_features <- setDT(data_long)[,uniqueN(variable)]
if (is.null(dilute)) dilute = FALSE
nrow_X <- nrow(data_long)/N_features # n per feature
if (dilute!=0){
# if nrow_X <= 10, no dilute happens
dilute <- ceiling(min(nrow_X/10, abs(as.numeric(dilute)))) # not allowed to dilute to fewer than 10 obs/feature
set.seed(1234)
data_long <- data_long[sample(nrow(data_long),
min(nrow(data_long)/dilute, nrow(data_long)/2))] # dilute
}
x_bound <- if (is.null(x_bound)) max(abs(data_long$value))*1.1 else as.numeric(abs(x_bound))
plot1 <- ggplot(data = data_long) +
coord_flip(ylim = c(-x_bound, x_bound)) +
geom_hline(yintercept = 0) + # the y-axis beneath
# sina plot:
ggforce::geom_sina(aes(x = variable, y = value, color = stdfvalue),
method = "counts", maxwidth = 0.7, alpha = 0.7) +
# print the mean absolute value:
#geom_text(data = unique(data_long[, c("variable", "mean_value")]),
# aes(x = variable, y=-Inf, label = sprintf(label_format, mean_value)),
# size = 3, alpha = 0.7,
# hjust = -0.2,
# fontface = "bold") + # bold
# # add a "SHAP" bar notation
# annotate("text", x = -Inf, y = -Inf, vjust = -0.2, hjust = 0, size = 3,
# label = expression(group("|", bar(SHAP), "|"))) +
scale_color_gradient(low="#FFCC33", high="#6600CC",
breaks=c(0,1), labels=c(" Low","High "),
guide = guide_colorbar(barwidth = 12, barheight = 0.3)) +
theme_bw() +
theme(axis.line.y = element_blank(),
axis.ticks.y = element_blank(), # remove axis line
legend.position="bottom",
legend.title=element_text(size=10),
legend.text=element_text(size=8),
axis.title.x= element_text(size = 10)) +
# reverse the order of features, from high to low
# also relabel the feature using `label.feature`
scale_x_discrete(limits = rev(levels(data_long$variable))#,
#labels = label.feature(rev(levels(data_long$variable)))
)+
labs(y = "SHAP value (impact on model output)", x = "", color = "Feature value ")
return(plot1)
}
I'm looking to add some annotations (ideally a text and an arrow) to a faceted ggplot outside the plot area.
What's that, you say? Hasn't someone asked something similar here, here and here? Well yes. But none of them were trying to do this below an x-axis with a log scale.
With the exception of this amazing answer by #Z.Lin — but that involved a specific package and I'm looking for a more generic solution.
At first glance this would appear to be a very niche question, but for those of you familiar with forest plots this may tweak some interest.
Firstly, some context... I'm interested in presenting the results of a coxph model using a forest plot in a publication. My goal here is to take the results of a model (literally a standalone coxph object) and use it to produce output that is customisable (gotta match the style guide) and helps translate the findings for an audience that might not be au fait with the technical details of hazard ratios. Hence the annotations and directional arrows.
Before you start dropping links to r packages/functions that could help do this... here are those that I've tried so far:
ggforestplot — this package produces lovely customisable forest plots (if you are using odds ratios), but it hard codes a geom_vline at zero which doesn't help for HR's
ggforest — this package is a nerd paradise of detail, but good luck a) editing the variable names and b) trying to theme it (I mentioned earlier that I'm working with a coxph object, what I didn't mention was that the varnames are ugly — they need to be changed for a punter to understand what we're trying to communicate)
finalfit offers a great workflow and its hr_plot kicks out some informative output, but it doesn't play nice if you've already got a coxph object and you just want to plot it
So... backstory out of the way. I've created my own framework for a forest plot below to which I'd love to add — in the space below the x-axis labels and the x-axis title — two annotations that help interpret the result. My current code struggles with:
repeating the code under each facet (this is something I'm trying to avoid)
mirroring the annotations of either side of the geom_vline with a log scale
Any advice anyone might have would be much appreciated... I've added a reproducible example below.
## LOAD REQUIRED PACKAGES
library(tidyverse)
library(survival)
library(broom)
library(ggforce)
library(ggplot2)
## PREP DATA
model_data <- lung %>%
mutate(inst_cat = case_when(
inst %% 2 == 0 ~ 2,
TRUE ~ 1)) %>%
mutate(pat.karno_cat = case_when(
pat.karno < 75 ~ 2,
TRUE ~ 1)) %>%
mutate(ph.karno_cat = case_when(
ph.karno < 75 ~ 2,
TRUE ~ 1)) %>%
mutate(wt.loss_cat = case_when(
wt.loss > 15 ~ 2,
TRUE ~ 1)) %>%
mutate(meal.cal_cat = case_when(
meal.cal > 900 ~ 2,
TRUE ~ 1))
coxph_model <- coxph(
Surv(time, status) ~
sex +
inst_cat +
wt.loss_cat +
meal.cal_cat +
pat.karno_cat +
ph.karno_cat,
data = model_data)
## PREP DATA
plot_data <- coxph_model %>%
broom::tidy(
exponentiate = TRUE,
conf.int = TRUE,
conf.level = 0.95) %>%
mutate(stat_sig = case_when(
p.value < 0.05 ~ "p < 0.05",
TRUE ~ "N.S.")) %>%
mutate(group = case_when(
term == "sex" ~ "gender",
term == "inst_cat" ~ "site",
term == "pat.karno_cat" ~ "outcomes",
term == "ph.karno_cat" ~ "outcomes",
term == "meal.cal_cat" ~ "outcomes",
term == "wt.loss_cat" ~ "outcomes"))
## PLOT FOREST PLOT
forest_plot <- plot_data %>%
ggplot() +
aes(
x = estimate,
y = term,
colour = stat_sig) +
geom_vline(
aes(xintercept = 1),
linetype = 2
) +
geom_point(
shape = 15,
size = 4
) +
geom_linerange(
xmin = (plot_data$conf.low),
xmax = (plot_data$conf.high)
) +
scale_colour_manual(
values = c(
"N.S." = "black",
"p < 0.05" = "red")
) +
annotate(
"text",
x = 0.45,
y = -0.2,
col="red",
label = "indicates y",
) +
annotate(
"text",
x = 1.5,
y = -0.2,
col="red",
label = "indicates y",
) +
labs(
y = "",
x = "Hazard ratio") +
coord_trans(x = "log10") +
scale_x_continuous(
breaks = scales::log_breaks(n = 7),
limits = c(0.1,10)) +
ggforce::facet_col(
facets = ~group,
scales = "free_y",
space = "free"
) +
theme(
legend.position = "bottom",
legend.title = element_blank(),
strip.text = element_text(hjust = 0),
axis.title.x = element_text(margin = margin(t = 25, r = 0, b = 0, l = 0))
)
Created on 2022-05-10 by the reprex package (v2.0.1)
I think I would use annotation_custom here. This requires standard coord_cartesian with clip = 'off', but it should be easy to re-jig your x axis to use scale_x_log10
plot_data %>%
ggplot() +
aes(
x = estimate,
y = term,
colour = stat_sig) +
geom_vline(
aes(xintercept = 1),
linetype = 2
) +
geom_point(
shape = 15,
size = 4
) +
geom_linerange(
xmin = (log10(plot_data$conf.low)),
xmax = (log10(plot_data$conf.high))
) +
scale_colour_manual(
values = c(
"N.S." = "black",
"p < 0.05" = "red")
) +
annotation_custom(
grid::textGrob(
x = unit(0.4, 'npc'),
y = unit(-7.5, 'mm'),
label = "indicates yada",
gp = grid::gpar(col = 'red', vjust = 0.5, hjust = 0.5))
) +
annotation_custom(
grid::textGrob(
x = unit(0.6, 'npc'),
y = unit(-7.5, 'mm'),
label = "indicates bada",
gp = grid::gpar(col = 'blue', vjust = 0.5, hjust = 0.5))
) +
annotation_custom(
grid::linesGrob(
x = unit(c(0.49, 0.25), 'npc'),
y = unit(c(-10, -10), 'mm'),
arrow = arrow(length = unit(3, 'mm')),
gp = grid::gpar(col = 'red'))
) +
annotation_custom(
grid::linesGrob(
x = unit(c(0.51, 0.75), 'npc'),
y = unit(c(-10, -10), 'mm'),
arrow = arrow(length = unit(3, 'mm')),
gp = grid::gpar(col = 'blue'))
) +
labs(
y = "",
x = "Hazard ratio") +
scale_x_log10(
breaks = c(0.1, 0.3, 1, 3, 10),
limits = c(0.1,10)) +
ggforce::facet_col(
facets = ~group,
scales = "free_y",
space = "free"
) +
coord_cartesian(clip = 'off') +
theme(
legend.position = "bottom",
legend.title = element_blank(),
strip.text = element_text(hjust = 0),
axis.title.x = element_text(margin = margin(t = 25, r = 0, b = 0, l = 0)),
panel.spacing.y = (unit(15, 'mm'))
)
I am plotting a boxplot to represent the rainfall forecast quality in weather forecast model. The x-axis is forecast time (day) and the y-axis being the ensemble spread of the forecast results. The blue boxes are the hindcast (past 20-year re-forecast) and the red ones are the forecast data.
# library
library(ggplot2)
library(readr)
library(forcats)
model_name <- "ecmwf"
hens <- 11
fens <- 51
ys <- 1999
ye <- 2017
# Observation
clim_obs <- as.factor(rep("clim_obs",20))
pcp_obs <- c(80.9737,229.319,111.603,24.0906,53.037,165.04,28.6957,120.151,387.85,155.383,434.328,184.369,169.443,176.654,14.1557,223.796,105.595,56.6908,89.8277,74.0017)
pcp_obs <- as.vector(t(pcp_obs))
obs = data.frame(clim_obs, pcp_obs)
# create a data frame for forecast/hindcast results
lead_time <- factor(rep(seq(1,40),each=hens*(ye-ys+1)+fens),ordered = TRUE,levels = c(seq(1,40)))
Groups <- factor(rep(c("hindcast","forecast"),c(hens*(ye-ys+1),fens)), ordered = TRUE, levels = c("hindcast","forecast"))
pcp <- read_csv(paste(model_name, "_hind_fcst.csv", sep=""))
pcp <- as.vector(t(pcp))
data = data.frame(lead_time, Groups, pcp)
str(data)
# grouped boxplot
p <- ggplot() +
varwidth = FALSE) +
# geom_boxplot(data=obs, aes(x=clim_obs, y=pcp_obs), alpha = 0.7, outlier.shape = NA, varwidth = FALSE) +
geom_boxplot(data=data, aes(x=fct_relevel(lead_time), y=pcp, fill=Groups), alpha = 0.7, outlier.shape = NA, varwidth = FALSE) +
labs(x = 'Lead time (day)',
y = '15-day accumulative rainfall',
title = '(c) ECMWF') +
theme_classic() +
theme(legend.position = 'bottom', aspect.ratio = 0.35,
axis.text.x = element_text(angle = 45, vjust = 0.5, hjust=0.5)) +
scale_y_continuous(breaks=seq(0,600,50), minor_breaks = seq(0,600,by=10),limits=c(0,600)) +
scale_fill_manual(values=c("deepskyblue" , "coral1")) +
geom_vline(xintercept = seq(0.5,40.5,by=7), #linetype="dotted",
color = "gray", size=0.25) +
ggsave(paste(model_name, "_hind_fcst.pdf", sep=""))
The resultant figure is here:
There is another box in white in the end of the plot, which is the observation data for comparison. Therefore, I add
geom_boxplot(data=obs, aes(x=clim_obs, y=pcp_obs), alpha = 0.7, outlier.shape = NA, varwidth = FALSE) +
but the order of the forecast time is wrong. The revised figure shows that the x-axis is in alphabetical oder (i.e. 1, 10, 11, 12, ..., 2, 21, 22, ... clim_obs) but I hope it can be numerical order (i.e. 1, 2, 3, 4, 5, ..., clim_obs)
How can I fix the problem?
The file to generate the data is here: link
Thanks for spending your time here!
You can use scale_x_discrete to arrange the x-axis :
library(ggplot2)
ggplot() +
geom_boxplot(data=obs, aes(x=clim_obs, y=pcp_obs), alpha = 0.7, outlier.shape = NA, varwidth = FALSE) +
geom_boxplot(data=data, aes(x=lead_time, y=pcp, fill=Groups), alpha = 0.7, outlier.shape = NA, varwidth = FALSE) +
labs(x = 'Lead time (day)',
y = '15-day accumulative rainfall',
title = '(c) ECMWF') +
theme_classic() +
theme(legend.position = 'bottom', aspect.ratio = 0.35,
axis.text.x = element_text(angle = 45, vjust = 0.5, hjust=0.5)) +
scale_y_continuous(breaks=seq(0,600,50), minor_breaks = seq(0,600,by=10),limits=c(0,600)) +
scale_fill_manual(values=c("deepskyblue" , "coral1")) +
geom_vline(xintercept = seq(0.5,40.5,by=7), #linetype="dotted",
color = "gray", size=0.25) +
scale_x_discrete(limits=c(1:40, 'clim_obs'))
I have some code to make plots for all of the variables in my data (36) and export them automatically. It works great, but the set binwidth is pretty small. When I try to change the bindwith, it makes it really small or really big for my graphs depending on their y-axis scale. How can I increase it proportionally for all?
# Plot separate ggplot figures in a loop.
library(ggplot2)
# Make list of variable names to loop over.
var_list = combn(names(LessCountS)[1:37], 2, simplify=FALSE)
my_comparisons <- list( c("HC", "IN"), c("IN", "OUT"), c("HC", "OUT") )
symnum.args <- list(
cutpoints = c(0.0001, 0.001, 0.01, 0.05, 1),
symbols = c("***", "**", "*", "NS")
)
# Make plots.
plot_list = list()
for (i in 1:37) {
p = ggplot(LessCount1, aes_string(x=var_list[[i]][1], y=var_list[[i]][2])) +
geom_dotplot(aes(fill= Type),
binaxis = "y", stackratio = .5, binwidth = 90,
stackdir = "center"
) +
theme_gray ()+
labs(x="", y = "Cell Count (cells/\u03bcL)") +
ggtitle(var_list[[i]][2]) +
scale_x_discrete(labels=c("HC" = "Controls", "IN" = "Inpatients",
"OUT" = "Outpatients")) +
theme(plot.title = element_text(hjust = 0.5, vjust = 2), legend.text=element_text(size=12),
axis.text = element_text(size=14),
axis.title = element_text(size = 14)) +
scale_fill_manual(values=c("#CCCCCC", "#990066", "#3366CC")) +
stat_summary(fun.y = median, fun.ymin = median, fun.ymax = median,
geom = "crossbar", width = 0.5, size = .45) +
stat_compare_means(comparisons = my_comparisons, label.y = , label = "p.signif", size = 5, symnum.args = symnum.args) +
stat_compare_means(label.y = )
plot_list[[i]] = p
}
# Save plots to tiff. Makes a separate file for each plot.
for (i in 1:37) {
file_name = paste("LessCount1_plot_", var_list[[i]][2], ".tiff", sep="")
tiff(file_name)
print(plot_list[[i]])
dev.off()
}
Examples of the outcome if I change the bindwidth.
If I dont specify the binwidth they are the same size, but quite small. I want to increase it proportionally for all irrespective of their scale, hoping this is possible!
Thanks in advance,
S
You need to make binwidth conditional on the data rather than setting it to a fixed value. Here's an example:
library("ggplot2")
for (ii in 1:5) {
y <- names(mtcars)[ii]
p <- ggplot(mtcars, aes(x = 1, y = !!sym(y))) +
geom_dotplot(binaxis = "y", stackdir = "center",
# binwidth = 1
binwidth = diff(range(mtcars[, y]))/20)
print(p)
}
I would like to add labels to the end of lines in ggplot, avoid them overlapping, and avoid them moving around during animation.
So far I can put the labels in the right place and hold them static using geom_text, but the labels overlap, or I can prevent them overlapping using geom_text_repel but the labels do not appear where I want them to and then dance about once the plot is animated (this latter version is in the code below).
I thought a solution might involve effectively creating a static layer in ggplot (p1 below) then adding an animated layer (p2 below), but it seems not.
How do I hold some elements of a plot constant (i.e. static) in an animated ggplot? (In this case, the labels at the end of lines.)
Additionally, with geom_text the labels appear as I want them - at the end of each line, outside of the plot - but with geom_text_repel, the labels all move inside the plotting area. Why is this?
Here is some example data:
library(dplyr)
library(ggplot2)
library(gganimate)
library(ggrepel)
set.seed(99)
# data
static_data <- data.frame(
hline_label = c("fixed_label_1", "fixed_label_2", "fixed_label_3", "fixed_label_4",
"fixed_label_5", "fixed_label_6", "fixed_label_7", "fixed_label_8",
"fixed_label_9", "fixed_label_10"),
fixed_score = c(2.63, 2.45, 2.13, 2.29, 2.26, 2.34, 2.34, 2.11, 2.26, 2.37))
animated_data <- data.frame(condition = c("a", "b")) %>%
slice(rep(1:n(), each = 10)) %>%
group_by(condition) %>%
mutate(time_point = row_number()) %>%
ungroup() %>%
mutate(score = runif(20, 2, 3))
and this is the code I am using for my animated plot:
# colours for use in plot
condition_colours <- c("red", "blue")
# plot static background layer
p1 <- ggplot(static_data, aes(x = time_point)) +
scale_x_continuous(breaks = seq(0, 10, by = 2), expand = c(0, 0)) +
scale_y_continuous(breaks = seq(2, 3, by = 0.10), limits = c(2, 3), expand = c(0, 0)) +
# add horizontal line to show existing scores
geom_hline(aes(yintercept = fixed_score), alpha = 0.75) +
# add fixed labels to the end of lines (off plot)
geom_text_repel(aes(x = 11, y = fixed_score, label = hline_label),
hjust = 0, size = 4, direction = "y", box.padding = 1.0) +
coord_cartesian(clip = 'off') +
guides(col = F) +
labs(title = "[Title Here]", x = "Time", y = "Mean score") +
theme_minimal() +
theme(panel.grid.minor = element_blank(),
plot.margin = margin(5.5, 120, 5.5, 5.5))
# animated layer
p2 <- p1 +
geom_point(data = animated_data,
aes(x = time_point, y = score, colour = condition, group = condition)) +
geom_line(data = animated_data,
aes(x = time_point, y = score, colour = condition, group = condition),
show.legend = FALSE) +
scale_color_manual(values = condition_colours) +
geom_segment(data = animated_data,
aes(xend = time_point, yend = score, y = score, colour = condition),
linetype = 2) +
geom_text(data = animated_data,
aes(x = max(time_point) + 1, y = score, label = condition, colour = condition),
hjust = 0, size = 4) +
transition_reveal(time_point) +
ease_aes('linear')
# render animation
animate(p2, nframes = 50, end_pause = 5, height = 1000, width = 1250, res = 120)
Suggestions for consideration:
The specific repelling direction / amount / etc. in geom_text_repel is determined by a random seed. You can set seed to a constant value in order to get the same repelled positions in each frame of animation.
I don't think it's possible for repelled text to go beyond the plot area, even if you turn off clipping & specify some repel range outside plot limits. The whole point of that package is to keep text labels away from one another while remaining within the plot area. However, you can extend the plot area & use geom_segment instead of geom_hline to plot the horizontal lines, such that these lines stop before they reach the repelled text labels.
Since there are more geom layers using animated_data as their data source, it would be cleaner to put animated_data & associated common aesthetic mappings in the top level ggplot() call, rather than static_data.
Here's a possible implementation. Explanation in annotations:
p3 <- ggplot(animated_data,
aes(x = time_point, y = score, colour = condition, group = condition)) +
# static layers (assuming 11 is the desired ending point)
geom_segment(data = static_data,
aes(x = 0, xend = 11, y = fixed_score, yend = fixed_score),
inherit.aes = FALSE, colour = "grey25") +
geom_text_repel(data = static_data,
aes(x = 11, y = fixed_score, label = hline_label),
hjust = 0, size = 4, direction = "y", box.padding = 1.0, inherit.aes = FALSE,
seed = 123, # set a constant random seed
xlim = c(11, NA)) + # specify repel range to be from 11 onwards
# animated layers (only specify additional aesthetic mappings not mentioned above)
geom_point() +
geom_line() +
geom_segment(aes(xend = time_point, yend = score), linetype = 2) +
geom_text(aes(x = max(time_point) + 1, label = condition),
hjust = 0, size = 4) +
# static aesthetic settings (limits / expand arguments are specified in coordinates
# rather than scales, margin is no longer specified in theme since it's no longer
# necessary)
scale_x_continuous(breaks = seq(0, 10, by = 2)) +
scale_y_continuous(breaks = seq(2, 3, by = 0.10)) +
scale_color_manual(values = condition_colours) +
coord_cartesian(xlim = c(0, 13), ylim = c(2, 3), expand = FALSE) +
guides(col = F) +
labs(title = "[Title Here]", x = "Time", y = "Mean score") +
theme_minimal() +
theme(panel.grid.minor = element_blank()) +
# animation settings (unchanged)
transition_reveal(time_point) +
ease_aes('linear')
animate(p3, nframes = 50, end_pause = 5, height = 1000, width = 1250, res = 120)