title: ggplot2 boxplot: horizontal bar at median?
body: <p>I would like to make a ggplot2 boxplot more meaningful by adding a thick bar at the median (so that if the median is equal to either of the lower or upper quartiles, it can be detected to which it is equal). I came across a recent post of Kohske:
  <a href="https://stackoverflow.com/questions/8134699/can-i-get-boxplot-notches-in-ggplot2">Can I get boxplot notches in ggplot2?</a>
  but I didn't know how to give the "crossbar" a "height". Then I tried 
  to use a rectangle but it didn't work either. Here is a minimal example:</p>
  
  <pre><code>require(ggplot2) 
  require(reshape2) 
  require(plyr) 
  set.seed(1) 
  ## parameters 
  p1 &lt;- c(5, 20, 100) 
  p2 &lt;- c("f1", "f2", "f3", "f4", "f5") 
  p3 &lt;- c("g1","g2","g3","g4","g5") 
  N &lt;- 1000 
  ## lengths 
  l1 &lt;- length(p1) 
  l2 &lt;- length(p2) 
  l3 &lt;- length(p3) 
  ## build result array containing the measurements 
  arr &lt;- array(rep(NA, l1*l2*l3*N), dim=c(l1, l2, l3, N), 
           dimnames=list( 
           p1=p1, 
           p2=p2, 
           p3=p3, 
           N=1:N)) 
  for(i in 1:l1){ 
      for(j in 1:l2){ 
          for(k in 1:l3){ 
              arr[i,j,k,] &lt;- i+j+k+runif(N, min=-4, max=4) 
          } 
      } 
  } 
  
  arr &lt;- arr + rexp(3*5*5*N) 
  ## create molten data 
  mdf &lt;- melt(arr, formula = . ~ p1 + p2 + p3 + N) # create molten data frame 
  ## confidence interval calculated by `boxplot.stats` 
  f &lt;- function(x){ 
      ans &lt;- boxplot.stats(x) 
      data.frame(x=x, y=ans$stats[3], ymin=ans$conf[1], ymax=ans$conf[2]) 
  } 
  
  ## (my poor) trial 
  ggplot(mdf, aes(x=p3, y=value)) + geom_boxplot(outlier.shape=1) + 
  stat_summary(fun.data=f, geom="rectangle", colour=NA, fill="black", 
  xmin=x-0.36, xmax=x+0.36, ymin=max(y-0.2, ymin), ymax=min(y+0.2, 
  ymax)) + facet_grid(p2 ~ p1, scales = "free_y") 
  
  
  **SOLUTION** (after the discussion with Kohske below):
  f &lt;- function(x, height){
      ans &lt;- median(x)
      data.frame(y=ans, ymin=ans-height/2, ymax=ans+height/2)
  }
  p &lt;- ggplot(mdf, aes(x=p3, y=value)) + geom_boxplot(outlier.shape=1) +
  stat_summary(fun.data=f, geom="crossbar", height=0.5, colour=NA,
           fill="black", width=0.78) +
  facet_grid(p2 ~ p1, scales = "free_y")
  pdf()
  print(p)
  dev.off()
  
  **UPDATE** Hmmm... it's not that trivial. The following example shows that the "height" of the crossbar should be adapted to the y-axis scale, otherwise it might be overseen.
  
  require(ggplot2)
  require(reshape2)
  require(plyr)
  set.seed(1)
  ## parameters
  p1 &lt;- c(5, 20, 100)
  p2 &lt;- c("f1", "f2", "f3", "f4", "f5")
  p3 &lt;- c("g1","g2","g3","g4","g5")
  N &lt;- 1000
  ## lengths
  l1 &lt;- length(p1)
  l2 &lt;- length(p2)
  l3 &lt;- length(p3)
  ## build result array containing the measurements
  arr &lt;- array(rep(NA, l1*l2*l3*N), dim=c(l1, l2, l3, N),
       dimnames=list(
       p1=p1,
       p2=p2,
       p3=p3,
       N=1:N))
  for(i in 1:l1){
      for(j in 1:l2){
          for(k in 1:l3){
              arr[i,j,k,] &lt;- i+j^4+k+runif(N, min=-4, max=4)
          }
      } 
  }
  arr &lt;- arr + rexp(3*5*5*N)
  arr[1,2,5,] &lt;- arr[1,2,5,]+30
  arr[1,5,3,] &lt;- arr[1,5,3,]+100
  
  ## create molten data
  mdf &lt;- melt(arr, formula = . ~ p1 + p2 + p3 + N) # create molten data frame
  
  f &lt;- function(x, height){
      ans &lt;- median(x)
      data.frame(y=ans, ymin=ans-height/2, ymax=ans+height/2)
  }
  
  ## plot
  p &lt;- ggplot(mdf, aes(x=p3, y=value)) + geom_boxplot(outlier.shape=1) +
  stat_summary(fun.data=f, geom="crossbar", height=0.7, colour=NA,
           fill="black", width=0.78) +
  facet_grid(p2 ~ p1, scales = "free_y")
  pdf()
  print(p)
  dev.off()
  </code></pre>