6
votes

The Problem

I want to place the legend of my plot above the plot. I also want to place the legend key symbol (colored squares) above the legend key labels (legend text). Unfortunately, when I do this, the legend key symbol "stretches" to fit the size of the label. I imagine ggplot2 is working fine, but how could I manually override this feature?

How do I keep a consistent legend key symbol across the top with labels of variable lengths?

Reproducible Example

(It's not necessarily a minimal example, just in case the structure of my actual code, like the coord_flip and fill calls, has an impact)

library(dplyr)
library(ggplot2)

dataFrame <- diamonds %>%
              group_by(color, cut) %>%
              summarise(count = n()) %>%
              group_by(color) %>%
              mutate(percent = count/sum(count),
                    pretty_label = paste0(round(percent*100, 1), "%")) %>%
              ungroup()

p <- ggplot(data = dataFrame, mapping = aes(x=color, y = percent, group = cut))+
      geom_bar(aes(fill = cut), stat = "identity", position = "fill")+
      geom_text(aes(label = pretty_label), position=position_fill(vjust=0.5), colour="white", stat = "identity")+
      coord_flip()+
      theme(legend.position="top")+
      guides(fill = guide_legend(label.position = "bottom", reverse = TRUE))

plot(p)

legend symbols stretch with labels

Notice how each of the legend symbols are different sizes, depending on the length of the label.

What I have already tried

I imagine it has something to do with guides, but I can't seem to get it right. Using the plot above (p), I have tried the following and more:

  1. From here and here: p + guides(colour = guide_legend(override.aes = list(size=3)))

  2. From here: p + guides(colour = guide_legend(keywidth = .5, keyheight = .5)) and p + guides(colour = guide_legend(keywidth = unit(.5, "cm"), keyheight = unit(.5, "cm")))

  3. From here: (Trying to wrap labels) p + guides(color = guide_legend(nrow = 2))

I've tried other, less "logical" attempts just because. None of which worked.

Final Thoughts

I could just be having a difficult time knowing what to search for. I am always open to figuring out the problem myself, if you are able to point me in the right direction. Any additional resources are more than welcome.

Thanks, in advance!

Session Output

> sessionInfo()
R version 3.5.1 (2018-07-02)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252    LC_MONETARY=English_United States.1252
[4] LC_NUMERIC=C                           LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] bindrcpp_0.2.2 ggplot2_3.0.0  dplyr_0.7.6   

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.18      bindr_0.1.1       magrittr_1.5      tidyselect_0.2.4  munsell_0.5.0     colorspace_1.3-2  viridisLite_0.3.0
 [8] R6_2.2.2          rlang_0.2.1       plyr_1.8.4        tools_3.5.1       grid_3.5.1        gtable_0.2.0      withr_2.1.2      
[15] yaml_2.1.19       lazyeval_0.2.1    assertthat_0.2.0  digest_0.6.17     tibble_1.4.2      purrr_0.2.5       glue_1.2.0       
[22] labeling_0.3      compiler_3.5.1    pillar_1.2.3      scales_0.5.0      pkgconfig_2.0.1  
1
All the options you have under "What I have already tried" work to some extent, if you replace colour with fill, e.g. p + guides(fill = guide_legend(override.aes = list(size=3))). You have a fill scale, not a colour scale.Marius
I thought that might be the case too. I tried it on my actual data, but my labels were too long and I wasn't increasing the size by enough. If the size is less than the longest label, it doesn't have an impact (at least for me). I neglected to try those possibilities on the example I gave, but that might have led me in the right direction. Thanks!Christian Million

1 Answers

7
votes

I don't know if there's a way to control the width of the legend color boxes separately from the text (other than hacking the legend grobs). However, if you add legend.key.width=unit(1.5, "cm") in your theme statement, all of the color boxes will be expanded to the same width (you may have to tweak the 1.5 up or down a bit to get the desired box widths).

library(scales)

ggplot(dataFrame, aes(x=color, y = percent))+
  geom_bar(aes(fill = cut), stat = "identity") +
  geom_text(aes(label = pretty_label), position=position_fill(vjust=0.5), 
            colour="white", size=3)+
  coord_flip()+
  theme(legend.position="top",
        legend.key.width=unit(1.5, "cm"))+
  guides(fill = guide_legend(label.position = "bottom", reverse = TRUE)) +
  scale_y_continuous(labels=percent)

enter image description here

You can save a little space by putting Very Good on two lines:

library(forcats)

ggplot(dataFrame, aes(x=color, y = percent, fill = fct_recode(cut, "Very\nGood"="Very Good")))+
  geom_bar(stat = "identity") +
  geom_text(aes(label = pretty_label), position=position_fill(vjust=0.5), 
            colour="white", size=3)+
  coord_flip()+
  theme(legend.position="top",
        legend.key.width=unit(1.2, "cm"))+
  guides(fill = guide_legend(label.position = "bottom", reverse = TRUE)) +
  labs(fill="Cut") +
  scale_y_continuous(labels=percent)

enter image description here