4

I have plotted a distribution and I want to shade the area>95 percentile. However when I try to use the different techniques documented here:ggplot2 shade area under density curve by group It does not work as the length of my dataset differ.

AGG[,1]=seq(1:1000)
AGG[,2]=rnorm(1000,mean=150,sd=10)
Z<-data.frame(AGG) 
library(ggplot2)
ggplot(Z,aes(x=Z[,2]))+stat_density(geom="line",colour="lightblue",size=1.1)+xlim(0,350)+ylim(0,0.05)+geom_vline(xintercept=quantile(Z[,2],prob=0.95),colour="red")+geom_text(aes(x=quantile(Z[,2],prob=0.95)),label="VaR 95%",y=0.0225, colour="red")
#I want to add a shaded area right of the VaR in this chart
markus
  • 25,843
  • 5
  • 39
  • 58
Sabotar
  • 77
  • 1
  • 6
  • Is it necessary that you sample random numbers from the distribution using `rnorm`, or would it be sufficient to plot the empirical function with `dnorm`? – jdobres Jan 11 '18 at 18:11
  • Actually I cannot use dnorm or something similar because my distribution does not follow the usual laws and I used Monte Carlo simulation to estimate it. So let's say that AGG[,2] is any given of random numbers for which I want to plot the density and shade the area above the 95th percentile – Sabotar Jan 11 '18 at 19:39
  • Thanks to both of you for your useful answer. Too bad it is more complex to shade area on GGplot than using the usual plot function in R. – Sabotar Jan 12 '18 at 14:43
  • Does this answer your question? [Shading a kernel density plot between two points.](https://stackoverflow.com/questions/3494593/shading-a-kernel-density-plot-between-two-points) – Lala La Sep 02 '20 at 21:23

2 Answers2

9

This is a case where ggplot's helper functions and built-in summaries can end up being more troublesome than helpful. In your situation, it's probably best to calculate your summary statistics directly, and then plot those. In the example below, I use density and quantile from the base stats library to compute what will be plotted. Feeding this to ggplot directly ends up being much simpler than trying to manipulate ggplot's summary functions. This way, shading is accomplished using geom_ribbon and ggplot's intended aesthetic system; no need to go digging through the plot object.

rm(list = ls())
library(magrittr)
library(ggplot2)

y <- rnorm(1000, 150, 10)

cutoff <- quantile(y, probs = 0.95)

hist.y <- density(y, from = 100, to = 200) %$% 
  data.frame(x = x, y = y) %>% 
  mutate(area = x >= cutoff)

the.plot <- ggplot(data = hist.y, aes(x = x, ymin = 0, ymax = y, fill = area)) +
  geom_ribbon() +
  geom_line(aes(y = y)) +
  geom_vline(xintercept = cutoff, color = 'red') +
  annotate(geom = 'text', x = cutoff, y = 0.025, color = 'red', label = 'VaR 95%', hjust = -0.1)
print(the.plot)

enter image description here

jdobres
  • 11,339
  • 1
  • 17
  • 37
2

Here is a solution using the function WVPlots::ShadedDensity. I will use this function because its arguments are self-explanatory and therefore the plot can be created very easily. On the downside, the customization is a bit tricky. But once you worked your head around a ggplot object, you'll see that it is not that mysterious.

library(WVPlots)

# create the data
set.seed(1)
V1 = seq(1:1000)
V2 = rnorm(1000, mean = 150, sd = 10)
Z <- data.frame(V1, V2)

Now you can create your plot.

threshold <- quantile(Z[, 2], prob = 0.95)[[1]]
p <- WVPlots::ShadedDensity(frame = Z, 
                            xvar = "V2",
                            threshold = threshold,
                            title = "Your title",
                            tail = "right")
p

enter image description here

But since you want the colour of the line to be lightblue etc, you need to manipulate the object p. In this regard, see also this and this question.

The object p contains four layers: geom_line, geom_ribbon, geom_vline and geom_text. You'll find them here: p$layers.

Now you need to change their aesthetic mappings. For geom_line there is only one, the colour

p$layers[[1]]$aes_params
$colour
[1] "darkgray"

If you now want to change the line colour to be lightblue simply overwrite the existing colour like so

p$layers[[1]]$aes_params$colour <- "lightblue"

Once you figured how to do that for one layer, the rest is easy.

p$layers[[2]]$aes_params$fill <- "grey"     #geom_ribbon
p$layers[[3]]$aes_params$colour <- "red"    #geom_vline
p$layers[[4]]$aes_params$label <- "VaR 95%" #geom_text

p

And the plot now looks like this

enter image description here

markus
  • 25,843
  • 5
  • 39
  • 58