3

I am making several instances of a tilted bar chart. As the sizes of count and the differences in percent vary, part of one of the labels (count) is pushed outside the bar in some instances. I need the labels to be entirely inside the bar in all instances. If not repositioned to fit inside the bar, I need the labels to be centered as is.

enter image description here

The code is:

library(tidyverse)
library(ggplot2)

data <- tibble(type = c('Cat', 'Dog'),
               group = c('Pets', 'Pets'),
               count = c(10000, 990000),
               percent = c(1, 99))

ggplot(data, aes(x = group, y = percent, fill = type)) +
  geom_bar(stat = 'identity', 
           position = position_stack(reverse = TRUE)) +
  coord_flip() +
  geom_text(aes(label = count),
            position = position_stack(vjust = 0.5, 
                                      reverse = TRUE))
rjen
  • 1,938
  • 1
  • 7
  • 19
  • For me, the graph is OK whichever the values... What are your versions of R and ggplot2? – Jrm_FRL Apr 29 '20 at 19:50
  • @Jrm_FRL: R version 3.6.2 and ggplot2 version 3.2.1. – rjen Apr 29 '20 at 20:09
  • You could plot counts in thousands (with a caption to make note of that) and then you'd need to plot 10 and 990 instead of 10,000 and 990,000. Regardless, with bar segments on either end that take up less than a few percent of the bar length, you're probably not going to be able to get the entire number inside the plot panel. – eipi10 Apr 29 '20 at 21:12

2 Answers2

4

Use hjust="inward":

ggplot(data, aes(x = group, y = percent, fill = type)) +
  geom_bar(stat = 'identity', position = position_stack(reverse = TRUE)) +
  coord_flip() +
  geom_text(aes(label = count), hjust = "inward", position = position_stack(vjust = 0.5, reverse = TRUE))

enter image description here

r2evans
  • 141,215
  • 6
  • 77
  • 149
  • 1
    I should note that the `990000` is slightly to the left. To fix that, we'd need to assign `hjust=` manually with some (likely simple) heuristic. – r2evans Jun 11 '20 at 19:32
  • Great! I hoped that a setting like that existed. Do you have a rough idea of what a heuristic for centering the ```990000``` could look like? – rjen Jun 11 '20 at 20:03
  • 1
    I misread the axes and intentions (common for me when `coord_flip()` is involved :-). The problem of "shift inward but only if the characters are too wide" is what is flummoxing me. Sorry, nothing off the top of my head. – r2evans Jun 11 '20 at 20:21
0

One thing key to note here is that plots in ggplot are drawn differently depending on the graphics device resolution, width, and height settings. This is why plots look a bit different depending on the computer you use to plot them. If I take your default graph and save different aspect ratios, this becomes evident:

  • width=3, height=5

enter image description here

  • width=7, height=5

enter image description here

The aspect ratio and resolution change the plot. You can also see this for yourself within R studio by just resizing the plot viewer window.

With that being said, there are some options to adjust your plot to be less likely to clip text out of bounds:

  • Rotate your text or rotate your plot back to horizontal bars. For long text labels, they are going to work out better with horizontal bars anyway.

  • geom_text_repel from the ggrepel package. Direct replacement of geom_text puts your labels in the plot area, and you can use min.segment.length= to specify the minimum line length as well as force= and direction= to play with positioning. Again, works better if you flip back your chart.

  • Use the expand= argument applied to scale_y_continuous. Try adding scale_y_continuous(expand=c(0.25,0.25)) to your plot, for example. Note that since your coordinate system is flipped, you have to specify "y" to expand "x". This expands the plot area around the geoms.

  • Change the output width= and height= and resolution when exporting your plots. As indicated above, this is the simple solution.

There are probably other suggestions, but that's mine.

chemdork123
  • 12,369
  • 2
  • 16
  • 32