2

This is a basic plotting question:

I need to add labels to a clustered/dodged bar chart. I have looked at several examples using text(), but cannot seem to position the labels correctly.

teachers <- c("A", "B","C", "D", "E")
mean_pre_scores <- c(10, 11, 12, 10,9)
mean_post_scores <- c(12,15,17,13,12)
pre_post <- data.frame(mean_pre_scores, mean_post_scores)

pre_post <- as.matrix(pre_post)
barplot((t(pre_post)), beside = T, names = teachers, legend = c("pre", "post"), 
        ylim = c(0,20), args.legend = list(x="bottomright"),  axes = T, main = "Unit 1 Test",
        col=c(26,51))

I want to modify this plot so that the values will be displayed above the bars. It would also be helpful to know how to show the values inside the bars.

Tyler Rinker
  • 108,132
  • 65
  • 322
  • 519
windy
  • 147
  • 1
  • 10

4 Answers4

3

I think this is what you're after:

z <- barplot((t(pre_post)), beside = T, names = teachers, 
    legend = c("pre", "post"), ylim = c(0,20), 
    args.legend = list(x="topright"),  axes = T, 
    main = "Unit 1 Content Pre Test", col=c(26,51))

text(cex=1, x=c(z[1, ], z[2, ]), y=c(pre_post) + par("cxy")[2]/2, c(pre_post), xpd=TRUE) 

enter image description here

To move the text inside the bars simply use subtractions as in:

text(cex=1, x=c(z[1, ], z[2, ]), y=c(pre_post) - par("cxy")[2]/2, c(pre_post), xpd=TRUE) 
Tyler Rinker
  • 108,132
  • 65
  • 322
  • 519
1

When you say "above the bars", do you mean directly above them? Or on the top axis?

Here's an example with them on top.

And now it's clear you wanted the numeric values on the bars instead of the letters on the top axis. I'll leave it here anyway, and post a second result below.

> barplot((t(pre_post)), beside = T, legend = c("pre", "post"), ylim = c(0,20),
          args.legend = list(x="bottomright"),  axes = T, col=c(26,51))
> axis(3, at = c(2, 5, 8, 11, 14), labels = teachers)
> title(main = "Unit 1 Content Pre Test", line = 3)
> box()

enter image description here

Here's the correct result. I moved the legend to the top right corner where it won't be covering any bars. A bit easier to read that way.

> barplot((t(pre_post)), beside = TRUE, legend = c("pre", "post"), 
          main = "Unit 1 Content Pre Test", ylim = c(0,20), 
          args.legend = list(x="topright"), col=c(26,51))
> text(x = c(1.5,2.5,4.5,5.5,7.5,8.5,10.5,11.5,13.5,14.5), 
       y = txt.height+1, labels = as.character(txt.height))
> box()

enter image description here

Rich Scriven
  • 97,041
  • 11
  • 181
  • 245
1

A ggplot2 solution:

# get the data into the right format
pre_post <- data.frame(teachers,mean_pre_scores, mean_post_scores)
library(reshape2)
prepost <- melt(pre_post, id = "teachers")

# create the plot
ggplot(prepost, aes(x = teachers, y = value, fill = variable)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = value), vjust = -0.5, position = position_dodge(0.9)) +
  theme_bw()

the result:

enter image description here

Jaap
  • 81,064
  • 34
  • 182
  • 193
0

Adapted from this post (R graphics: Add labels to stacked bar chart)

For inside labels:

teachers <- c("A", "B","C", "D", "E")
mean_pre_scores <- c(10, 11, 12, 10,9)
mean_post_scores <- c(12,15,17,13,12)
pre_post.df <- data.frame(mean_pre_scores, mean_post_scores)

pre_post <- as.matrix(pre_post.df)
b<-barplot((t(pre_post)), beside = T, names = teachers, legend = c("pre", "post"), 
     ylim = c(0,20), args.legend = list(x="bottomright"),  axes = T, main = "Unit 1 Content Pre Test",
     col=c(26,51))


ypos.inside<-apply(pre_post, 2, function(x) x -1 )
ypos.inside <- t(ypos.inside)

text(b, ypos.inside, pre_post)

enter image description here

For outside labels:

teachers <- c("A", "B","C", "D", "E")
mean_pre_scores <- c(10, 11, 12, 10,9)
mean_post_scores <- c(12,15,17,13,12)
pre_post.df <- data.frame(mean_pre_scores, mean_post_scores)

pre_post <- as.matrix(pre_post.df)
b<-barplot((t(pre_post)), beside = T, names = teachers, legend = c("pre", "post"), 
     ylim = c(0,20), args.legend = list(x="bottomright"),  axes = T, main = "Unit 1 Content Pre Test",
     col=c(26,51))

ypos.outside<-apply(pre_post, 2, function(x) x +1 )
ypos.outside <- t(ypos.outside)
text(b, ypos.outside, pre_post)

enter image description here

Community
  • 1
  • 1
Silence Dogood
  • 3,587
  • 1
  • 13
  • 17
  • This approach is less generalizable than the one I provide in that if the scale becomes extreme (change one number to something like 100) the relative label placement changes. – Tyler Rinker Apr 25 '14 at 14:33