He encontrado un antiguo trabajo en el que necesité crear un gráfico de dispersión entre dos variables numéricas y que en los márgenes de dicho gráfico aparecieran los boxplots de cada variable por separado. Los gráficos están construidos con las funciones gráficas básicas adaptando los márgenes de cada uno con el parámetro fig de la función par() y añadiendo cada gráfico al anterior con el parámetro new = TRUE. La función mtext() sirve para añadir texto en los márgenes de un gráfico y en los boxplots elimino los ejes con axes = TRUE. En el siguiente ejemplo utilizo los datos mtcars:

par(fig = c(0, 0.8, 0, 0.8), new = TRUE)
plot(mtcars$wt, mtcars$mpg, xlab = "Car Weight", ylab = "Miles Per Gallon")
par(fig = c(0, 0.8, 0.55, 1), new = TRUE)
boxplot(mtcars$wt, horizontal = TRUE, axes = FALSE)
par(fig = c(0.65, 1, 0, 0.8), new = TRUE)
boxplot(mtcars$mpg, axes = FALSE)
mtext("Scatterplot with marginal boxplots", side = 3, outer = TRUE, line = -3)


Aunque en este código se podrían cambiar los boxplots marginales por histogramas o densidades, hoy en día existen librerías que simplifican la tarea de tener que programarlos. La librería {ggplot2} tiene una serie de extensiones (https://exts.ggplot2.tidyverse.org/gallery/) que añaden más opciones al ya amplio ecosistema de gráficos creados con dicha librería.

Por ejemplo, la librería {ggExtra} permite añadir gráficos marginales a los scatterplots creados con {ggplot2}:

# install.packages(c('ggplot2', 'ggExtra'))
library(ggplot2)
library(ggExtra)
# Creamos el gráfico de dispersión
p <- ggplot(data = mtcars, aes(x = wt, y = mpg)) + geom_point() + xlab("Car Weight") + ylab("Miles Per Gallon")
# Boxplots marginales
ggMarginal(p, type = 'boxplot')

# Histogramas marginales
ggMarginal(p, type = 'histogram', fill = 'grey', bins = 8)

# Densidades marginales y por categorías
q <- ggplot(data = mtcars, aes(x = wt, y = mpg, colour = factor(cyl))) + geom_point() +
  labs(x = "Car Weight", y = "Miles Per Gallon", colour = 'Cylinders')
ggMarginal(q, type = 'density', groupColour = TRUE, groupFill = TRUE)