plot density plots with confidence intervals of 95% in R - r

I'm trying draw multiple density plots in one plot for comparison porpuses. I wanted them to have their confidence interval of 95% like in the following figure. I'm working with ggplot2 and my df is a long df of observations for a certain location that I would like to compare for different time intervals.
I've done some experimentation following this example but I don't have the coding knowledge to achieve what I want.
What i managed to do so far:
library(magrittr)
library(ggplot2)
library(dplyr)
build_object <- ggplot_build(
ggplot(data=ex_long, aes(x=val)) + geom_density())
plot_credible_interval <- function(
gg_density, # ggplot object that has geom_density
bound_left,
bound_right
) {
build_object <- ggplot_build(gg_density)
x_dens <- build_object$data[[1]]$x
y_dens <- build_object$data[[1]]$y
index_left <- min(which(x_dens >= bound_left))
index_right <- max(which(x_dens <= bound_right))
gg_density + geom_area(
data=data.frame(
x=x_dens[index_left:index_right],
y=y_dens[index_left:index_right]),
aes(x=x,y=y),
fill="grey",
alpha=0.6)
}
gg_density <- ggplot(data=ex_long, aes(x=val)) +
geom_density()
gg_density %>% plot_credible_interval(tab$q2.5[[40]], tab$q97.5[[40]])
Help would be much apreaciated.

This is obviously on a different set of data, but this is roughly that plot with data from 2 t distributions. I've included the data generation in case it is of use.
library(tidyverse)
x1 <- seq(-5, 5, by = 0.1)
t_dist1 <- data.frame(x = x1,
y = dt(x1, df = 3),
dist = "dist1")
x2 <- seq(-5, 5, by = 0.1)
t_dist2 <- data.frame(x = x2,
y = dt(x2, df = 3),
dist = "dist2")
t_data = rbind(t_dist1, t_dist2) %>%
mutate(x = case_when(
dist == "dist2" ~ x + 1,
TRUE ~ x
))
p <- ggplot(data = t_data,
aes(x = x,
y = y )) +
geom_line(aes(color = dist))
plot_data <- as.data.frame(ggplot_build(p)$data)
bottom <- data.frame(plot_data) %>%
mutate(dist = case_when(
group == 1 ~ "dist1",
group == 2 ~ "dist2"
)) %>%
group_by(dist) %>%
slice_head(n = ceiling(nrow(.) * 0.1)) %>%
ungroup()
top <- data.frame(plot_data) %>%
mutate(dist = case_when(
group == 1 ~ "dist1",
group == 2 ~ "dist2"
)) %>%
group_by(dist) %>%
slice_tail(n = ceiling(nrow(.) * 0.1)) %>%
ungroup()
segments <- t_data %>%
group_by(dist) %>%
summarise(x = mean(x),
y = max(y))
p + geom_area(data = bottom,
aes(x = x,
y = y,
fill = dist),
alpha = 0.25,
position = "identity") +
geom_area(data = top,
aes(x = x,
y = y,
fill = dist),
alpha = 0.25,
position = "identity") +
geom_segment(data = segments,
aes(x = x,
y = 0,
xend = x,
yend = y,
color = dist,
linetype = dist)) +
scale_color_manual(values = c("red", "blue")) +
scale_linetype_manual(values = c("dashed", "dashed"),
labels = NULL) +
ylab("Density") +
xlab("\U03B2 for AQIv") +
guides(color = guide_legend(title = "p.d.f \U03B2",
title.position = "right",
labels = NULL),
linetype = guide_legend(title = "Mean \U03B2",
title.position = "right",
labels = NULL,
override.aes = list(color = c("red", "blue"))),
fill = guide_legend(title = "Rej. area \U03B1 = 0.05",
title.position = "right",
labels = NULL)) +
annotate(geom = "text",
x = c(-4.75, -4),
y = 0.35,
label = c("RK", "OK")) +
theme(panel.background = element_blank(),
panel.border = element_rect(fill = NA,
color = "black"),
legend.position = c(0.2, 0.7),
legend.key = element_blank(),
legend.direction = "horizontal",
legend.text = element_blank(),
legend.title = element_text(size = 8))

