4

For a ggplot graph, I wish to have the values of the legend (here, 0 and 1) to be positioned over the colours they represent, and not to the side of them. By which I mean not to the left, right, above or below the coloured square, but within the square itself. This would result in a number 0 inside a red square and the number 1 inside a blue square. How can this be done?

ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
geom_bar() +
theme(legend.position = "top", 
    legend.direction = "horizontal") +
guides(color = guide_legend(title.position = "left", label.position = "top")) 
Nevil
  • 161
  • 1
  • 11

2 Answers2

5

Since you're using fill you need to use fill within guides then play with label.vjust and title.vjust to get everything to line up.

library(tidyverse)

ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
  geom_bar() +
  theme(legend.position = "top", 
        legend.direction = "horizontal") +
  guides(fill = guide_legend(label.vjust = -7, label.position = "top", title.vjust = 0.2))

Created on 2018-11-23 by the reprex package (v0.2.1)

Jake Kaupp
  • 7,892
  • 2
  • 26
  • 36
0

With minor adjustment, you can use the approach shown at change-geom-texts-default-a-legend-to-label-string-itself. This changes the key generating function, and so allows ggplot2 to calculate the positions to place the labels for you.

Th idea is to keep the original rectGrob for the geom_bar legend key and use an additional textGrob to place the label on top.

library(ggplot2)
library(grid)

oldK <- GeomBar$draw_key # to save for later

# see other answer on how to tweak function based n manual colours
GeomBar$draw_key <- function (data, params, size, 
                              var=unique(mtcars$vs),
                              cols=scales::hue_pal()(length(var))) {

    # match labels to the fill colour
    txt <- if(is.factor(var)) levels(var) else sort(var)
    txt <- txt[match(data$fill, cols)]

    # This is from the original GeomBar$draw_key -------------------
    lwd <- min(data$size, min(size)/4)
    rg <- rectGrob(width = unit(1, "npc") - unit(lwd, "mm"), height = unit(1, 
        "npc") - unit(lwd, "mm"), gp = gpar(col = data$colour, 
        fill = alpha(data$fill, data$alpha), lty = data$linetype, 
        lwd = lwd * .pt, linejoin = "mitre"))
    # ---------------------------------------------------------------

    # Add text label: lifted from other answer
    # hard-coded text size multiplier -- got to be a better way!
    tg <- textGrob(txt, 0.5, 0.5,  
             just="center", 
             gp = gpar(col = "black", 
                       fontfamily = data$family, 
                       fontface = data$fontface, 
                       fontsize = 10*data$size * .pt)) 
    # ---------------------------------------------------------------

    grobTree(rg, tg) # output new key
}

You can then plot

ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
  geom_bar() +
  theme(
    legend.position = "top", 
    legend.direction = "horizontal",
    legend.text = element_blank())

# reset key function to original
GeomBar$draw_key <- oldK

enter image description here

This should be fairly robust to resizing your plot.

user20650
  • 24,654
  • 5
  • 56
  • 91