132

I tried to make the title self-explanatory, but here goes - data first:

dtf <- structure(list(variable = structure(c(1L, 1L, 2L, 2L, 3L, 3L, 
4L, 4L, 5L, 5L), .Label = c("vma", "vla", "ia", "fma", "fla"), class = "factor"), 
    ustanova = structure(c(1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L, 1L, 
    2L), .Label = c("srednja škola", "fakultet"), class = "factor"), 
    `(all)` = c(42.9542857142857, 38.7803203661327, 37.8996138996139, 
    33.7672811059908, 29.591439688716, 26.1890660592255, 27.9557692307692, 
    23.9426605504587, 33.2200772200772, 26.9493087557604)), .Names = c("variable", 
"ustanova", "(all)"), row.names = c(NA, 10L), class = c("cast_df", 
"data.frame"), idvars = c("variable", "ustanova"), rdimnames = list(
    structure(list(variable = structure(c(1L, 1L, 2L, 2L, 3L, 
    3L, 4L, 4L, 5L, 5L), .Label = c("vma", "vla", "ia", "fma", 
    "fla"), class = "factor"), ustanova = structure(c(1L, 2L, 
    1L, 2L, 1L, 2L, 1L, 2L, 1L, 2L), .Label = c("srednja škola", 
    "fakultet"), class = "factor")), .Names = c("variable", "ustanova"
    ), row.names = c("vma_srednja škola", "vma_fakultet", "vla_srednja škola", 
    "vla_fakultet", "ia_srednja škola", "ia_fakultet", "fma_srednja škola", 
    "fma_fakultet", "fla_srednja škola", "fla_fakultet"), class = "data.frame"), 
    structure(list(value = structure(1L, .Label = "(all)", class = "factor")), .Names = "value", row.names = "(all)", class = "data.frame")))

And I'd like to create a dodged barplot, do the coord_flip and put some text labels inside the bars:

ggplot(bar) + geom_bar(aes(variable, `(all)`, fill = ustanova), position = "dodge") +
 geom_text(aes(variable, `(all)`, label = sprintf("%2.1f", `(all)`)), position = "dodge") +
 coord_flip()

you can see output here.

enter image description here

I reckon I'm asking for something trivial. I want the text labels to "follow" stacked bars. Labels are placed correctly on the y-axis, but how to position them correctly on x-axis?

Henrik
  • 65,555
  • 14
  • 143
  • 159
aL3xa
  • 35,415
  • 18
  • 79
  • 112
  • Yepp... I should've posted this one on ggplot2 mailing list. I don't want to cross-post, so I'm going to put on my patience hat and wait for @hadley to come up with something! =) – aL3xa May 16 '11 at 13:05
  • I was wrong. And I need to get more acquainted with the ggplot package apparently. – Joris Meys May 16 '11 at 13:30

1 Answers1

218

Is this what you want?

library(ggplot2)

ggplot(bar) + 
  geom_col(aes(variable, `(all)`, fill = ustanova), position = "dodge") +
  geom_text(aes(variable, `(all)`, label = sprintf("%2.1f", `(all)`), group = ustanova), 
            position = position_dodge(width = .9)) +
  coord_flip()

The key is to position = position_dodge(width = .9) (where .9 is the default width of the bars) instead of position = "dodge", which is just a shortcut without any parameter. Additionally you have to set the group=ustanova aesthetic in geom_text to dodge the labels by ustanova (A second option would be to make fill = ustanova a global aesthetic via ggplot(bar, aes(fill = ustanova))


In ggplot2_2.0.0 you find several examples in ?geom_text on how to position geom_text on dodged or stacked bars (the code chunk named "# Aligning labels and bars"). The Q&A What is the width argument in position_dodge? provides a more thorough description of the topic.

stefan
  • 90,330
  • 6
  • 25
  • 51
kohske
  • 65,572
  • 8
  • 165
  • 155
  • 2
    So is there no way to automate this? One must always specify the width? – vashts85 May 04 '16 at 16:58
  • 16
    This only worked for me after I added `fill = ustanova` in the `geom_text` layer which makes sense especially when you have many horizontal bars (maybe only needed for 3+ bars per x) and the number of breaks must be the same for the text as it is for the bars. – Alan Jun 28 '16 at 19:11
  • 4
    For facetted and dodged this is a useful answer: http://stackoverflow.com/a/26661791/1000343 – Tyler Rinker Feb 14 '17 at 01:08
  • important to mention that the standard dodge is .9 in `position_dodge()`. I'd generally avoid combining methods `position = "dodge"` and `position = position_dodge()` for exactly that reason that you might get unexpected results. – tjebo Feb 17 '23 at 07:53