R - ggplot2 - Get histogram of difference between two groups

Viewed 5147

Let's say I have a histogram with two overlapping groups. Here's a possible command from ggplot2 and a pretend output graph.

ggplot2(data, aes(x=Variable1, fill=BinaryVariable)) + geom_histogram(position="identity")

Overlapping histograms

So what I have is the frequency or count of each event. What I'd like to do instead is to get the difference between the two events in each bin. Is this possible? How?

For example, if we do RED minus BLUE:

  • Value at x=2 would be ~ -10
  • Value at x=4 would be ~ 40 - 200 = -160
  • Value at x=6 would be ~ 190 - 25 = 155
  • Value at x=8 would be ~ 10

I'd prefer to do this using ggplot2, but another way would be fine. My dataframe is set up with items like this toy example (dimensions are actually 25000 rows x 30 columns) EDITED: Here is example data to work with GIST Example

ID   Variable1   BinaryVariable
1     50            T          
2     55            T
3     51            N
..    ..            ..
1000  1001          T
1001  1944          T
1002  1042          N

As you can see from my example, I'm interested in a histogram to plot Variable1 (a continuous variable) separately for each BinaryVariable (T or N). But what I really want is the difference between their frequencies.

2 Answers

Here's a solution that uses ggplot as requested. The key idea is to use ggplot_build to get the rectangles computed by stat_histogram. From that you can compute the differences in each bin and then create a new plot using geom_rect.

setup and create a mock dataset with lognormal data

library(ggplot2)
library(data.table)
theme_set(theme_bw())
n1<-500
n2<-500
k1 <- exp(rnorm(n1,8,0.7))
k2 <- exp(rnorm(n2,10,1))
df <- data.table(k=c(k1,k2),label=c(rep('k1',n1),rep('k2',n2)))

Create the first plot

p <- ggplot(df, aes(x=k,group=label,color=label)) + geom_histogram(bins=40) + scale_x_log10()

Histogram without showing differences

Get the rectangles using ggplot_build

p_data <- as.data.table(ggplot_build(p)$data[1])[,.(count,xmin,xmax,group)]
p1_data <- p_data[group==1]
p2_data <- p_data[group==2]

Join on the x-coordinates to compute the differences. Note that the y-values aren't the counts, but the y-coordinates of the first plot.

newplot_data <- merge(p1_data, p2_data, by=c('xmin','xmax'), suffixes = c('.p1','.p2'))
newplot_data <- newplot_data[,diff:=count.p1 - count.p2]
setnames(newplot_data, old=c('y.p1','y.p2'), new=c('k1','k2'))

df2 <- melt(newplot_data,id.vars =c('xmin','xmax'),measure.vars=c('k1','diff','k2'))

make the final plot

ggplot(df2, aes(xmin=xmin,xmax=xmax,ymax=value,ymin=0,group=variable,color=variable)) + geom_rect()

Final histogram with bars showing the difference between two distributions.

Of course the scales and legends still need to be fixed, but that's a different topic.

Related