How to Add an External Legend to Ggpairs()

How to add an external legend to ggpairs()?

I am working on something similar, this is the approach i would take,

  1. Ensure legends are set to 'TRUE' in the ggpairs function call
  2. Now iterate over the subplots in the plot matrix and remove the legends for each of them and just retain one of them since the densities are all plotted on the same column.

    colIdx <- c(3,5,6,7)

    for (i in 1:length(colIdx)) {

    # Address only the diagonal elements
    # Get plot out of matrix
    inner <- getPlot(p, i, i);

    # Add any ggplot2 settings you want (blank grid here)
    inner <- inner + theme(panel.grid = element_blank()) +
    theme(axis.text.x = element_blank())

    # Put it back into the matrix
    p <- putPlot(p, inner, i, i)

    for (j in 1:length(colIdx)){
    if((i==1 & j==1)){

    # Move legend right
    inner <- getPlot(p, i, j)
    inner <- inner + theme(legend.position=c(length(colIdx)-0.25,0.50))
    p <- putPlot(p, inner, i, j)
    }
    else{

    # Delete legend
    inner <- getPlot(p, i, j)
    inner <- inner + theme(legend.position="none")
    p <- putPlot(p, inner, i, j)
    }
    }
    }

incorporate standalone legend in ggpairs (take 2)

With some slight modification to solution 1: First, draw the matrix of plots without their legends (but still with the colour mapping). Second, use your trim_gg function to remove the diagonal spaces. Third, for the plot in the top left position, draw its legend but position it into the empty space to the right.

data(state)
dd <- data.frame(state.x77,
State = state.name,
Abbrev = state.abb,
Region = state.region,
Division = state.division)

columns <- c(3, 5, 6, 7)
colour <- "Region"

library(GGally)
library(ggplot2) ## for theme()

# Base plot
ggfun <- function(data = NULL, columns = NULL, colour = NULL, legends = FALSE) {
ggpairs(data,
columns = columns,
mapping = ggplot2::aes_string(colour = colour),
lower = list(continuous = "points"),
diag = list(continuous = "blankDiag"),
upper = list(continuous = "blank"),
legends = legends)
}

# Remove the diagonal elements
trim_gg <- function(gg) {
n <- gg$nrow
gg$nrow <- gg$ncol <- n-1
v <- 1:n^2
gg$plots <- gg$plots[v > n & v%%n != 0]
gg$xAxisLabels <- gg$xAxisLabels[-n]
gg$yAxisLabels <- gg$yAxisLabels[-1]
return(gg)
}

# Get the plot
gg0 <- trim_gg(ggfun(dd, columns, colour))

# For plot in position (1,1), draw its legend in the empty panels to the right
inner <- getPlot(gg0, 1, 1)

inner <- inner +
theme(legend.position = c(1.01, 0.5),
legend.direction = "horizontal",
legend.justification = "left") +
guides(colour = guide_legend(title.position = "top"))

gg0 <- putPlot(gg0, inner, 1, 1)
gg0

Sample Image

Legend using ggpairs

There should be a standard way to to it, but I could not find one. The old print function is still there and accessible but as in internal function. Try this instead of the print:

  GGally:::print_ggpairs_old(p)

Sample Image

And if you add this (thanks to this post answer Legend using ggpairs )

colidx <- c(3,5,6,7)
for (i in 1:length(colidx)) {

# Address only the diagonal elements
# Get plot out of plot-matrix
inner <- getPlot(p, i, i);

# Add ggplot2 settings (here we remove gridlines)
inner <- inner + theme(panel.grid = element_blank()) +
theme(axis.text.x = element_blank())

# Put it back into the plot-matrix
p <- putPlot(p, inner, i, i)

for (j in 1:length(colidx)){
if((i==1 & j==1)){

# Move the upper-left legend to the far right of the plot
inner <- getPlot(p, i, j)
inner <- inner + theme(legend.position=c(length(colidx)-0.25,0.50))
p <- putPlot(p, inner, i, j)
}
else{

# Delete the other legends
inner <- getPlot(p, i, j)
inner <- inner + theme(legend.position="none")
p <- putPlot(p, inner, i, j)
}
}
}

You get something much better:

Sample Image

Is it possible to combine a ggplot legend and table

A simple approach is to use the legend labels themselves as the table. Here I demonstrate using knitr::kable to automatically format the table column widths:

library(knitr)
table = summary.table %>%
rename(`Prb FR` = prob.fr, `Prb ED` = prob.ed.n) %>%
kable %>%
gsub('|', ' ', ., fixed = T) %>%
strsplit('\n') %>%
trimws
header = table[[1]]
header = paste0(header, '\n', paste0(rep('─', nchar(header)), collapse =''))
table = table[-(1:2)]
table = do.call(rbind, table)[,1]
table = data.frame(N=summary.table$N, lab = table)

plot_data = full.data %>%
group_by(N) %>%
do({
tibble(error = seq(min(.$error), max(.$error),length.out=100),
prob.ed.n = pchip(.$error, .$prob.ed.n, error))
}) %>%
left_join(table)

ggplot(plot_data, aes(x = error, y = prob.ed.n, group = N, colour = lab)) +
geom_line() +
guides(color = guide_legend(header, reverse=TRUE,
label.position = "left",
title.theme = element_text(size=8, family='mono'),
label.theme = element_text(size=8, family='mono'))) +
theme(
legend.key = element_rect(fill = NA, colour = NA),
legend.spacing.y = unit(0, "pt"),
legend.key.height = unit(10, "pt"),
legend.background = element_blank())

Sample Image

ggplot2 legend showing but variables in plot don't have colour

Option 1

This should work for you.

I calculated a scaled version of avgbase, then I transformed your data into long-format. Because this generally helps when you want to group variables within a plot.

In ggplot(), I filter in every geom_ the specific data points I want to plot (i.e., in geom_line() I filter for temp and avgbase_scaled because I want them plotted as lines).

library(tidyverse)

h1enraw <-structure(list(run = c(1738, 1739, 1740, 1741, 1742, 1743),
temp = c(19, 19, 19, 19, 21, 22),
avgbase = c(1386, 1386, 1389, 1389, 1352, 1336),
no2c = c(6.98, 6.96, 6.94, 6.99, 7.01, 7.01),
no3c = c(18.52, 17.6, 18.77, 19.81, 18.22, 18.60)),
row.names = c(NA, 6L), class = "data.frame")

scaleRight <- 40
ymax <- 43

h1enraw %>%
mutate(avgbase_scaled = avgbase/scaleRight, .keep = "unused") %>%
pivot_longer(c(-run), names_to = "value_type", values_to = "value") %>%
ggplot(aes(x = run, color = value_type)) +
geom_line(data = . %>% filter(value_type %in% c("avgbase_scaled",
"temp")),
aes(y = value), size=0.9)+
geom_point(data = . %>% filter(value_type %in% c("no2c",
"no3c")),
aes(y = value), size=1)+
scale_y_continuous(breaks=seq(0,40,2), expand = expansion(mult = c(0,.05)),
sec.axis = sec_axis(~.*scaleRight, name = "Average Baseline (Transmitance Units)"))+
coord_cartesian(ylim = c(0, ymax)) +
theme_classic() +
labs( y="Temperature (°C) / Concentration (ppm)", x="Run",
title = "H1 - High Temperature Cycle") +
theme(text = element_text(size=9),
axis.text.x = element_text(angle=90, vjust=0.6),
axis.text=element_text(size=12),
axis.title=element_text(size=14,face="bold"),
panel.border = element_rect(colour = "chocolate1", fill=NA, size=0.5),
legend.position = "bottom",
legend.title=element_blank(),
legend.text = element_text(size=8),
axis.title.y = element_text(size=10),
plot.title = element_text(size=14, face="bold"))+
scale_color_manual(labels = c("Temperature", expression(NO[2]), expression(NO[3]), "Avg Baseline"),
values = c("temp"="cornflowerblue", "no2c"="red",
"no3c"="darkgreen", "avgbase_scaled"="chocolate1"),
breaks=c("temp","no2c","no3c","avgbase_scaled"))

Created on 2022-08-30 with reprex v2.0.2

Option 2

Here an option without data transformation: You have to use the color parameter within aes() and assign the correct colnames:

scaleRight <- 40
ymax <-43

ggplot(h1enraw, aes(x=run)) +
geom_path(aes(y=temp, color="temp"), size=0.9) +
geom_point(aes(y=no2c, color="no2c")) +
geom_point(aes(y=no3c, color="no3c"))+
geom_line(aes(y=avgbase/scaleRight, color="avgbase"), size=0.9) +
scale_y_continuous(breaks=seq(0,40,2), expand = expansion(mult = c(0,.05)),
sec.axis = sec_axis(~.*scaleRight, name = "Average Baseline (Transmitance Units)")) +
coord_cartesian(ylim = c(0, ymax)) +
theme_classic() +
labs( y="Temperature (°C) / Concentration (ppm)", x="Run",
title = "H1 - High Temperature Cycle") +
theme(text = element_text(size=9),
axis.text.x = element_text(angle=90, vjust=0.6),
axis.text = element_text(size=12),
axis.title = element_text(size=14,face="bold"),
legend.position = "bottom",
legend.title = element_blank(),
legend.text = element_text(size=8),
axis.title.y = element_text(size=10),
plot.title = element_text(size=14, face="bold"))+
scale_color_manual(labels=c("Temperature", "NO2", "NO3", "Avg Baseline"),
values = c("temp"="cornflowerblue", "no2c"="red",
"no3c"="darkgreen", "avgbase"="chocolate1"))

Created on 2022-08-30 with reprex v2.0.2



Related Topics



Leave a reply



Submit