Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ggplot2: Top legend key symbol size changes with legend key label

Tags:

r

legend

ggplot2

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  
like image 853
Christian Million Avatar asked Oct 24 '18 22:10

Christian Million


1 Answers

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

like image 62
eipi10 Avatar answered Sep 29 '22 07:09

eipi10