Related

How I can makea plot which I have customized in R

I want to plot the following plot
The x-axis ranges from 1 to 9, and the y-axis ranges from -0.5 to +0.5. I have also specified colours within the boxes
First I created some reproducible data with Y factors and X values. You could define the correct and incorrect colors in a new column using case_when. To create bars use geom_col and scale_fill_manual to define the labels for your colors. Here is a reproducible example:
# Data
df <- data.frame(Y = rep(c(0.3, -0.1, -0.3), each = 9),
X = rep(c(1:9), n = 3))
library(dplyr)
library(ggplot2)
df %>%
mutate(color = case_when(Y == 0.3 | Y == -0.3 ~ 'orange',
TRUE ~ 'grey')) %>%
ggplot(aes(x = X, y = factor(Y), fill = color)) +
geom_col(width = 1) +
scale_fill_manual('', values = c('orange' = 'orange', 'grey' = 'grey'),
labels = c('Correct', 'Incorrect')) +
theme_classic() +
labs(y = 'Y', x = '')
Created on 2022-12-03 with reprex v2.0.2
Update
Slightly modify the data:
df <- data.frame(Y = rep(c(0.45, 0.25, 0.05, -0.05, -0.25, -0.45), each = 9),
X = rep(c(1:9), n = 6))
library(dplyr)
library(ggplot2)
df %>%
mutate(color = case_when(Y %in% c(-0.45, 0.45, -0.25, 0.25) ~ 'orange',
TRUE ~ 'grey')) %>%
ggplot(aes(x = X, y = factor(Y), fill = color)) +
geom_col(width = 1) +
scale_fill_manual('', values = c('orange' = 'orange', 'grey' = 'grey'),
labels = c('Correct', 'Incorrect')) +
theme_classic() +
labs(y = 'Y', x = '')
Created on 2022-12-03 with reprex v2.0.2
Update to axis
You can use the following code:
df <- data.frame(Y = c(0.45, 0.25, 0.05, -0.05, -0.25, -0.45),
X = rep(9, n = 6))
library(dplyr)
library(ggplot2)
df %>%
mutate(color = case_when(Y %in% c(-0.45, 0.45, -0.25, 0.25) ~ 'orange',
TRUE ~ 'grey')) %>%
ggplot(aes(x = X, y = factor(Y), fill = color)) +
geom_col(width = 1) +
scale_fill_manual('', values = c('orange' = 'orange', 'grey' = 'grey'),
labels = c('Correct', 'Incorrect')) +
theme_classic() +
labs(y = 'Y', x = '') +
coord_cartesian(expand = FALSE, xlim = c(1, NA)) +
scale_x_continuous(breaks = seq(1, 9, by = 1))
Created on 2022-12-03 with reprex v2.0.2

add pvalue bars to facet plot with "fill" sub-group

