3

I tried the following code but instead of showing the label in middle, I want to point it to leftmost (you can see my picture under). Thanks for the help!

library(tidyverse)
library(ggrepel)

mtcars %>% 
  group_by(am, cyl) %>% 
  slice(1) %>% 
  ggplot(aes(x = am, y = mpg, group = cyl, fill = cyl, label = mpg)) + 
  geom_bar(position = "dodge", stat = "identity") +
  geom_label_repel(data = mtcars %>% filter(am == 0, 
                                            cyl == 4) %>% 
                     slice(1),
                   nudge_x = 0.2,
                   nudge_y = 0.3,
                   aes(fill = NULL))

Created on 2019-01-22 by the reprex package (v0.2.1)

numerairX
  • 147
  • 5

2 Answers2

4

This solution will work generally, for any combination of labels.

First, it seems as if the am variable is a binary factor, so I code it as a factor so the x axis is a little cleaner. In your case, you are looking for position_dodge(width = 1) for your labels, which automatically lines the labels up on top of the corresponding bars. The way I thought was best for subsetting to only the one label you want us ti define a column mpg_lab which is NA for all the labels you don't want. If you change the conditions in the last mutate row you can isolate different labels, or delete that row altogether for all the labels.

df <- mtcars %>% 
  mutate(am = factor(am)) %>% 
  group_by(am, cyl) %>% 
  slice(1) %>% 
  mutate(mpg_lab = ifelse(am == 0 & cyl == 4, mpg, NA))

df %>% 
  ggplot(aes(x = am, y = mpg, group = cyl, fill = cyl)) + 
  geom_bar(position = "dodge", stat = "identity") + 
  geom_label_repel(data = df, 
             aes(label = mpg_lab, fill = NULL), position = position_dodge(width = 1), point.padding = NA, ylim = max(df$mpg_lab, na.rm = T) * 1.02)

enter image description here

A couple of optional things I added was turning off point.padding in geom_label_repel to keep the labels from randomly moving around side-to-side. I also bumped the label up so you can see the arrow by using the ylim argument in there. You can play around with those options if you want something different.

astrofunkswag
  • 2,608
  • 12
  • 25
1

On way is to use geom_text instead. You don't get the white background around the label, but you can position it exactly:

mtcars %>% 
 group_by(am, cyl) %>% 
 slice(1) %>% 
 ggplot(aes(x = am, y = mpg, group = cyl, fill = cyl, label = mpg)) + 
 geom_bar(position = "dodge", stat = "identity") +
 geom_text(data = mtcars %>% filter(am == 0,cyl == 4) %>% slice(1),
           nudge_x = -0.3,
           nudge_y = 0.7,
           aes(fill = NULL))

Edit:

As pointed out, geom_label will make the white background, which is a much nicer option :P

morgan121
  • 2,213
  • 1
  • 15
  • 33
  • 2
    You can simply use `geom_label` instead of `geom_text`. – Axeman Jan 22 '19 at 23:39
  • 1
    This also just nudges the label around until it is over the correct bar. This is a fine workaround if it's a one-off plot but wouldn't work if you applied the code to another context, say if you changed `cyl` to 8 in the label code – astrofunkswag Jan 22 '19 at 23:45
  • Hi Rab thanks for the answer! and @Axeman for the improvement. it works nice but I do want to keep the arrow as in geom_label_repel, any suggestions? much appreciated – numerairX Jan 23 '19 at 00:01