ggplot2 density with multiple groups and trim=TRUE: density does not go to 0?

1.3k views Asked by At

I am plotting in ggplot2 densities for multiple groups that each have a different support. For this reason, I would like the density lines to stop at the min/max of the respective groups, to convey visually the difference in supports. I can do this with ggvis, see:

enter image description here

However, I am not able to do this nicely with geom_density():

  • By default (trim=FALSE) geom_density() will extend the lines of the density at 0, so that all densities overlap at 0, makes difficult to compare support
  • Setting trim=TRUE, geom_density() does not extend the lines outside of the respective support, yet will trim the lines even over the y axis.

How can I mimic the ggvis plot with ggplot::geom_density?

Code

library(tidyverse)
library(ggvis)
#> 
#> Attaching package: 'ggvis'
#> The following object is masked from 'package:ggplot2':
#> 
#>     resolution
library(patchwork)

df <- tibble(x1=truncnorm::rtruncnorm(500, -1, 1),
           x2=truncnorm::rtruncnorm(500, -1.5, 1.5),
           x3=rnorm(500)) %>% 
  gather(variable, value, everything())


pl_gg_trim <- ggplot(df, aes(x=value, color=variable))+
  geom_density(trim=TRUE) +
  ggtitle("trim=TRUE: lines don't go to 0")
pl_gg_notrim <- ggplot(df, aes(x=value, color=variable))+
  geom_density()+
  ggtitle("trim=FALSE: hard to understand support of values")



pl_ggvis <- ggvis(df, ~value, fill = ~variable) %>%
  group_by(variable) %>%
  layer_densities()

#does not work with reprex: pl_ggvis

pl_gg_notrim/pl_gg_trim

Created on 2021-03-31 by the reprex package (v1.0.0)

2

There are 2 answers

0
user12728748 On

A workaround could be to use the base density function and plot that as geom_line:

library(tidyverse)
library(broom)
set.seed(123)
df <- tibble(x1=truncnorm::rtruncnorm(500, -1, 1),
             x2=truncnorm::rtruncnorm(500, -1.5, 1.5),
             x3=rnorm(500)) %>% 
  pivot_longer(everything(), names_to = "variable", values_to = "value")

df %>%
  nest(data=value) %>% 
  mutate(fit=map(data, ~density(.$value)), results=map(fit, tidy)) %>% 
  unnest(results) %>% 
  ggplot(aes(x, y, color=variable)) + 
  geom_line() +
  labs(x="value", y="density")

Created on 2021-03-31 by the reprex package (v1.0.0)

0
Matifou On

With ggplot 3.3.0 (see news), there is now a new outline.type argument, that controls how the strokes are plotted.

Setting outline.type = "full" will make the end points of the density points go to zero, at the cost however of having now a line on the x axis linking both points.

library(tidyverse)

df <- tibble(x1=truncnorm::rtruncnorm(500, -1, 1),
             x2=truncnorm::rtruncnorm(500, -1.5, 1.5),
             x3=rnorm(500)) |> 
  gather(variable, value, everything())


ggplot(df, aes(x=value, color=variable))+
  geom_density(trim=TRUE, outline.type = "full")

Created on 2023-10-31 with reprex v2.0.2