I'm looking for a solution since too much time without finding it, so it's time to ask for some help...
I would like to add pValue to boxplots organized with facet_wrap (ggplot2). Similar to what you obtain with the script I add to this post (the first part of the script is the exemple of what I want and it's working well for 1 plot, the second part is related to facet and doesn't work).
I would like to add pvalue between all "dose" values of "OJ", same for "VC", but also between, for exemple "dose"=1 of OJ and VC (as in the plot). It's working well for 1 plot, but not in facet_wrap. The error message is:
Error: Assigned data value must be compatible with existing data.
x Existing data has 6 rows.
x Assigned data has 60 rows.
ℹ Only vectors of size 1 are recycled.
Thanks for your help (if only...)
The script:
################# DATAFRAME
data("ToothGrowth")
df <- ToothGrowth
vec <- c("A","B")
df$dose <- as.character(df$dose)
df$facet <- rep(sample(vec, 2),replace=T, nrow(df)/2)
view(df)
################### STAT
df_pval <- df %>%
rstatix::group_by(dose) %>%
rstatix::wilcox_test(len ~ supp) %>%
rstatix::add_xy_position()
df_pval2 <- df %>%
rstatix::group_by(supp) %>%
rstatix::wilcox_test(len ~ dose) %>%
rstatix::add_xy_position(x = "supp", dodge = 0.8)
################### PLOT
plotx <- ggplot(df, aes(x = supp, y = len)) +
geom_boxplot(aes(fill = dose)) +
stat_pvalue_manual(df_pval,
label = "{p}",
color = "dose",
fontface = "bold",
step.group.by = "dose",
step.increase = 0.1,
tip.length = 0,
bracket.colour = "black",
show.legend = FALSE) +
stat_pvalue_manual(df_pval2,
label = "{p}",
color = "black",
fontface = "bold",
step.group.by = "supp",
step.increase = 0.1,
tip.length = 0,
bracket.colour = "black",
show.legend = FALSE)
plot(plotx)
################### STAT FACET
df_pval3 <- df %>%
rstatix::group_by(dose, facet) %>%
rstatix::wilcox_test(len ~ supp) %>%
rstatix::add_xy_position()
df_pval4 <- df %>%
rstatix::group_by(supp, facet) %>%
rstatix::wilcox_test(len ~ dose) %>%
rstatix::add_xy_position(x = "supp", dodge = 0.8)
print(df_pval)
print(df_pval2)
###################### PLOT FACET
ploty <- ggplot(df, aes(x = supp, y = len)) +
geom_boxplot(aes(fill = dose)) +
facet_wrap(~df[,4]) + stat_pvalue_manual(df_pval3,
label = "{p}",
color = "dose",
fontface = "bold",
step.group.by = "dose",
step.increase = 0.1,
tip.length = 0,
bracket.colour = "black",
show.legend = FALSE) +
stat_pvalue_manual(df_pval4,
label = "{p}",
color = "black",
fontface = "bold",
step.group.by = "supp",
step.increase = 0.1,
tip.length = 0,
bracket.colour = "black",
show.legend = FALSE)
plot(ploty)

How to change the colors of the dots in the graph? ggpubr package

