jaydoc jaydoc -4 years ago 104
R Question

Grouped bar plots with SEM errorbars using base graphics in R?

Edit: Based on comments below I decided to be more explicit. So here is an example of the data I am working on.

> example_data
A B outcome
1 2.31 1.47 Y
2 2.14 1.32 N
3 3.49 1.00 Y
4 2.12 0.62 Y
5 0.47 0.55 N
6 3.36 0.50 N
7 3.50 0.33 Y
8 1.97 0.39 Y
9 3.12 0.99 N
10 2.04 0.89 Y
11 2.78 0.36 Y
12 1.83 0.70 N
13 3.53 0.77 N
14 2.25 0.39 N
15 1.67 0.43 N
16 3.09 1.10 Y

So, I have two variables
. They are subgroups of a larger variable, so they can be represented in the same y axis. I want to group them by another variable
which has two levels.

I do the following

> dataset <- example_data
> attach(dataset)
> means1<- tapply(A,outcome,function(x) mean(x,na.rm=TRUE))
> means2<- tapply(B,outcome,function(x) mean(x,na.rm=TRUE))
> std.err1<- tapply(A,outcome,function(x)sd(x,na.rm=TRUE)/sqrt(length(na.omit(x))))
> std.err2<- tapply(B,outcome,function(x)sd(x,na.rm=TRUE)/sqrt(length(na.omit(x))))
> matrix_means<- matrix(c(means1,means2),byrow = TRUE)
> graph<- barplot(matrix_means, beside=TRUE, space=c(0.2,0,0.2,0), axes=FALSE, ann=FALSE, col=c("white","black","red","blue"),ylim=c(0,4), names=c("Outcome-N","Outcome-Y","Outcome-N","Outcome-Y"), xpd=FALSE)
> axis(2, cex.axis=1.5)

Now I also need to use the arrows function to draw the SEM bar between the mean and
of each group.

I can get the SEMs using
, but do not know how to place the arrows over each of the 4 bars.

> arrows(graph, means1, graph, means1 + std.err1, graph, means2, graph, means2 + std.err2, code=3, angle=90, length=0.1)

This does not place any arrows on the plot.

graphics and not
, how do I do this? Can someone help? All my other graphs were done using the
package in
, so I am trying to do this one too using base graphics so they will all look the same for my manuscript.


Answer Source

R.S. is correct, if you make an MCVE it would be a lot easier for us to understand your problem and see how to help. If your data set is very large and it's hard to make a sensible subset of it, the preloaded datasets can come in handy. You can get a list of what's available by running data(package="datasets").

In this case your question is pretty general and from what you write you're already more than half-way there. The graph object you've made contains the relevant x-values. With the mean and SEM values you already know where the arrows should start and how far they should extend, so all you have to do is to pass this information to the arrow function as vectors, and you're golden.

As you haven't supplied any data to work with I'll use the mtcars dataset to demonstrate.


summ.df <- data.frame(
  mean=tapply(mtcars$hp, mtcars$cyl, mean),
  sd=tapply(mtcars$hp, mtcars$cyl, sd),
  n=tapply(mtcars$hp, mtcars$cyl, length))

summ.df$sem <- summ.df$sd/sqrt(summ.df$n)

par(mar=c(2.5, 2.5, 2, 1.5))
bplt <- barplot(summ.df$mean, col="white")
arrows(bplt, summ.df$mean+summ.df$sem, 
       bplt, summ.df$mean-summ.df$sem, 
       angle=90, code=3, xpd=NA, length=0.1)

barplot with error bars (1 SE)

In reply to the update
The call to arrows is what's causing you trouble. If you read the documentation (?arrows) you'll see that the four first arguments are x and y coordinates for the start and end points of the arrows. For each arrow the start and end x values are the same (they're entirely vertical) and given by graph, while the end and start y values are given by the mean ± SEM, respectively.

As such you'll get the correct result by calling

arrows(graph, c(means1, means2) + c(std.err1, std.err2), 
       graph, c(means1, means2) - c(std.err1, std.err2), 
       code=3, angle=90, length=0.1)
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download