Search code examples
rplotbar-chartsapply

R barplot + sapply shuffles bars (although it shouldn't)


I need to plot means and standard errors for 4 variables grouped by 2 (left vs. right hand). Here are the data:

left_start_mydata = read.table(text="condition  force
right_small 1.80523635404968
                               right_small  2.6420765093878
                               right_small  -0.814658993753841
                               right_small  -2.60104096307957
                               right_small  -1.98589533137477
                               right_small  3.40251831946075
                               right_small  -0.320129242153803
                               right_small  -2.98033170716285
                               right_small  1.89317065279704
                               right_small  -3.84882524848594
                               right_small  -3.98968367934259
                               right_small  1.10427581334271
                               right_large  -1.75347355221301
                               right_large  0.791286271808679
                               right_large  -2.0073148173165
                               right_large  -5.03908061365724
                               right_large  -3.21618785397385
                               right_large  3.15958835997412
                               right_large  -0.728320450803572
                               right_large  -0.754841068944837
                               right_large  1.26489177600709
                               right_large  -1.25150854925629
                               right_large  2.91927950249639
                               right_large  0.343070062995591
                               left_small   2.76611178207954
                               left_small   1.98555350876524
                               left_small   1.90443573003935
                               left_small   0.939363367617274
                               left_small   1.47248738494375
                               left_small   -1.04761679029031
                               left_small   -0.824572467883381
                               left_small   -1.54423800803017
                               left_small   1.5187848305815
                               left_small   1.0956007263072
                               left_small   3.89244539291397
                               left_small   1.72801660622873
                               left_large   0.902501901614639
                               left_large   2.89567274148723
                               left_large   -0.503732000967399
                               left_large   -2.87429518370343
                               left_large   -1.85785327815289
                               left_large   -4.73265776308004
                               left_large   -0.752958593136438
                               left_large   2.47010977406911
                               left_large   -1.19149141260447
                               left_large   -0.396960252581726
                               left_large   1.54175722591051
                               left_large   2.05533917545533
                                ",header=TRUE)

At the next step, I calculate descriptive statistics for each condition:

attach(left_start_mydata)
left_start_mean_force = tapply(force, INDEX=condition, mean)   #means
left_start_sem_force = tapply(force,INDEX=condition,sd)/ sqrt(tapply(force,condition, length) ) #stand_errors

Now I plot:

barcols = c("red","blue")

sapply(2, 
       function(x) {
         mids = barplot(matrix(left_start_mean_force,
                               nrow=2,
                               byrow=TRUE),
                              ylim=c(-2,3),
                              beside=TRUE,
                              col=barcols)

         axis(1,at=colMeans(mids),
              c("left hand","right hand"),lwd=0,lwd.tick=0)

         abline(h=0)

         arrows(mids, left_start_mean_force - left_start_sem_force, 
                mids, left_start_mean_force + left_start_sem_force, 
                 code = 3, 
                 angle = 90, 
                 length = 0.1, 
                 lwd = 2)

       }
)

And I get almost what I need (see the figure below).

enter image description here

BUT! If you look at the bars for the right hand, you can see that the red one (which should represent condition "right_large") is actually lower than the blue bar next to it ("right_small"), whereas the actual value is higher (i.e., closer to zero):

> left_start_mean_force
 left_large  left_small right_large right_small 
  1.2812381  -0.6430682  -0.5242566  -0.6063786 

It seems that somehow these two columns were shuffled. The problem only appears for mean values. Standard errors are represented correctly, i.e., "right_large" is shown on the left, and "right_small" on the right side.

What is the problem? I assume, it must be something either with the barplot function of with sapply.

P.S.: please don't suggest me ggplot and other packages, I'm sure there's a simple soluton with standard functions.


Solution

  • Your data is grouped incorrectly. You need to set byrow to FALSE in this part of the code:

         mids = barplot(matrix(left_start_mean_force,
                               nrow=2,
                               byrow=FALSE), # <<<<<< HERE
                              ylim=c(-2,3),
                              beside=TRUE,
                              col=barcols)
    

    When you pass a matrix to barplot, it groups values by column, not row.

     m = matrix(1:4, nrow=2)
     barplot(m, beside=T) 
     # m is:
     #   1  3
     #   2  4
    

    barplot_by_column

    On the other hand, what you are doing is similar to this:

     m = matrix(1:4, nrow=2, byrow=T)
     barplot(m, beside=T) 
     # m is:
     #   1  2
     #   3  4
    

    barplot_by_row