I am using the ggerrorplot () function of the ggpubr package to create the graph below. My question is whether there is any way to change the colors of the dots without changing the color of the point that represents the mean and standard deviation? Observe the image:
My code:
# loading packages
library(ggpubr)
# Create data frame
GROUP <- c()
TEST <- c()
VALUE <- c()
for (i in 0:100) {
gp <- c('Group1','Group2','Group1 and Group2')
ts <- c('Test1','Test2')
GROUP <- append(GROUP, sample(gp, 1))
TEST <- append(TEST, sample(ts, 1))
VALUE <- append(VALUE, sample(1:200, 1))
}
df <- data.frame(GROUP, TEST, VALUE)
# Seed
set.seed(123)
# Plot
ggerrorplot(df, x = "GROUP", y = "VALUE",
desc_stat = "mean_sd",
add = c("jitter"),
color = "TEST",
palette = "jco",
add.params = list(size = 0.2),
order = c('Group1','Group2','Group1 and Group2')
) +
labs(x = '', y = 'Values\n') +
theme(legend.title = element_blank())
Can you accomplish this by simply passing in color to add.params?
# loading packages
library(ggpubr)
#> Loading required package: ggplot2
# Create data frame
GROUP <- c()
TEST <- c()
VALUE <- c()
for (i in 0:100) {
gp <- c('Group1','Group2','Group1 and Group2')
ts <- c('Test1','Test2')
GROUP <- append(GROUP, sample(gp, 1))
TEST <- append(TEST, sample(ts, 1))
VALUE <- append(VALUE, sample(1:200, 1))
}
df <- data.frame(GROUP, TEST, VALUE)
# Seed
set.seed(123)
# Plot
ggerrorplot(df, x = "GROUP", y = "VALUE",
desc_stat = "mean_sd",
add = c("jitter"),
color = "TEST",
palette = "jco",
add.params = list(size = 0.2, color = "red"),
order = c('Group1','Group2','Group1 and Group2')
) +
labs(x = '', y = 'Values\n') +
theme(legend.title = element_blank())
Created on 2021-03-10 by the reprex package (v0.3.0)
Another potential workaround - replicate the plot using ggplot() and geom_linerange(), e.g.
library(ggpubr)
library(ggsci)
library(cowplot)
# Create data frame
GROUP <- c()
TEST <- c()
VALUE <- c()
for (i in 0:100) {
gp <- c('Group1','Group2','Group1 and Group2')
ts <- c('Test1','Test2')
GROUP <- append(GROUP, sample(gp, 1))
TEST <- append(TEST, sample(ts, 1))
VALUE <- append(VALUE, sample(1:200, 1))
}
df <- data.frame(GROUP, TEST, VALUE)
# Seed
set.seed(123)
data_summary <- function(data, varname, groupnames){
require(plyr)
summary_func <- function(x, col){
c(mean = mean(x[[col]], na.rm=TRUE),
sd = sd(x[[col]], na.rm=TRUE))
}
data_sum<-ddply(data, groupnames, .fun=summary_func,
varname)
data_sum <- rename(data_sum, c("mean" = varname))
return(data_sum)
}
df2 <- data_summary(df, varname = "VALUE", groupnames = c("TEST", "GROUP"))
# Plot
p1 <- ggplot(df, aes(x = factor(GROUP, levels = c('Group1','Group2','Group1 and Group2')),
y = VALUE, color = TEST)) +
geom_jitter(shape = 21, fill = "black", stroke = 0,
position = position_jitterdodge(jitter.width = 0.2)) +
geom_linerange(data = df2, aes(ymin=VALUE-sd, ymax=VALUE+sd),
position=position_dodge(width = .75)) +
geom_point(data = df2, aes(y = VALUE), size = 3,
position = position_dodge(width = 0.75)) +
scale_color_jco() +
labs(x = '', y = 'Values\n') +
theme_classic(base_size = 14) +
theme(legend.title = element_blank(),
legend.position = "top")
p2 <- ggerrorplot(df, x = "GROUP", y = "VALUE",
desc_stat = "mean_sd",
add = c("jitter"),
color = "TEST",
palette = "jco",
add.params = list(size = 0.2),
order = c('Group1','Group2','Group1 and Group2')
) +
labs(x = '', y = 'Values\n') +
theme(legend.title = element_blank())
cowplot::plot_grid(p1, p2, nrow = 1, ncol = 2, labels = "AUTO")
When you plot them side-by-side you can see that they aren't exactly the same, but this might work for you nonetheless.
Edit
An advantage of this approach is that you can adjust the 'fill' scale separately if you don't want all the dots to be the same colour, but you do want them to be different to the lines, e.g.
p1 <- ggplot(df, aes(x = factor(GROUP, levels = c('Group1','Group2','Group1 and Group2')),
y = VALUE, color = TEST)) +
geom_jitter(aes(fill = TEST), shape = 21, stroke = 0,
position = position_jitterdodge(jitter.width = 0.2)) +
geom_linerange(data = df2, aes(ymin=VALUE-sd, ymax=VALUE+sd),
position=position_dodge(width = .75)) +
geom_point(data = df2, aes(y = VALUE), size = 3,
position = position_dodge(width = 0.75)) +
scale_color_jco() +
scale_fill_npg() +
labs(x = '', y = 'Values\n') +
theme_classic(base_size = 14) +
theme(legend.title = element_blank(),
legend.position = "top")
p2 <- ggerrorplot(df, x = "GROUP", y = "VALUE",
desc_stat = "mean_sd",
add = c("jitter"),
color = "TEST",
palette = "jco",
add.params = list(size = 0.2),
order = c('Group1','Group2','Group1 and Group2')
) +
labs(x = '', y = 'Values\n') +
theme(legend.title = element_blank())
cowplot::plot_grid(p1, p2, nrow = 1, ncol = 2, labels = "AUTO")

