How to maintain size of ggplot with long labels

I have a plot that is a simple barplot of number of each type of an event. I need the labels of the plot to be under the plot as some of the events have very long names and were squashing the plot sideways. I tried to move the labels underneath the plot but it now gets squashed upwards when there are lots of event types. Is there a way of having a static plot size (i.e. for the bar graph) so that long legends don't squash the plot?

My code:

ggplot(counts_df, aes(x = Var2, y = value, fill - Var1)+
    geom_bar(stat = "identity") +
    theme(legend.position = "bottom") +
    theme(legen.direction = "vertical") +
    theme(axis.text.x = element_text(angle = -90)

The result:

enter image description here

I think this is because the image size must be static so the plot gets sacrificed for the axis. The same thing happens when I put a legend beneath the plot.


There a several ways to avoid overplotting of labels or squeezing the plot area or to improve readability in general. Which of the proposed solutions is most suitable will depend on the lengths of the labels and the number of bars, and a number of other factors. So, you will probably have to play around.

Dummy data

Unfortunately, the OP hasn't included a reproducible example, so we we have to make up our own data:

V1 <- c("Long label", "Longer label", "An even longer label",
        "A very, very long label", "An extremely long label",
        "Long, longer, longest label of all possible labels", 
        "Another label", "Short", "Not so short label")
df <- data.frame(V1, V2 = nchar(V1))
yaxis_label <- "A rather long axis label of character counts"

"Standard" bar chart

Labels on the x-axis are printed upright, overplotting each other:

library(ggplot2)  # version 2.2.0+
p <- ggplot(df, aes(V1, V2)) + geom_col() + xlab(NULL) +
  ylab(yaxis_label) 
p

Note that the recently added geom_col() instead of geom_bar(stat="identity") is being used.

enter image description here

OP's approach: rotate labels

Labels on x-axis are rotated by 90° degrees, squeezing the plot area:

p + theme(axis.text.x = element_text(angle = 90))

enter image description here

Horizontal bar chart

All labels (including the y-axis label) are printed upright, improving readability but still squeezing the plot area (but to a lesser extent as the chart is in landscape format):

p + coord_flip()

enter image description here

Vertical bar chart with labels wrapped

Labels are printed upright, avoiding overplotting, squeezing of plot area is reduced. You may have to play around with the width parameter to stringr::str_wrap.

q <- p + aes(stringr::str_wrap(V1, 15), V2) + xlab(NULL) +
  ylab(yaxis_label)
q

enter image description here

Horizontal bar chart with labels wrapped

My favorite approach: All labels are printed upright, improving readability, squeezing of plot area are is reduced. Again, you may have to play around with the width parameter to stringr::str_wrap to control the number of lines the labels are split into.

q + coord_flip()

enter image description here

Addendum: Abbreviate labels using scale_x_discrete()

For the sake of completeness, it should be mentioned that ggplot2 is able to abbreviate labels. In this case, I find the result disappointing.

p + scale_x_discrete(labels = abbreviate)

enter image description here


To clarify, what this question appears to be asking about is how to specify the panel size in ggplot2.

I believe that the correct answer to this question is 'you just can't do that'.

As of the present time, there does not seem to be any parameter that can be set in any ggplot2 function that would achieve this. If there was one, I think it would most likely be in the form of height and width arguments to an element_rect call within a call to theme (which is how we make other changes to the panel, e.g. altering its background colour), but there's nothing resembling those in the docs for element_rect so my best guess is that specifying the panel size is impossible:

https://ggplot2.tidyverse.org/reference/element.html

The following reference is old but I can't find anything more up to date that positively confirms whether or not this is the case:

https://groups.google.com/forum/#!topic/ggplot2/nbhph_arQ7E

In that discussion, someone asks whether it is possible to specify the panel size, and Hadley says 'Not yet, but it's on my to do list'. That was nine years ago; I guess it's still on his to do list!


One more solution in addition to those above - use staggered labels. These can be used with text wrapping to get a fairly readable result:

p + scale_x_discrete(guide = ggplot2::guide_axis(n.dodge = 2), 
                     labels = function(x) stringr::str_wrap(x, width = 20))

(Using the plot p from @Uwe's answer)

enter image description here