13

I have a ggplot2 plot as follows:

library(ggplot2)

ggplot(mtcars, aes(factor(cyl), fill=factor(cyl))) + 
    geom_bar() +
    coord_flip() +
    theme(legend.position = 'top') +
    guides(fill = guide_legend(title=NULL))

I'd like add spacing between the fill elements as follows:

enter image description here

Tyler Rinker
  • 108,132
  • 65
  • 322
  • 519
  • 2
    It seems like you should be able to set it with something like `theme(legend.text = element_text(margin = margin(r = 2, unit = 'in')))`, but that doesn't seem to do anything. A hacky (but effective) way to do it is to simply add spaces to the items: `fill=factor(paste(cyl, ' '))` – alistaire Jul 15 '16 at 18:09
  • @alistaire, that's much simpler than my hack. You should add it as an answer. – eipi10 Jul 15 '16 at 18:28
  • 1
    Possible duplicate of [Is there a way to change the spacing between legend items in ggplot2?](http://stackoverflow.com/questions/11366964/is-there-a-way-to-change-the-spacing-between-legend-items-in-ggplot2) – user20650 Jul 16 '16 at 15:01

6 Answers6

8

The issue mentioned by alistaire and Tyler Rinker was solved. Now we can adjust the margins of the element_text`.

ggplot(mtcars, aes(factor(cyl), fill = factor(cyl))) +
  geom_bar() +
  coord_flip() +
  theme(
    legend.position = 'top',
    legend.title = element_blank(),
    legend.text = element_text(margin = margin(r = 2, unit = 'cm'))
  )

enter image description here

mpalanco
  • 12,960
  • 2
  • 59
  • 67
4

It really seems something like theme(legend.text = element_text(margin = margin(r = 2, unit = 'in'))) would be the right way to accomplish the task, but that doesn't do anything at all.

Instead, (and not for the first time) I fall back on the Microsoft Word style of alignment-hacking, i.e. just add spaces:

ggplot(mtcars, aes(factor(cyl), fill=factor(paste(cyl, '                    ')))) + 
    geom_bar() +
    coord_flip() +
    theme(legend.position = 'top') +
    guides(fill = guide_legend(title=NULL))

plot with spaced legend

Because there's spaces on the 8 as well, it's a little off-center, but if you just paste them onto the previous labels you can nudge them around as you like.

Apologies for any nightmares caused to graphic designers.

alistaire
  • 42,459
  • 4
  • 77
  • 117
2

This is another hack but one that I prefer, as it adds additional white space at the end of each label according to its number of characters. Replace fill = factor(cyl) with

fill = sprintf("%-20s", factor(cyl)).

This pads all strings in the vector with white characters on the right to reach 20 characters total. This is perfect if you have text labels of different lengths. You can change 20 to whatever number you want, or remove the negative sign to add spaces to the left instead of the right. In general sprintf() is a good function to explore and use for formatting text and numbers as desired.

Mabyn
  • 316
  • 2
  • 20
1

This is a hack, but...

Let's add some empty factor levels in cyl between the real levels. Then we'll make sure they're included in the plot (using drop=FALSE) for spacing in the legend, but will set their colors and labels to empty values so that you can't see them in the legend. I found that I also needed to include override.aes=list(color="white") in order to avoid the blank legend key boxes still being ever-so-slightly visible in the legend.

mtcars$cyl = factor(mtcars$cyl, levels=c(4, 11:15, 6, 16:20, 8))
cols = hcl(seq(15,375,length.out=4)[1:3], 100, 65)

ggplot(mtcars, aes(cyl, fill=cyl)) + 
  geom_bar() +
  coord_flip() +
  scale_fill_manual(values=c(cols[1], rep("white",5), cols[2], rep("white",5), cols[3]), 
                    labels=c(4, rep("",5), 6, rep("",5), 8), drop=FALSE) +
  theme(legend.position = 'top') +
  guides(fill = guide_legend(title=NULL, nrow=1, override.aes=list(color="white"))) 

enter image description here

eipi10
  • 91,525
  • 24
  • 209
  • 285
1

With ggplot2 v3.0.0, we can use legend.spacing.x to manipulate the space between legend keys.

library(ggplot2)

ggplot(mtcars, aes(factor(cyl), fill = factor(cyl))) + 
  geom_bar() +
  coord_flip() +
  theme(legend.position = 'top') +
  guides(fill = guide_legend(title = "Cyl")) +
  theme(legend.spacing.x = unit(0.5, 'cm'))

Created on 2018-05-30 by the reprex package (v0.2.0).

Tung
  • 26,371
  • 7
  • 91
  • 115
  • 1
    Great +1. If only it didn't space the text from the legend color/symbol in horizontol legends. Ideally, we could space each name-key pair from each other, not just space out all the items regardless. – Jack Wasey Aug 03 '18 at 23:32
  • 1
    @JackWasey: did you mean something like this https://stackoverflow.com/a/50885122/786542? – Tung Aug 04 '18 at 06:13
  • this is not what the OP is asking for, @Tung. He wants increased spacing, but maintenance of the distance between the text and its respective label (so the pink, green, orange spaced apart.. but the 4, 6 and 8 nestled up against the colour blocks. How this isn't a standard option is beyond me. – kabammi Sep 23 '20 at 02:52
  • OK, I've found something, I'll post it below. – kabammi Sep 23 '20 at 02:59
1

Not a hack here, this is the way to do it:

Use theme(legend.text = element_text(margin = margin(r = 2, unit = 'cm')))

ggplot(mtcars, aes(factor(cyl), fill = factor(cyl))) +
  geom_bar() +
  coord_flip() +
  theme(
    legend.position = 'top',
    legend.title = element_blank(),
    legend.text = element_text(margin = margin(r = 2, unit = 'cm'))
  )

Will do it.

kabammi
  • 326
  • 3
  • 14