R label with commas but no decimals

My goal is to produce labels with commas, but no decimals. Let's say I have a ggplot with the following section:
geom_text(aes(y = var,
label = scales::comma(round(var))), hjust = 0, nudge_y = 300 )
This is almost what I need. It gives me the commas, but has a decimal. I have seen here (axis labels with comma but no decimals ggplot) that comma_format() could be good, but I think the label in my case needs a data argument, which comma_format() does not take. What can I do?
Update:
As an example of when this problem occurs, see the following, which uses gganimate and has a lot more going on. Code derived from Jon Spring's answer at Animated sorted bar chart with bars overtaking each other
library(gapminder)
library(gganimate)
library(tidyverse)
gap_smoother <- gapminder %>%
filter(continent == "Asia") %>%
group_by(country) %>%
complete(year = full_seq(year, 1)) %>%
mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>%
group_by(year) %>%
mutate(rank = min_rank(-gdpPercap) * 1) %>%
ungroup() %>%
group_by(country) %>%
complete(year = full_seq(year, .5)) %>%
mutate(gdpPercap = spline(x = year, y = gdpPercap, xout = year)$y) %>%
mutate(rank = approx(x = year, y = rank, xout = year)$y) %>%
ungroup() %>%
arrange(country,year)
gap_smoother2 <- gap_smoother %>% filter(year<=2007 & year>=1999)
gap_smoother3 <- gap_smoother2 %<>% filter(rank<=8)
p <- ggplot(gap_smoother3, aes(rank, group = country,
fill = as.factor(country), color = as.factor(country))) +
geom_tile(aes(y = gdpPercap/2,
height = gdpPercap,
width = 0.9), alpha = 0.8, color = NA) +
geom_text(aes(y = 0, label = paste(country, " ")), vjust = 0.2, hjust = 1) +
geom_text(aes(y = gdpPercap,
label = scales::comma(round(gdpPercap))), hjust = 0, nudge_y = 300 ) +
coord_flip(clip = "off", expand = FALSE) +
scale_x_reverse() +
guides(color = FALSE, fill = FALSE) +
labs(title='{closest_state %>% as.numeric %>% floor}',
x = "", y = "GFP per capita") +
theme(plot.title = element_text(hjust = 0, size = 22),
axis.ticks.y = element_blank(), # These relate to the axes post-flip
axis.text.y = element_blank(), # These relate to the axes post-flip
plot.margin = margin(1,1,1,4, "cm")) +
transition_states(year, transition_length = 1, state_length = 0) +
enter_grow() +
exit_shrink() +
ease_aes('linear')
animate(p, fps = 2, duration = 5, width = 600, height = 500)
In addition to the solution provided by #drf, you need to add scale_y_continuous(scales::comma) to your ggplot commands. But put it before the coord_flip function.
p <- ggplot(gap_smoother3, aes(rank, group = country,
fill = as.factor(country), color = as.factor(country))) +
geom_tile(aes(y = gdpPercap/2,
height = gdpPercap,
width = 0.9), alpha = 0.8, color = NA) +
geom_text(aes(y = gdpPercap,
label = scales::comma(round(gdpPercap), accuracy=1)),
hjust = 0, nudge_y = 300 ) +
scale_y_continuous(labels = scales::comma) +
... etc.

Add vline to geom_density and shade confidence interval of mean R

