This tutorial is partly inspired by work from Allen et al. (2019) and was initially created to contribute to a GitHub repository called 'open-visualizations'. The idea behind the 'open-visualizations' repository stems from the fact that (open) science - in general - lacks 'fully' transparent and robust visualizations, i.e., figures have always some form of 'hidden-data'. To overcome this issue, I started experimenting with some code in Python and R and after posting a Tweet, in which I stated that I was working on 'open-visualizations', Neuroscientist Micah Allen replied and advised me to check out his work on Rainclouds. After performing two of their tutorials, in R and Python respectively, I thought of a way to combine the raincloud approach with my own work performed in Python. Shortly after posting another Tweet, which included some figures that I produced in R, I received a huge amount of encouraging feedback which led me to writing this markdown document. Next to this markdown document, I have created two other tutorials in Python. Both tutorials are available in my repository open-visualizations.
If you have any questions, suggestions for improvement or identify bugs, please open an issue in the GitHub repository open-visualizations.
If you use my repository for your research, please reference it.
knitr::opts_chunk$set(fig.width=6, fig.height=5, fig.path='figs_repmes/', echo=TRUE, warning=FALSE, message=FALSE)
R.version$version.string
Make sure you have the packages that are needed for this tutorial.
plyr
before dplyr
. If you need functions from both plyr
and dplyr
, please load plyr
first, then dplyr
, otherwise error messages might occur (source: R console). Rmisc
also depends on this package.lattice
since Rmisc
dependends on this package.ggplot2
, dplyr
, and readr
.rmarkdown
to convert this .Rmd template into a variety of formats including HTML, MS Word, PDF, and Beamer (Only required if you do not work in Rstudio).Rmisc
to perform some basic statistical computations (e.g., calculate mean, median, sd, se, ci).gghalves
from their GitHub repository since it features the newest options as opposed to the version on CRAN. To install from GitHub, the devtools
package needs to be installed first. gghalves
is a ggplot2
extension for easy plotting of half-half geom combinations. Think half boxplot and half jitterplot, or half violinplot and half dotplot.plyr
- Wickham, 2019lattice
- Sarkar, 2019rmarkdown
- Allaire & Xie, 2020ggplot2
- Wickham et al., 2020dplyr
- Wickham et al., 2020readr
- Wickham et al., 2020Rmisc
- Hope, 2013devtools
- Wickham & Hester, 2020gghalves
- Tiedemann, 2020 packages <- c("plyr", "lattice", "ggplot2", "dplyr", "readr", "rmarkdown","Rmisc") if (length(setdiff(packages, rownames(installed.packages()))) > 0) { install.packages(setdiff(packages, rownames(installed.packages()))) } if (!require(devtools)) { install.packages("devtools") } devtools::install_github('erocoar/gghalves')
library("plyr") library("lattice") library("ggplot2") library("dplyr") library("readr") library("rmarkdown") library("Rmisc") library("devtools") library("gghalves") # width and height variables for saved plots w = 6 h = 4 # Define limits of y-axis y_lim_min = 4 y_lim_max = 7.5 # Make the figure folder if it doesn't exist yet dir.create('../tutorial_R/figs_repmes/', showWarnings = FALSE)
gghalves
.The main idea behind gghalves
is that standard geom's
aggregate data e.g.,geom_boxplot
, geom_violin
and geom_dotplot
who all tend to be an approximation of symmetry. Given that the space to display information is limited, we can make better use of it by cutting the geom
s in half and displaying additional geom
s that e.g. give information about the sample size Tiedemann, 2020.
For this example, we make use of the iris dataset, which is freely available in R.
We manipulate the dataset by creating two variables (1) before
and (2) after
.
Specifiy the variable n
and create a dataframe d
which includes the variables y
, x
and id
.
before = iris$Sepal.Length[1:50] after = iris$Sepal.Length[51:100] n <- length(before) d <- data.frame(y = c(before, after), x = rep(c(1,2), each=n), id = factor(rep(1:n,2)))
Let's create a first very basic figure only showing the individual datapoints.
f1 <- ggplot(data=d, aes(y=y)) + #Add geom_() objects geom_point(aes(x=x), color = "magenta", size = 1.5) + #Define additional settings scale_x_continuous(breaks=c(1,2), labels=c("Before", "After"), limits=c(0, 3)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 1: Repeated measures individual datapoints') + theme_classic() + coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f1 ggsave('../tutorial_R/figs_repmes/figure1.png', width = w, height = h)
Let's again create a simple figure, but now with the datapoints connected (i.e., intra-individual trends).
f2 <- ggplot(data=d, aes(y=y)) + #Add geom_() objects geom_point(aes(x=x), color = "magenta", size = 1.5) + geom_line(aes(x=x, group=id), color = 'lightgray') + #Define additional settings scale_x_continuous(breaks=c(1,2), labels=c("Before", "After"), limits=c(0, 3)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 2: Repeated measures with connecting lines') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f2 ggsave('../tutorial_R/figs_repmes/figure2.png', width = w, height = h)
Let's add some jitter to avoid that datapoints overlap.
set.seed(321) d$xj <- jitter(d$x, amount=.09)
Now we create the the figure again, including jitter.
f3 <- ggplot(data=d, aes(y=y)) + #Add geom_() objects geom_point(aes(x=xj), color = "magenta", size = 1.5) + geom_line(aes(x=xj, group=id), color = 'lightgray') + #Define additional settings scale_x_continuous(breaks=c(1,2), labels=c("Before", "After"), limits=c(0, 3)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 3: Repeated measures with jitter and connections') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f3 ggsave('../tutorial_R/figs_repmes/figure3.png', width = w, height = h)
Produce another figure, but now defined by different colors for Before
and After
.
Just for illustrative purposes, alpha
was used in geom_line
.
f4 <- ggplot(data=d, aes(y=y)) + #Add geom_() objects geom_point(data = d %>% filter(x=="1"), aes(x=xj), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x=="2"), aes(x=xj), color = 'darkorange', size = 1.5, alpha = .6) + geom_line(aes(x=xj, group=id), color = 'lightgray', alpha = .3) + #Define additional settings scale_x_continuous(breaks=c(1,2), labels=c("Before", "After"), limits=c(0, 3)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 4: Repeated measures with jittered datapoints and connections') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f4 ggsave('../tutorial_R/figs_repmes/figure4.png', width = w, height = h)
Let's add box- and violinplots to create raincloud
like plots.
f5 <- ggplot(data = d, aes(y = y)) + #Add geom_() objects geom_point(data = d %>% filter(x =="1"), aes(x = xj), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x =="2"), aes(x = xj), color = 'darkorange', size = 1.5, alpha = .6) + geom_line(aes(x = xj, group = id), color = 'lightgray', alpha = .3) + geom_half_boxplot( data = d %>% filter(x=="1"), aes(x=x, y = y), position = position_nudge(x = -.25), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'dodgerblue') + geom_half_boxplot( data = d %>% filter(x=="2"), aes(x=x, y = y), position = position_nudge(x = .15), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'darkorange') + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = -.3), side = "l", fill = 'dodgerblue') + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = .3), side = "r", fill = "darkorange") + #Define additional settings scale_x_continuous(breaks=c(1,2), labels=c("Before", "After"), limits=c(0, 3)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 5: Repeated measures with box- and violin plots') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f5 ggsave('../tutorial_R/figs_repmes/figure5.png', width = w, height = h)
f5b <- ggplot(data = d, aes(y = y)) + #Add geom_() objects geom_point(data = d %>% filter(x =="1"), aes(x = xj), color = 'dodgerblue', size = 1.5, alpha = .5) + geom_point(data = d %>% filter(x =="2"), aes(x = xj), color = 'darkorange', size = 1.5, alpha = .5) + geom_line(aes(x = xj, group = id), color = 'lightgray', alpha = .5) + geom_half_boxplot( data = d %>% filter(x=="1"), aes(x=x, y = y), position = position_nudge(x = -.25), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = TRUE, width = .1, fill = 'dodgerblue', alpha = .5) + geom_half_boxplot( data = d %>% filter(x=="2"), aes(x=x, y = y), position = position_nudge(x = .15), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = TRUE, width = .1, fill = 'darkorange', alpha = .5) + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = 1.3), side = "r", fill = 'dodgerblue', alpha = .5, trim = FALSE) + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = .3), side = "r", fill = "darkorange", alpha = .5, trim = FALSE) + #Define additional settings scale_x_continuous(breaks=c(1,2), labels=c("Before", "After"), limits=c(0, 3)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 5b: Repeated measures with box- and violin plots') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f5b ggsave('../tutorial_R/figs_repmes/figure5b.png', width = w, height = h)
f5c <- ggplot(data = d, aes(y = y)) + #Add geom_() objects geom_point(data = d %>% filter(x =="1"), aes(x = xj), color = 'dodgerblue', size = 1.5, alpha = .5) + geom_point(data = d %>% filter(x =="2"), aes(x = xj), color = 'darkorange', size = 1.5, alpha = .5) + geom_line(aes(x = xj, group = id), color = 'lightgray', alpha = .5) + geom_half_boxplot( data = d %>% filter(x=="1"), aes(x=x, y = y), position = position_nudge(x = 1.21), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = TRUE, width = .1, fill = 'dodgerblue', alpha = .5) + geom_half_boxplot( data = d %>% filter(x=="2"), aes(x=x, y = y), position = position_nudge(x = .15), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = TRUE, width = .1, fill = 'darkorange', alpha = .5) + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = 1.3), side = "r", fill = 'dodgerblue', alpha = .5, trim = FALSE) + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = .3), side = "r", fill = "darkorange", alpha = .5, trim = FALSE) + #Define additional settings scale_x_continuous(breaks=c(1,2), labels=c("Before", "After"), limits=c(0, 3)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 5c: Repeated measures with box- and violin plots') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f5c ggsave('../tutorial_R/figs_repmes/figure5c.png', width = w, height = h)
A potential downside of the current approach is that it uses a lot of filtering and a lot of geom
s.
score_mean_1 <- mean(d$y[1:50]) score_mean_2 <- mean(d$y[51:100]) score_median1 <- median(d$y[1:50]) score_median2 <- median(d$y[51:100]) score_sd_1 <- sd(d$y[1:50]) score_sd_2 <- sd(d$y[51:100]) score_se_1 <- score_sd_1/sqrt(n) #-> adjust your n score_se_2 <- score_sd_2/sqrt(n) #-> adjust your n score_ci_1 <- CI(d$y[1:50], ci = 0.95) score_ci_2 <- CI(d$y[51:100], ci = 0.95) #Create data frame with 2 rows and 7 columns containing the descriptives group <- c("x", "z") N <- c(50, 50) score_mean <- c(score_mean_1, score_mean_2) score_median <- c(score_median1, score_median2) sd <- c(score_sd_1, score_sd_2) se <- c(score_se_1, score_se_2) ci <- c((score_ci_1[1] - score_ci_1[3]), (score_ci_2[1] - score_ci_2[3])) #Create the dataframe summary_df <- data.frame(group, N, score_mean, score_median, sd, se, ci)
Let's add the items calculated in summary_df
to the figure
f6 <- ggplot(data = d, aes(y = y)) + #Add geom_() objects geom_point(data = d %>% filter(x =="1"), aes(x = xj), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x =="2"), aes(x = xj), color = 'darkorange', size = 1.5, alpha = .6) + geom_line(aes(x = xj, group = id), color = 'lightgray', alpha = .3) + geom_half_boxplot( data = d %>% filter(x=="1"), aes(x=x, y = y), position = position_nudge(x = -.28), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'dodgerblue') + geom_half_boxplot( data = d %>% filter(x=="2"), aes(x=x, y = y), position = position_nudge(x = .18), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'darkorange') + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = -.3), side = "l", fill = 'dodgerblue') + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = .3), side = "r", fill = "darkorange") + geom_point(data = d %>% filter(x=="1"), aes(x = x, y = score_mean[1]), position = position_nudge(x = -.13), color = "dodgerblue", alpha = .6, size = 1.5) + geom_errorbar(data = d %>% filter(x=="1"), aes(x = x, y = score_mean[1], ymin = score_mean[1]-ci[1], ymax = score_mean[1]+ci[1]), position = position_nudge(-.13), color = "dodgerblue", width = 0.05, size = 0.4, alpha = .5) + geom_point(data = d %>% filter(x=="2"), aes(x = x, y = score_mean[2]), position = position_nudge(x = .13), color = "darkorange", alpha = .6, size = 1.5)+ geom_errorbar(data = d %>% filter(x=="2"), aes(x = x, y = score_mean[2], ymin = score_mean[2]-ci[2], ymax = score_mean[2]+ci[2]), position = position_nudge(.13), color = "darkorange", width = 0.05, size = 0.4, alpha = .5) + #Define additional settings scale_x_continuous(breaks=c(1,2), labels=c("Before", "After"), limits=c(0, 3)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 6: Repeated measures with box- and violin plots') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f6 ggsave('../tutorial_R/figs_repmes/figure6.png', width = w, height = h)
Optionally you could add a line between the two means. Define the x-coordinates where the line needs to be drawn. Note these coordinates can be calculated as 1 - position_nudge() for the first variable and 2 + position_nudge() for the second variable, which in our case is 1 - .13 = .87 and 2 + .13 = 2.13.
x_tick_means <- c(.87, 2.13) f7 <- ggplot(data = d, aes(y = y)) + #Add geom_() objects geom_point(data = d %>% filter(x =="1"), aes(x = xj), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x =="2"), aes(x = xj), color = 'darkorange', size = 1.5, alpha = .6) + geom_line(aes(x = xj, group = id), color = 'lightgray', alpha = .3) + geom_half_boxplot( data = d %>% filter(x=="1"), aes(x=x, y = y), position = position_nudge(x = -.28), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'dodgerblue') + geom_half_boxplot( data = d %>% filter(x=="2"), aes(x=x, y = y), position = position_nudge(x = .18), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'darkorange') + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = -.3), side = "l", fill = 'dodgerblue') + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = .3), side = "r", fill = "darkorange") + geom_point(data = d %>% filter(x=="1"), aes(x = x, y = score_mean[1]), position = position_nudge(x = -.13), color = "dodgerblue", alpha = .6, size = 1.5) + geom_errorbar(data = d %>% filter(x=="1"), aes(x = x, y = score_mean[1], ymin = score_mean[1]-ci[1], ymax = score_mean[1]+ci[1]), position = position_nudge(-.13), color = "dodgerblue", width = 0.05, size = 0.4, alpha = .6) + geom_point(data = d %>% filter(x=="2"), aes(x = x, y = score_mean[2]), position = position_nudge(x = .13), color = "darkorange", alpha = .6, size = 1.5)+ geom_errorbar(data = d %>% filter(x=="2"), aes(x = x, y = score_mean[2], ymin = score_mean[2]-ci[2], ymax = score_mean[2]+ci[2]), position = position_nudge(.13), color = "darkorange", width = 0.05, size = 0.4, alpha = .6) + #Add a line connecting the two means geom_line(data = summary_df, aes(x = x_tick_means, y = score_mean), color = 'gray', size = 1) + #Define additional settings scale_x_continuous(breaks=c(1,2), labels=c("Before", "After"), limits=c(0, 3)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 7: Repeated measures with box- and violin plots and means + ci ') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f7 ggsave('../tutorial_R/figs_repmes/figure7.png', width = w, height = h)
As a last step, let's create these figures for a 2 x 2 repeated measures study.
Define an additional variable z
which has two categories 3
and 4
and create a second jittered variable.
before = iris$Sepal.Length[1:50] after = iris$Sepal.Length[51:100] n <- length(before) d <- data.frame(y = c(before, after), x = rep(c(1,2), each=n), z = rep(c(3,4), each=n), id = factor(rep(1:n,2))) set.seed(321) d$xj <- jitter(d$x, amount = .09) d$xj_2 <- jitter(d$z, amount = .09)
f8 <- ggplot(data = d, aes(y = y)) + #Add geom_() objects geom_point(data = d %>% filter(x =="1"), aes(x = xj), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x =="2"), aes(x = xj), color = 'darkorange', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(z =="3"), aes(x = xj_2), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(z =="4"), aes(x = xj_2), color = 'darkorange', size = 1.5, alpha = .6) + geom_line(aes(x = xj, group = id), color = 'lightgray', alpha = .3) + geom_line(aes(x = xj_2, group = id), color = 'lightgray', alpha = .3) + geom_half_boxplot( data = d %>% filter(x=="1"), aes(x=x, y = y), position = position_nudge(x = -.35), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'dodgerblue', alpha = .6) + geom_half_boxplot( data = d %>% filter(x=="2"), aes(x=x, y = y), position = position_nudge(x = -1.16), side = "l",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'darkorange', alpha = .6) + geom_half_boxplot( data = d %>% filter(z=="3"), aes(x=z, y = y), position = position_nudge(x = 1.3), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'dodgerblue', alpha = .6) + geom_half_boxplot( data = d %>% filter(z=="4"), aes(x=z, y = y), position = position_nudge(x = .2), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'darkorange', alpha = .6) + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = -.40), side = "l", fill = 'dodgerblue', alpha = .6) + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = -1.40), side = "l", fill = "darkorange", alpha = .6) + geom_half_violin( data = d %>% filter(z=="3"),aes(x = z, y = y), position = position_nudge(x = 1.45), side = "r", fill = 'dodgerblue', alpha = .6) + geom_half_violin( data = d %>% filter(z=="4"),aes(x = z, y = y), position = position_nudge(x = .45), side = "r", fill = "darkorange", alpha = .6) + geom_point(data = d %>% filter(x=="1"), aes(x = x, y = score_mean[1]), position = position_nudge(x = -.13), color = "dodgerblue", alpha = .6, size = 1.5) + geom_errorbar(data = d %>% filter(x=="1"), aes(x = x, y = score_mean[1], ymin = score_mean[1]-ci[1], ymax = score_mean[1]+ci[1]), position = position_nudge(-.13), color = "dodgerblue", width = 0.05, size = 0.4, alpha = .6) + geom_point(data = d %>% filter(x=="2"), aes(x = x, y = score_mean[2]), position = position_nudge(x = -1.1), color = "darkorange", alpha = .6, size = 1.5)+ geom_errorbar(data = d %>% filter(x=="2"), aes(x = x, y = score_mean[2], ymin = score_mean[2]-ci[2], ymax = score_mean[2]+ci[2]), position = position_nudge(x = -1.1), color = "darkorange", width = 0.05, size = 0.4, alpha = .6) + geom_point(data = d %>% filter(z=="3"), aes(x = z, y = score_mean[1]), position = position_nudge(x = 1.15), color = "dodgerblue", alpha = .5) + geom_errorbar(data = d %>% filter(z=="3"), aes(x = z, y = score_mean[1], ymin = score_mean[1]-ci[1], ymax = score_mean[1]+ci[1]), position = position_nudge(1.15), color = "dodgerblue", width = 0.05, size = 0.4, alpha = .5)+ geom_point(data = d %>% filter(z=="4"), aes(x = z, y = score_mean[2]), position = position_nudge(x = .15), color = "darkorange", alpha = .5)+ geom_errorbar(data = d %>% filter(z=="4"), aes(x = z, y = score_mean[2], ymin = score_mean[2]-ci[2], ymax = score_mean[2]+ci[2]), position = position_nudge(.15), color = "darkorange", width = 0.05, size = 0.4, alpha = .5)+ #Define additional settings scale_x_continuous(breaks=c(1,2,3,4), labels=c("Before", "After","Before", "After"), limits=c(0, 5))+ xlab("Condition") + ylab("Value") + ggtitle('Figure 8: 2 x 2 Repeated measures with box- and violin plots') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f8 ggsave('../tutorial_R/figs_repmes/figure8.png', width = w, height = h)
Finally, let's add lines that connect the means of each group.
#First we must again define the x-coordinates of the means. x_tick_means_x <- c(.87, 2.13) #same as above x_tick_means_z <- c(2.87, 4.13) #just add 2 for each tick f9 <- ggplot(data = d, aes(y = y)) + #Add geom_() objects geom_point(data = d %>% filter(x =="1"), aes(x = xj), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x =="2"), aes(x = xj), color = 'darkorange', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(z =="3"), aes(x = xj_2), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(z =="4"), aes(x = xj_2), color = 'darkorange', size = 1.5, alpha = .6) + geom_line(aes(x = xj, group = id), color = 'lightgray', alpha = .3) + geom_line(aes(x = xj_2, group = id), color = 'lightgray', alpha = .3) + geom_half_boxplot( data = d %>% filter(x=="1"), aes(x=x, y = y), position = position_nudge(x = -.35), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'dodgerblue', alpha = .6) + geom_half_boxplot( data = d %>% filter(x=="2"), aes(x=x, y = y), position = position_nudge(x = -1.16), side = "l",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'darkorange', alpha = .6) + geom_half_boxplot( data = d %>% filter(z=="3"), aes(x=z, y = y), position = position_nudge(x = 1.3), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'dodgerblue', alpha = .6) + geom_half_boxplot( data = d %>% filter(z=="4"), aes(x=z, y = y), position = position_nudge(x = .2), side = "r",outlier.shape = NA, center = TRUE, errorbar.draw = FALSE, width = .2, fill = 'darkorange', alpha = .6) + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = -.40), side = "l", fill = 'dodgerblue', alpha = .6) + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = -1.40), side = "l", fill = "darkorange", alpha = .6) + geom_half_violin( data = d %>% filter(z=="3"),aes(x = z, y = y), position = position_nudge(x = 1.45), side = "r", fill = 'dodgerblue', alpha = .6) + geom_half_violin( data = d %>% filter(z=="4"),aes(x = z, y = y), position = position_nudge(x = .45), side = "r", fill = "darkorange", alpha = .6) + geom_point(data = d %>% filter(x=="1"), aes(x = x, y = score_mean[1]), position = position_nudge(x = -.13), color = "dodgerblue", alpha = .6, size = 1.5) + geom_errorbar(data = d %>% filter(x=="1"), aes(x = x, y = score_mean[1], ymin = score_mean[1]-ci[1], ymax = score_mean[1]+ci[1]), position = position_nudge(-.13), color = "dodgerblue", width = 0.05, size = 0.4, alpha = .6) + geom_point(data = d %>% filter(x=="2"), aes(x = x, y = score_mean[2]), position = position_nudge(x = .13), color = "darkorange", alpha = .6, size = 1.5)+ geom_errorbar(data = d %>% filter(x=="2"), aes(x = x, y = score_mean[2], ymin = score_mean[2]-ci[2], ymax = score_mean[2]+ci[2]), position = position_nudge(x = .13), color = "darkorange", width = 0.05, size = 0.4, alpha = .6) + geom_point(data = d %>% filter(z=="3"), aes(x = z, y = score_mean[1]), position = position_nudge(x = -.13), color = "dodgerblue", alpha = .5) + geom_errorbar(data = d %>% filter(z=="3"), aes(x = z, y = score_mean[1], ymin = score_mean[1]-ci[1], ymax = score_mean[1]+ci[1]), position = position_nudge(-.13), color = "dodgerblue", width = 0.05, size = 0.4, alpha = .5)+ geom_point(data = d %>% filter(z=="4"), aes(x = z, y = score_mean[2]), position = position_nudge(x = .13), color = "darkorange", alpha = .5)+ geom_errorbar(data = d %>% filter(z=="4"), aes(x = z, y = score_mean[2], ymin = score_mean[2]-ci[2], ymax = score_mean[2]+ci[2]), position = position_nudge(.13), color = "darkorange", width = 0.05, size = 0.4, alpha = .5)+ #Add lines connecting the two means geom_line(data = summary_df, aes(x = x_tick_means_x, y = score_mean), color = 'gray', size = 1) + geom_line(data = summary_df, aes(x = x_tick_means_z, y = score_mean), color = 'gray', size = 1) + #Define additional settings scale_x_continuous(breaks=c(1,2,3,4), labels=c("Before", "After","Before", "After"), limits=c(0, 5))+ xlab("Condition") + ylab("Value") + ggtitle('Figure 9: 2 x 2 Repeated measures with box- and violin plots') + theme_classic()+ coord_cartesian(ylim=c(y_lim_min, y_lim_max)) f9 ggsave('../tutorial_R/figs_repmes/figure9.png', width = w, height = h)
before = iris$Sepal.Length[1:50] during = iris$Sepal.Length[51:100] after = iris$Sepal.Length[1:50] n <- length(before) d <- data.frame(y = c(before, during, after), x = rep(c(1,2,3), each=n), id = factor(rep(1:n,3))) set.seed(321) d$xj <- jitter(d$x, amount = .09) f10 <- ggplot(data=d, aes(y=y)) + #Add geom_() objects geom_point(data = d %>% filter(x=="1"), aes(x=xj), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x=="2"), aes(x=xj), color = 'darkgreen', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x=="3"), aes(x=xj), color = 'darkorange', size = 1.5, alpha = .6) + geom_line(aes(x=xj, group=id), color = 'lightgray', alpha = .3) + #Define additional settings scale_x_continuous(breaks=c(1,2,3), labels=c("Before", "During", "After"), limits=c(0.5, 3.5)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 10: Repeated measures with jittered datapoints and connections') + theme_classic()+ coord_cartesian(ylim=c(3.5, 7.5)) f10 ggsave('../tutorial_R/figs_repmes/figure10.png', width = w, height = h)
before = iris$Sepal.Width[1:50] during = iris$Sepal.Length[51:100] after = iris$Sepal.Length[1:50] n <- length(before) d <- data.frame(y = c(before, during, after), x = rep(c(1,2,3), each=n), id = factor(rep(1:n,3))) set.seed(321) d$xj <- jitter(d$x, amount = .09) f11 <- ggplot(data=d, aes(y=y)) + #Add geom_() objects geom_point(data = d %>% filter(x=="1"), aes(x=xj), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x=="2"), aes(x=xj), color = 'darkgreen', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x=="3"), aes(x=xj), color = 'darkorange', size = 1.5, alpha = .6) + geom_line(aes(x=xj, group=id), color = 'lightgray', alpha = .3) + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = 2.2), side = "r", fill = 'dodgerblue', alpha = .5, color = "dodgerblue", trim = TRUE) + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = 1.2), side = "r", fill = "darkgreen", alpha = .5, color = "darkgreen", trim = TRUE) + geom_half_violin( data = d %>% filter(x=="3"),aes(x = x, y = y), position = position_nudge(x = 0.2), side = "r", fill = "darkorange", alpha = .5, color = "darkorange", trim = TRUE) + #Define additional settings scale_x_continuous(breaks=c(1,2,3), labels=c("Before", "During", "After"), limits=c(0.5, 4)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 11: Repeated measures with jittered datapoints and connections') + theme_classic()+ coord_cartesian(ylim=c(2.3, 7.2)) f11 ggsave('../tutorial_R/figs_repmes/figure11.png', width = w, height = h)
f12 <- ggplot(data=d, aes(y=y)) + #Add geom_() objects geom_point(data = d %>% filter(x=="1"), aes(x=xj), color = 'dodgerblue', size = 1.5, alpha = .8) + geom_point(data = d %>% filter(x=="2"), aes(x=xj), color = 'darkgreen', size = 1.5, alpha = .8) + geom_point(data = d %>% filter(x=="3"), aes(x=xj), color = 'darkorange', size = 1.5, alpha = .8) + geom_line(aes(x=xj, group=id), color = 'lightgray', alpha = .3) + geom_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = 0), fill = 'dodgerblue', alpha = .1, color = "white", trim = TRUE) + geom_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = 0), fill = "darkgreen", alpha = .1, color = "white", trim = FALSE) + geom_violin( data = d %>% filter(x=="3"),aes(x = x, y = y), position = position_nudge(x = 0), fill = "darkorange", alpha = .1, color = "white", trim = TRUE) + #Define additional settings scale_x_continuous(breaks=c(1,2,3), labels=c("Before", "During", "After"), limits=c(0.5, 4)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 12: Repeated measures with jittered datapoints and connections') + theme_classic()+ coord_cartesian(ylim=c(2.3, 7.2)) f12 ggsave('../tutorial_R/figs_repmes/figure12.png', width = w, height = h)
before = iris$Sepal.Width[1:50] during = iris$Sepal.Length[51:100] after = iris$Sepal.Length[1:50] n <- length(before) d <- data.frame(y = c(before, during, after), x = rep(c(1,2,3), each=n), z = rep(c(4,5,6), each=n), id = factor(rep(1:n,3))) set.seed(321) d$xj <- jitter(d$x, amount = .09) d$xj_2 <- jitter(d$z, amount = .09) #d$xj_3 <- jitter(d$a, amount = .09) f13 <- ggplot(data=d, aes(y=y)) + #Add geom_() objects geom_point(data = d %>% filter(x=="1"), aes(x=xj), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x=="2"), aes(x=xj), color = 'darkgreen', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(x=="3"), aes(x=xj), color = 'darkorange', size = 1.5, alpha = .6) + #Add geom_() objects geom_point(data = d %>% filter(z=="4"), aes(x=xj_2), color = 'dodgerblue', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(z=="5"), aes(x=xj_2), color = 'darkgreen', size = 1.5, alpha = .6) + geom_point(data = d %>% filter(z=="6"), aes(x=xj_2), color = 'darkorange', size = 1.5, alpha = .6) + geom_line(aes(x=xj, group=id), color = 'lightgray', alpha = .3) + geom_line(aes(x=xj_2, group=id), color = 'lightgray', alpha = .3) + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = -0.2), side = "l", fill = 'dodgerblue', alpha = .5, color = "dodgerblue", trim = TRUE) + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = -1.2), side = "l", fill = "darkgreen", alpha = .5, color = "darkgreen", trim = TRUE) + geom_half_violin( data = d %>% filter(x=="3"),aes(x = x, y = y), position = position_nudge(x = -2.2), side = "l", fill = "darkorange", alpha = .5, color = "darkorange", trim = TRUE) + geom_half_violin( data = d %>% filter(z=="4"),aes(x = x, y = y), position = position_nudge(x = 5.2), side = "r", fill = 'dodgerblue', alpha = .5, color = "dodgerblue", trim = TRUE) + geom_half_violin( data = d %>% filter(z=="5"),aes(x = x, y = y), position = position_nudge(x = 4.2), side = "r", fill = "darkgreen", alpha = .5, color = "darkgreen", trim = TRUE) + geom_half_violin( data = d %>% filter(z=="6"),aes(x = x, y = y), position = position_nudge(x = 3.2), side = "r", fill = "darkorange", alpha = .5, color = "darkorange", trim = TRUE) + #Define additional settings scale_x_continuous(breaks=c(1,2,3,4,5,6), labels=c("Before", "During", "After", "Before", "During", "After"), limits=c(0, 7)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 13: Repeated measures with jittered datapoints and connections') + theme_classic()+ coord_cartesian(ylim=c(2.3, 7.2)) f13 ggsave('../tutorial_R/figs_repmes/figure13.png', width = w, height = h)
f14 <- ggplot(data=d, aes(y=y)) + #Add geom_() objects geom_point(data = d %>% filter(x=="1"), aes(x=xj), color = 'dodgerblue', size = 1.5, alpha = .8) + geom_point(data = d %>% filter(x=="2"), aes(x=xj), color = 'darkgreen', size = 1.5, alpha = .8) + geom_point(data = d %>% filter(x=="3"), aes(x=xj), color = 'darkorange', size = 1.5, alpha = .8) + #Add geom_() objects geom_point(data = d %>% filter(z=="4"), aes(x=xj_2), color = 'dodgerblue', size = 1.5, alpha = .8) + geom_point(data = d %>% filter(z=="5"), aes(x=xj_2), color = 'darkgreen', size = 1.5, alpha = .8) + geom_point(data = d %>% filter(z=="6"), aes(x=xj_2), color = 'darkorange', size = 1.5, alpha = .8) + geom_line(aes(x=xj, group=id), color = 'lightgray', alpha = .3) + geom_line(aes(x=xj_2, group=id), color = 'lightgray', alpha = .3) + geom_half_violin( data = d %>% filter(x=="1"),aes(x = x, y = y), position = position_nudge(x = 0), side = "r", fill = 'dodgerblue', alpha = .15, color = "lightgray", trim = TRUE) + geom_half_violin( data = d %>% filter(x=="2"),aes(x = x, y = y), position = position_nudge(x = 0), side = "r", fill = "darkgreen", alpha = .15, color = "lightgray", trim = TRUE) + geom_half_violin( data = d %>% filter(x=="3"),aes(x = x, y = y), position = position_nudge(x = 0), side = "r", fill = "darkorange", alpha = .15, color = "lightgray", trim = TRUE) + geom_half_violin( data = d %>% filter(z=="4"),aes(x = x, y = y), position = position_nudge(x = 3), side = "r", fill = 'dodgerblue', alpha = .15, color = "lightgray", trim = TRUE) + geom_half_violin( data = d %>% filter(z=="5"),aes(x = x, y = y), position = position_nudge(x = 3), side = "r", fill = "darkgreen", alpha = .15, color = "lightgray", trim = TRUE) + geom_half_violin( data = d %>% filter(z=="6"),aes(x = x, y = y), position = position_nudge(x = 3), side = "r", fill = "darkorange", alpha = .15, color = "lightgray", trim = TRUE) + #Define additional settings scale_x_continuous(breaks=c(1,2,3,4,5,6), labels=c("Before", "During", "After", "Before", "During", "After"), limits=c(0, 7)) + xlab("Condition") + ylab("Value") + ggtitle('Figure 14: Repeated measures with jittered datapoints and connections') + theme_classic()+ coord_cartesian(ylim=c(2.3, 7.2)) f14 ggsave('../tutorial_R/figs_repmes/figure14.png', width = w, height = h)
ggtext
package by Wilke, 2020 might be worthwile .patchwork
package by Pedersen, 2020..tiff
extension and add dpi=
as used in the following line of code:ggsave("figure.tiff", height=h, width=w, units='in', dpi=600)
if("package_name" %in% (.packages())){ detach('package:package_name', unload=TRUE)} library("package_name")
You have reached the end of this document.
I hope you'll be able to use this tutorial to create more open-visualizations
for your research!
If you use this tutorial, please cite it in your work.
open-visualizations for repeated measures in R by Jordy van Langen
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.