简体   繁体   中英

ggplot: colouring areas between density lines according to relative position

I have this plot

set.seed(28100)
df <- data.frame(value = sample(1:10000,1000,replace=TRUE),
                 gender = sample(c("male","female"),1000,replace=TRUE))

ggplot(df, aes(value)) + 
  geom_density() +
  geom_density(data=subset(df, gender=='male'), aes(value), colour="blue") +
  geom_density(data=subset(df, gender=='female'), aes(value), colour="red")

I wonder if it's conceivable to fill the areas between the red and blue density lines with two colours: one colour when the blue line is above the red line and a different colour when the blue line is below.

There's no easy way to color in different overlapping regions unless you explicitly calculate the regions yourself. Here's a function that can help calculate regions where densities swap places

densitysplit <- function(val, grp, N=200, x=seq(min(val), max(val), length.out=N)) {
    grp <- factor(grp)
    den <- Map(function(z) {
        dx<-density(val[grp==z])
          approxfun(dx$x, dx$y)(x)
    }, levels(grp))
    maxcat <- apply(do.call("cbind",den), 1, which.max)
    data.frame(x=x, ymin=do.call("pmin", den), ymax=do.call("pmax", den), 
        top = levels(grp)[maxcat],
        group = cumsum(c(1,diff(maxcat)!=0))
    )
}

For your data, you would do something like this

head(densitysplit(df$value, df$gender))
#           x         ymin         ymax  top group
# 1   8.00000 4.214081e-05 5.198326e-05 male     1
# 2  58.17085 4.485596e-05 5.433638e-05 male     1
# 3 108.34171 4.760983e-05 5.665547e-05 male     1
# 4 158.51256 5.039037e-05 5.893143e-05 male     1
# 5 208.68342 5.318724e-05 6.115595e-05 male     1
# 6 258.85427 5.598707e-05 6.332672e-05 male     1

This gives you the data you need to use geom_ribbon to plot the data. You can do

ggplot(df, aes(value)) + 
  geom_ribbon(data=densitysplit(df$value, df$gender), aes(x, ymin=ymin, ymax=ymax, fill=top, group=group)) + 
  geom_density() +
  geom_density(data=subset(df, gender=='male'), aes(value), colour="blue") +
  geom_density(data=subset(df, gender=='female'), aes(value), colour="red")

在此输入图像描述

You can use fill and alpha to generate the (maybe) desired effect.

set.seed(28100)
df <- data.frame(value = sample(1:10000,1000,replace=TRUE),
             gender = sample(c("male","female"),1000,replace=TRUE))

ggplot(df, aes(value, colour=gender, fill=gender, alpha=0.5)) + 
  geom_density() +theme(legend.position="none")

I hope this helps. Cheers

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM