'Controlling legend key spacing and color in ggplot2 [duplicate]
Issue
I am sure this is a very fix for someone who has done it before. I want to have a different spacing between legend key box 2 and 3 in ggplot2. Is there any trick to have a different spacing between the boxes of the legend? I also want to match the color of the legend box and the text. Currently, the legend text is black. So, I have two questions here
- How I add a different spacing between the legend keys: bigger spacing between box 2 and 3, keep the spacing between other legends key boxes similar).
- How to match the color of the legend key box with the text? Here is my data and code. I have also added the design of the graph I want to produce from this code.
Packages
library(tidyverse)
library(ggplot2)
library(ggtext)
Sample Data
Food = c("meat", "meat", "meat", "meat", "wheat","wheat","wheat",
"wheat", "maize","maize","maize","maize")
Subgroup = c("Male", "Female", "Urban", "Rural", "Male", "Female",
"Urban", "Rural", "Male", "Female","Urban", "Rural")
mean = c(8.66, 10.45, 9.88, 7.32, 21.04, 19.65, 20.26, 20.87, 51.06 , 44.51, 47.60, 48.40)
df <- data.frame(Food, Subgroup, mean)
df$Subgroup[df$Subgroup == "Urban"] <- 1
df$Subgroup[df$Subgroup == "Rural"] <- 2
df$Subgroup[df$Subgroup == "Female"] <- 3
df$Subgroup[df$Subgroup == "Male"] <- 4
df$Subgroup <- factor(df$Subgroup,
levels = c(1, 2, 3, 4),
labels = c("Urban", "Rural", "Female", "Male"))
#Color code
colorPanel = c( '#42235e', '#7d103d', '#007da5', '#003b5d' )
# bar chart
Plot_FBGDS <- ggplot(df, aes(x = Food, y = mean, fill = Subgroup)) +
geom_col(stat = "identity", position = position_dodge(-0.84), width = 0.82) +
scale_y_continuous(breaks = c(0,20, 40, 60,80), expand = c(0,0),
limits = c(0,100),
labels = function(x) paste0(x, "%")) +
geom_text(aes(label = paste0(mean,"%"), y = mean + 2, color = Subgroup), stat = "identity",
size = 3, vjust = 0.5, face = "bold", family = "sans", position = position_dodge(-0.88)) +
scale_color_manual(values = colorPanel) +
scale_x_discrete(limits = c("meat",
"wheat",
"maize")) +
coord_flip() +
scale_fill_manual(values = colorPanel) +
labs( x= " ",
y = " ") +
theme(text = element_text(size = 14, color = "black", family = "sans"),
panel.background = element_rect(fill = "white"),
panel.border = element_blank(),
axis.text.y = element_text(family = "sans", color = "black", size = 14),
axis.text.x = element_blank(),
axis.line.x = element_blank(),
axis.line.y = element_line(),
axis.ticks.y.left = element_line(colour = "green"),
axis.ticks.length=unit(0, "cm"),
axis.title.x = element_text(size = 10, color = "black", family = "sans"),
axis.text = element_text(size = 10, color = "black",family = "sans"),
legend.key = element_rect(colour = NA, fill=NA, size= 7),
legend.text = element_text(size = 10, family = "sans"),
legend.margin=margin(t= -1, r= 2, b= 2, l= 2),
legend.title = element_blank(),
legend.key.height = unit(0.03, "npc"),
legend.key.width = unit(0.03, "npc"),
legend.position = c(0.85, 0.70),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank(),
panel.grid.minor.x = element_blank())
Plot_FBGDS
Current Plot
Solution 1:[1]
One option would be the ggnewscale package which allows to add a second fill legend. Doing so we could draw separate legends for Urban/Rural and for Male/Female for which the spacing could be set via legend.spacing.y and legend.margin. To make this work we have to add a duplicated geom_col where we have to explicitly map on the fill aes.
To color the legend text according to the bars you could make use of the ggtext package which allows styling of text via markdown, HTML and CSS. To this end use ggtext::element_markdown for legend.text and add labels to the legends where you set your desired colors using some HTML and CSS.
EDIT Following this answer by @teunbrand we could increase or set the spacing between legend keys via legend.spacing.y if we set byrow=TRUE in guide_legend. However, as legend.spacing.y also effects the spacing between legends you probably have to adjust the top and/or bottom legend.margin as well to get your desired spacing between the legends for each group.
library(ggplot2)
library(ggtext)
# Color code
colorPanel <- c("#42235e", "#7d103d", "#007da5", "#003b5d")
names(colorPanel) <- c("Urban", "Rural", "Female", "Male")
labels <- paste0("<span style='color:", colorPanel, ";'>", names(colorPanel), "</span>")
names(labels) <- names(colorPanel)
# bar chart
library(ggnewscale)
Plot_FBGDS <- ggplot(df, aes(x = Food, y = mean, fill = Subgroup)) +
geom_col(position = position_dodge(-0.84), width = 0.82) +
geom_text(aes(label = paste0(mean, "%"), y = mean + 1, color = Subgroup),
size = 3, hjust = 0, vjust = 0.5, fontface = "bold", family = "sans", position = position_dodge(-0.88), show.legend = FALSE
) +
scale_fill_manual(values = colorPanel, breaks = c("Urban", "Rural"), labels = labels[c("Urban", "Rural")],
aesthetics = c("color", "fill"),
guide = guide_legend(order = 1, byrow = TRUE)) +
new_scale_fill() +
new_scale_color() +
geom_col(aes(fill = Subgroup), position = position_dodge(-0.84), width = 0.82) +
scale_fill_manual(values = colorPanel, breaks = c("Female", "Male"), labels = labels[c("Female", "Male")],
aesthetics = c("color", "fill"),
guide = guide_legend(order = 2, byrow = TRUE)) +
scale_y_continuous(
breaks = c(0, 20, 40, 60, 80), expand = c(0, 0),
limits = c(0, 100),
labels = function(x) paste0(x, "%")
) +
scale_x_discrete(limits = c(
"meat",
"wheat",
"maize"
)) +
coord_flip() +
labs(x = " ", y = " ") +
theme(
text = element_text(size = 14, color = "black", family = "sans"),
panel.background = element_rect(fill = "white"),
panel.border = element_blank(),
axis.text.y = element_text(family = "sans", color = "black", size = 14),
axis.text.x = element_blank(),
axis.line.x = element_blank(),
axis.line.y = element_line(),
axis.ticks.y.left = element_line(colour = "green"),
axis.ticks.length = unit(0, "cm"),
axis.title.x = element_text(size = 10, color = "black", family = "sans"),
axis.text = element_text(size = 10, color = "black", family = "sans"),
legend.text = element_markdown(size = 10, family = "sans"),
legend.key = element_rect(colour = NA, fill = NA, size = 7),
legend.key.height = unit(0.03, "npc"),
legend.key.width = unit(0.03, "npc"),
legend.margin = margin(t = 4, r = 2, b = 4, l = 2),
legend.title = element_blank(),
legend.position = c(0.85, 0.70),
legend.spacing.y = unit(8, "pt"),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank(),
panel.grid.minor.x = element_blank()
)
Plot_FBGDS
#> Warning: position_dodge requires non-overlapping x intervals

Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

