ggplot2 violin plot: fill central 95% only?

后端 未结 3 1663
滥情空心
滥情空心 2020-12-19 05:57

ggplot2 can create a very attractive filled violin plot:

ggplot() + geom_violin(data=data.frame(x=1, y=rnorm(10 ^ 5)), 
    aes(x=x, y=y), fill=\'gray90\', c         


        
相关标签:
3条回答
  • 2020-12-19 06:29

    Just make a selection first. Proof of concept:

    df1 <- data.frame(x=1, y=rnorm(10 ^ 5))
    df2 <- subset(df1, y > quantile(df1$y, 0.025) & y < quantile(df1$y, 0.975))
    
    ggplot(mapping = aes(x = x, y = y)) + 
      geom_violin(data = df1, aes(fill = '100%'), color = NA) +
      geom_violin(data = df2, aes(fill = '95%'), color = 'black') +
      theme_classic() + 
      scale_fill_grey(name = 'level')
    

    0 讨论(0)
  • 2020-12-19 06:31

    @Heroka gave a great answer. Here is a more general function based on his answer that allows to fill the violin plot according to any ranges (not just quantiles).

    violincol <- function(x,from=-Inf,to=Inf,col='grey'){ d <- density(x)

    right <- data.frame(x=d$y, y=d$x) #note flip of x and y, prevents coord_flip later
    
    whichrange <- function(r,x){x <= r[2] & x > r[1]}
    ranges <- cbind(from,to)  
    
    right$col <- sapply(right$y,function(y){
        id <- apply(ranges,1,whichrange,y)
        if(all(id==FALSE)) NA else col[which(id)]
    })
    
    left <- right[nrow(right):1,]
    left$x <- 0 - left$x
    
    dat <- rbind(right,left)
    
    p <- ggplot(dat, aes(x=x,y=y)) +
        geom_polygon(data=dat,aes(fill=col),show.legend = F)+
        geom_path()+
        scale_fill_manual(values=col)
    return(p)
    }
    
    x <- rnorm(10^5)
    violincol(x=x)
    violincol(x=x,from=c(-Inf,0),to=c(0,Inf),col=c('green','red'))
    r <- seq(-5,5,0.5)
    violincol(x=x,from=r,to=r+0.5,col=rainbow(length(r)))
    
    0 讨论(0)
  • 2020-12-19 06:35

    Does this do what you want? It requires some data-processing and the drawing of two violins.

    set.seed(1)
    dat <- data.frame(x=1, y=rnorm(10 ^ 5))
    
    #calculate for each point if it's central or not
    dat_q <- quantile(dat$y, probs=c(0.025,0.975))
    dat$central <- dat$y>dat_q[1] & dat$y < dat_q[2]
    
    #plot; one'95' violin and one 'all'-violin with transparent fill.
    p1 <- ggplot(data=dat, aes(x=x,y=y)) +
      geom_violin(data=dat[dat$central,], color="transparent",fill="gray90")+
      geom_violin(color="black",fill="transparent")+
    
      theme_classic()
    

    Edit: the rounded edges bothered me, so here is a second approach. If I were doing this, I would want straight lines. So I did some playing with the density (which is what violin plots are based on)

    d_y <- density(dat$y)
    
    right_side <- data.frame(x=d_y$y, y=d_y$x) #note flip of x and y, prevents coord_flip later
    right_side$central <- right_side$y > dat_q[1]&right_side$y < dat_q[2]
    
    #add the 'left side', this entails reversing the order of the data for
    #path and polygon
    #and making x  negative
    left_side <- right_side[nrow(right_side):1,]
    left_side$x <- 0 - left_side$x
    
    density_dat <- rbind(right_side,left_side)
    
    
    p2 <- ggplot(density_dat, aes(x=x,y=y)) +
      geom_polygon(data=density_dat[density_dat$central,],fill="red")+
      geom_path()
    
    
    p2
    

    0 讨论(0)
提交回复
热议问题