Search code examples
rggplot2probability-density

How to extract the density value from ggplot in r


How can I extract the values matrix of each density plot?

For example, I am interested in that when weight = 71, what will the density of orange, apple, pears, banana be ?

Below is the minimal example:

library(ggplot2)
set.seed(1234)
df = data.frame(
  fruits = factor(rep(c("Orange", "Apple", "Pears", "Banana"), each = 200)),
  weight = round(c(rnorm(200, mean = 55, sd=5),
                 rnorm(200, mean=65, sd=5),
                 rnorm(200, mean=70, sd=5),
                 rnorm(200, mean=75, sd=5)))
)
dim(df) [1] 800   2

ggplot(df, aes(x = weight)) +
  geom_density() + 
  facet_grid(fruits ~ ., scales = "free", space = "free")

enter image description here


Solution

  • Save the plot in a variable, build the data structure with ggplot_build and split the data by panel. Then interpolate with approx to get the new values.

    g <- ggplot(df, aes(x = weight)) +
      geom_density() + 
      facet_grid(fruits ~ ., scales = "free", space = "free")
    
    p <- ggplot_build(g)
    
    # These are the columns of interest    
    p$data[[1]]$x
    p$data[[1]]$density
    p$data[[1]]$PANEL
    

    Split the list member p$data[[1]] by panel but keep only the x and density values. Then loop through the split data to interpolate by group of fruit.

    sp <- split(p$data[[1]][c("x", "density")], p$data[[1]]$PANEL)
    
    new_weight <- 71
    sapply(sp, \(DF){
      with(DF, approx(x, density, xout = new_weight))
    })
    #  1          2          3           4         
    #x 71         71         71          71        
    #y 0.04066888 0.05716947 0.001319164 0.07467761
    

    Or, without splitting the data previously, use by.

    b <- by(p$data[[1]][c("x", "density")], p$data[[1]]$PANEL, \(DF){
      with(DF, approx(x, density, xout = new_weight))
    })
    do.call(rbind, lapply(b, as.data.frame))
    #   x           y
    #1 71 0.040668880
    #2 71 0.057169474
    #3 71 0.001319164
    #4 71 0.074677607