After reading through different posts, I found out how to add a vline of mean to density plots as shown here.
Using the data provided in the above link:
1) How can one add 95% confidence intervals around the mean using geom_ribbon?
CIs can be computed as
#computation of the standard error of the mean
sem<-sd(x)/sqrt(length(x))
#95% confidence intervals of the mean
c(mean(x)-2*sem,mean(x)+2*sem)
2) How can one limit the vline to the region under the curve? You will see in the picture below that vline plots outside the curve.
Sample data very close to my real problem can be found at https://www.dropbox.com/s/bvvfdpgekbjyjh0/test.csv?dl=0
UPDATE
Using real data in the link above, I have tried the following using #beetroot's answer.
# Find the mean of each group
dat=me
library(dplyr)
library(plyr)
cdat <- ddply(data,.(direction,cond), summarise, rating.mean=mean(rating,na.rm=T))# summarize by season and variable
cdat
#ggplot
p=ggplot(data,aes(x = rating)) +
geom_density(aes(colour = cond),size=1.3,adjust=4)+
facet_grid(.~direction, scales="free")+
xlab(NULL) + ylab("Density")
p=p+coord_cartesian(xlim = c(0, 130))+scale_color_manual(name="",values=c("blue","#00BA38","#F8766D"))+
scale_fill_manual(values=c("blue", "#00BA38", "#F8766D"))+
theme(legend.title = element_text(colour="black", size=15, face="plain"))+
theme(legend.text = element_text(colour="black", size = 15, face = "plain"))+
theme(title = red.bold.italic.text, axis.title = red.bold.italic.text)+
theme(strip.text.x = element_text(size=20, color="black",face="plain"))+ # facet labels
ggtitle("SAMPLE A") +theme(plot.title = element_text(size = 20, face = "bold"))+
theme(axis.text = blue.bold.italic.16.text)+ theme(legend.position = "none")+
geom_vline(data=cdat, aes(xintercept=rating.mean, color=cond),linetype="dotted",size=1)
p
## implementing #beetroot's code to restrict lines under the curve and shade CIs around the mean
# I will use ddply for mean and CIs
cdat <- ddply(data,.(direction,cond), summarise, rating.mean=mean(rating,na.rm=T),
sem = sd(rating,na.rm=T)/sqrt(length(rating)),
ci.low = mean(rating,na.rm=T) - 2*sem,
ci.upp = mean(rating,na.rm=T) + 2*sem)# summarize by direction and variable
#In order to limit the lines to the outline of the curves you first need to find out which y values
#of the curves correspond to the means, e.g. by accessing the density values with ggplot_build and
#using approx:
cdat.dens <- ggplot_build(ggplot(data, aes(x=rating, colour=cond)) +
facet_grid(.~direction, scales="free")+
geom_density(aes(colour = cond),size=1.3,adjust=4))$data[[1]] %>%
mutate(cond = ifelse(group==1, "A",
ifelse(group==2, "B","C"))) %>%
left_join(cdat) %>%
select(y, x, cond, rating.mean, sem, ci.low, ci.upp) %>%
group_by(cond) %>%
mutate(dens.mean = approx(x, y, xout = rating.mean)[[2]],
dens.cilow = approx(x, y, xout = ci.low)[[2]],
dens.ciupp = approx(x, y, xout = ci.upp)[[2]]) %>%
select(-y, -x) %>%
slice(1)
cdat.dens
#---
#You can then combine everything with various geom_segments:
ggplot(data, aes(x=rating, colour=cond)) +
geom_density(data = data, aes(x = rating, colour = cond),size=1.3,adjust=4) +facet_grid(.~direction, scales="free")+
geom_segment(data = cdat.dens, aes(x = rating.mean, xend = rating.mean, y = 0, yend = dens.mean, colour = cond),
linetype = "dashed", size = 1) +
geom_segment(data = cdat.dens, aes(x = ci.low, xend = ci.low, y = 0, yend = dens.cilow, colour = cond),
linetype = "dotted", size = 1) +
geom_segment(data = cdat.dens, aes(x = ci.upp, xend = ci.upp, y = 0, yend = dens.ciupp, colour = cond),
linetype = "dotted", size = 1)
Gives this:
You will notice the mean and CIs are not aligned as in the original plot. What am I not doing right #beetroot?
Using the data from the link, you can calculate the mean, se and ci like so (I suggest using dplyr, the successor of plyr):
set.seed(1234)
dat <- data.frame(cond = factor(rep(c("A","B"), each=200)),
rating = c(rnorm(200),rnorm(200, mean=.8)))
library(ggplot2)
library(dplyr)
cdat <- dat %>%
group_by(cond) %>%
summarise(rating.mean = mean(rating),
sem = sd(rating)/sqrt(length(rating)),
ci.low = mean(rating) - 2*sem,
ci.upp = mean(rating) + 2*sem)
In order to limit the lines to the outline of the curves you first need to find out which y values of the curves correspond to the means, e.g. by accessing the density values with ggplot_build and using approx:
cdat.dens <- ggplot_build(ggplot(dat, aes(x=rating, colour=cond)) + geom_density())$data[[1]] %>%
mutate(cond = ifelse(group == 1, "A", "B")) %>%
left_join(cdat) %>%
select(y, x, cond, rating.mean, sem, ci.low, ci.upp) %>%
group_by(cond) %>%
mutate(dens.mean = approx(x, y, xout = rating.mean)[[2]],
dens.cilow = approx(x, y, xout = ci.low)[[2]],
dens.ciupp = approx(x, y, xout = ci.upp)[[2]]) %>%
select(-y, -x) %>%
slice(1)
> cdat.dens
Source: local data frame [2 x 8]
Groups: cond [2]
cond rating.mean sem ci.low ci.upp dens.mean dens.cilow dens.ciupp
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 A -0.05775928 0.07217200 -0.2021033 0.08658471 0.3865929 0.403623 0.3643583
2 B 0.87324927 0.07120697 0.7308353 1.01566320 0.3979347 0.381683 0.4096153
You can then combine everything with various geom_segments:
ggplot() +
geom_density(data = dat, aes(x = rating, colour = cond)) +
geom_segment(data = cdat.dens, aes(x = rating.mean, xend = rating.mean, y = 0, yend = dens.mean, colour = cond),
linetype = "dashed", size = 1) +
geom_segment(data = cdat.dens, aes(x = ci.low, xend = ci.low, y = 0, yend = dens.cilow, colour = cond),
linetype = "dotted", size = 1) +
geom_segment(data = cdat.dens, aes(x = ci.upp, xend = ci.upp, y = 0, yend = dens.ciupp, colour = cond),
linetype = "dotted", size = 1)
As Axeman pointed out you can create a polygon based on the ribbon area as explained in this answer.
So for your data you can subset and add the additional rows like so:
ribbon <- ggplot_build(ggplot(dat, aes(x=rating, colour=cond)) + geom_density())$data[[1]] %>%
mutate(cond = ifelse(group == 1, "A", "B")) %>%
left_join(cdat.dens) %>%
group_by(cond) %>%
filter(x >= ci.low & x <= ci.upp) %>%
select(cond, x, y)
ribbon <- rbind(data.frame(cond = c("A", "B"), x = c(-0.2021033, 0.7308353), y = c(0, 0)),
as.data.frame(ribbon),
data.frame(cond = c("A", "B"), x = c(0.08658471, 1.01566320), y = c(0, 0)))
And add geom_polygon to the plot:
ggplot() +
geom_polygon(data = ribbon, aes(x = x, y = y, fill = cond), alpha = .5) +
geom_density(data = dat, aes(x = rating, colour = cond)) +
geom_segment(data = cdat.dens, aes(x = rating.mean, xend = rating.mean, y = 0, yend = dens.mean, colour = cond),
linetype = "dashed", size = 1) +
geom_segment(data = cdat.dens, aes(x = ci.low, xend = ci.low, y = 0, yend = dens.cilow, colour = cond),
linetype = "dotted", size = 1) +
geom_segment(data = cdat.dens, aes(x = ci.upp, xend = ci.upp, y = 0, yend = dens.ciupp, colour = cond),
linetype = "dotted", size = 1)
Here's the adapted code for your real data. It's just a bit tricky to incorporate two groups instead of one:
cdat <- dat %>%
group_by(direction, cond) %>%
summarise(rating.mean = mean(rating, na.rm = TRUE),
sem = sd(rating, na.rm = TRUE)/sqrt(length(rating)),
ci.low = mean(rating, na.rm = TRUE) - 2*sem,
ci.upp = mean(rating, na.rm = TRUE) + 2*sem)
cdat.dens <- ggplot_build(ggplot(dat, aes(x=rating, colour=interaction(direction, cond))) + geom_density())$data[[1]] %>%
mutate(cond = ifelse((group == 1 | group == 2 | group == 3 | group == 4), "A",
ifelse((group == 5 | group == 6 | group == 7 | group == 8), "B", "C")),
direction = ifelse((group == 1 | group == 5 | group == 9), "EAST",
ifelse((group == 2 | group == 6 | group == 10), "NORTH",
ifelse((group == 3 | group == 7 | group == 11), "SOUTH", "WEST")))) %>%
left_join(cdat) %>%
select(y, x, cond, direction, rating.mean, sem, ci.low, ci.upp) %>%
group_by(cond, direction) %>%
mutate(dens.mean = approx(x, y, xout = rating.mean)[[2]],
dens.cilow = approx(x, y, xout = ci.low)[[2]],
dens.ciupp = approx(x, y, xout = ci.upp)[[2]]) %>%
select(-y, -x) %>%
slice(1)
ggplot() +
geom_density(data = dat, aes(x = rating, colour = cond)) +
geom_segment(data = cdat.dens, aes(x = rating.mean, xend = rating.mean, y = 0, yend = dens.mean, colour = cond),
linetype = "dashed", size = 1) +
geom_segment(data = cdat.dens, aes(x = ci.low, xend = ci.low, y = 0, yend = dens.cilow, colour = cond),
linetype = "dotted", size = 1) +
geom_segment(data = cdat.dens, aes(x = ci.upp, xend = ci.upp, y = 0, yend = dens.ciupp, colour = cond),
linetype = "dotted", size = 1) +
facet_wrap(~direction)
If you want to draw the mean line without building the plot object and without manipulating the data prior to plotting you can use stat_summary():
(
ggplot(data = dat, aes(x = rating, colour = cond))
+ geom_density()
+ stat_summary(
aes(y = rating, x = 0),
geom = 'rect',
fun.data = density_mean_line(dat$rating),
key_glyph = "vline",
size = 1
)
)
giving:
where:
density_mean_line = function(values) {
values_range = range(values, na.rm=TRUE)
function(x) {
density_data = StatDensity$compute_group(
data.frame(x=x),
scales=list(
x=scale_x_continuous(limits = values_range)
)
)
mean_x = mean(x)
data.frame(
xmin=mean_x,
xmax=mean_x,
ymin=0,
ymax=approx(density_data$x, density_data$density, xout=mean_x)$y
)
}
}
And dat is defined as in erc's answer:
set.seed(1234)
dat <- data.frame(cond = factor(rep(c("A","B"), each=200)),
rating = c(rnorm(200),rnorm(200, mean=.8)))
This technique can also be used to generate solid area (of the same colour as the density outline):
(
ggplot(data = dat, aes(x = rating, colour = cond, group = cond))
+ stat_summary(
aes(y = rating, x = 0, fill = cond),
geom = 'rect',
fun.data = density_ci(dat$rating),
size=1
)
+ stat_summary(
aes(y = rating, x = 0),
geom = 'rect',
fun.data = density_mean_line(dat$rating),
key_glyph = "vline",
size = 0.5,
color='grey20'
)
+ geom_density()
)
where:
density_ci = function(values, resolution=100) {
values_range = range(values, na.rm=TRUE)
function(x) {
density_data = StatDensity$compute_group(
data.frame(x=x),
scales=list(
x=scale_x_continuous(limits = values_range)
)
)
mean_x = mean(x)
sem = sd(x) / sqrt(length(x))
ci_lower = mean_x - 1.96 * sem
ci_upper = mean_x + 1.96 * sem
x_values = seq(ci_lower, ci_upper, length.out=resolution)
data.frame(
xmin=x_values,
xmax=x_values,
ymin=rep(0, resolution),
ymax=approx(density_data$x, density_data$density, xout=x_values)$y
)
}
}

Resources