ggplot2 es un sistema para crear gráficos de forma declarativa, basándose en una gramática para gráficos. Su mecánica es la siguiente: le proporcionamos los datos, le decimos cómo asignar variables a la estética, qué tipos de gráficas usar y él se encarga de los detalles. Permite crear gráficos complejos de manera sencilla, aunque también es cierto que puede resultar complejo para hacer gráficos sencillos.

Vamos a utilizar las librerías ggplot2, ggpubr, readxl y patchwork (las instalamos, caso de no haberlo hecho antes):

library(ggplot2)
library(ggpubr)
# Instala la librería readxl, caso de que no lo esté
if(!"readxl" %in% installed.packages())
  install.packages("readxl")
# Carga la librería
library(readxl)
# Instala la librería patchwork, caso de que no lo esté
if(!"readxl" %in% installed.packages())
  install.packages("patchwork")
# Carga la librería
library(patchwork)

Diagramas de dispersión

Los diagramas de dispersión son útiles para representar dos variables numéricas (tanto discretas como continuas), habitualmente con el objetivo de determinar relaciones o patrones de comportamiento.

Si sólo nos interesa representar el diagrama de dispersión entre dos variables numéricas, bastaría con usar la función de la librería base plot:

# Generamos n valores distribuidos uniformemente entre 0 y 10
n=100
v1=runif(n,0,10)
# Generamos los valores de y como una transformación lineal
# de los valores de x, más una componente aleatoria
v2=3*v1+2+rnorm(n,0,3)
# Regresentamos la nube de puntos
plot(v1,v2)

Para incluir la recta de regresión muestral, que modela su relación, sería necesario estimarla con lm y almacenar su resultado en un objeto para poder representarlo en el gráfico:

# Estimamos la recta de regresión muestral y guardamos la salida 
# en el objeto regresion, para poder utilizarla posteriormente
regresion<-lm(v2~v1)
# Representamos el conjunto de datos
plot(v1,v2,pch=21,col="red",bg="gray",cex=2/3,
     main=" ", xlab="x", ylab="y",
     family="Times New Roman" , font=2)
# Añadimos la recta de regresión muestral
abline(regresion)
# Añadimos la expresión de la recta de regresión muestral
text(1.5,30,paste("y = ",round(coef(regresion)[2],2),
     "x +",round(coef(regresion)[1],2)))

Con ggplot2, la construcción del mismo gráfico es más natural. Lo creamos con ggplot pasándole como argumento el nombre del conjunto de datos; dentro de la estética (función aes()) incluimos las variables x e y; elegimos la geometría geom_point() para la nube de puntos y añadimos la recta de regresión muestral junto a su banda de confianza con geom_smooth,(method="lm"), su expresión con stat_regline_equation() (de la librería ggpubr) y también el valor del coeficiente de determinación \(R^2\) con stat_cor(), situándolos en las posiciones introducidas en label.x y label.y.

# Creamos un data.frame con las variables v1 y v2
data <- data.frame(x=v1,y=v2)
# Creamos el gráfico
ggplot(data)+
  aes(x=x,y=y)+
  geom_point()+
  geom_smooth(formula=y~x, method="lm",se=TRUE,level=0.95)+
  stat_regline_equation(label.x=7.5,label.y=15) +
  stat_cor(label.x=0,label.y=30)

Además, se pueden usar muchas opciones para personalizar el gráfico como alpha para la transparencia, family y font para el formato del texto, labs para las etiquetas del título, ejes, … A continuación vamos a tratar de imitar el gráfico obtenido con plot

# Creamos un data.frame con las variables v1 y v2
data <- data.frame(x=v1,y=v2)
# Creamos el gráfico
ggplot(data)+
  aes(x=x,y=y)+
  geom_point(color="red",fill="grey",shape=21)+
  geom_smooth(formula=y~x, method="lm",se=TRUE,level=0.95,color="black")+
  labs(title="Diagrama de dispersión",x="x",y="y",family="Times New Roman",font=2)+
  stat_regline_equation(label.x=7.5,label.y=15) +
  stat_cor(label.x=0,label.y=30)+
  theme_classic()

En ocasiones puede interesarnos incorporar otras variables tanto cualitativas como cuantitativas para apreciar relaciones más complejas. Podemos hacerlo utilizándolas para identificar distintos colores o formas (si se trata de factores o variables cualitativas) o tamaño de los puntos (en el caso de que sean numéricas).

Generamos un nuevo conjunto de datos, algo más complejo, con una variable cualitativa con 6 categorías (factor), dos variables cuantitativas continuas (x e y), entre las que habrá distintas relaciones según el valor del factor, y una variable cuantitativa discreta (z).

#Indicamos el número de datos que deseamos generar.
n <- 120
#A continuación, vamos a generar distintas variables numéricas (continuas, discretas), así como variables categóricas. 
v1 <- rep(c("A","B","C","D","E","F"),rep(n/6,6))
v2 <- runif(n,0,10)
v3 <- c(rep(5,20),v2[21:40],-v2[41:60]+10,
        v2[61:80]^2/10,exp(v2[81:100])*10/exp(10),rep(7,20))+rnorm(n)
v4 <- trunc(v2^2)
#Para poder emplear la librería ggplot2 es necesario considerar el conjunto de datos como un data.frame 
data <- data.frame(factor=v1,x=v2,y=v3,z=v4)

Si simplemente representamos las variables numéricas con un diagrama de puntos, no apreciamos ningún patrón de comportamiento:

ggplot(data)+
  geom_point(aes(x=x,y=y))

Sin embargo, si separamos en distintos gráficos según los valores del factor, con la función facet_wrap(), podemos distinguirlos:

ggplot(data)+
  geom_point(aes(x=x,y=y,color=factor))+
  facet_wrap(vars(factor))

También es posible agregar todos los datos en cada panel y destacar los correspondientes para cada uno de ellos, creando una capa con los mismos datos pero eliminando la variable categórica (en el ejemplo factor).

ggplot(data,aes(x=x,y=y,color=factor))+
  geom_point(data=transform(data,factor=NULL),color="grey85")+
  geom_point()+
  facet_wrap(vars(factor))

Otra opción sería representarlos en un único gráfico, diferenciando las distintas categorías, por ejemplo por colores:

ggplot(data)+
  geom_point(aes(x=x,y=y,colour=factor))

En este caso no es muy adecuada, pero sí lo sería cuando hay alguna relación entre los distintos patrones de comportamiento. Por ejemplo, lo que ocurre con este otro conjunto de datos:

#Indicamos el número de datos que deseamos generar.
n <- 120
#A continuación, vamos a generar distintas variables numéricas (continuas, discretas), así como variables categóricas. 
v1 <- rep(c("A","B","C","D","E","F"),rep(n/6,6))
v2 <- runif(n,0,10)
v3 <- c(rep(0,20),1/4*v2[21:40],2+1/2*v2[41:60]+1,
        v2[61:80]+2,2*v2[81:100]+3,4*v2[101:120]+4)+rnorm(n)
v4 <- v2^3
#Para poder emplear la librería ggplot2 es necesario considerar el conjunto de datos como un data.frame 
data <- data.frame(factor=v1,x=v2,y=v3,z=v4)
ggplot(data)+
  geom_point(aes(x=x,y=y,colour=factor))

Igual que ocurría con la función básica plot, podemos modificar el tamaño de los puntos, añadir título al gráfico, modificar los ejes (con la función labs), la leyenda (con scale_.._discrete), su posición (con theme()), o el estilo del gráfico (empleando theme_..).

ggplot(data)+
  geom_point(aes(x=x,y=y,colour=factor))+
  labs(title="Diagrama de dispersión",x="x",y="y",family="Times New Roman", font=2)+
  scale_colour_discrete(name="Tipo",labels=c("1","2","3","4","5","6"))+
  theme_minimal()+
  theme(legend.position = "top")

Además del color, también podemos modificar la forma de los puntos en función de alguna variable (con shape)1.

ggplot(data)+
  geom_point(aes(x=x,y=y,shape=factor,colour=factor))+
  labs(title="Diagrama de dispersión",x="x",y="y",family="Times New Roman", font=2)+
  theme_classic()+
  theme(legend.position = "left")

También es posible hacer lo mismo con el tamaño de los puntos (size). Esta opción puede ser muy útil cuando queremos dar pesos a nuestros datos (porque no todos son igual de fiables) o simplemente incorporar otra variable numérica sin pasar a 3D.

Además, podemos eliminar leyendas utilizando la función guides(). Para que se puedan diferenciar con claridad los puntos, se puede utilizar alpha = .. en la función geom_point() para indicar la transparencia de los puntos.

ggplot(data)+
  geom_point(aes(x=x,y=y,colour=factor,size=z),alpha=0.5)+
  labs(title="Diagrama de dispersión",x="x",y="y",family="Times New Roman", font=2)+
  scale_colour_discrete(name="Factor",labels=c("A","B","C","D","E","F"))+
  guides(size="none")+
  theme_bw()+
  theme(legend.position = "bottom")

Podemos representar la recta de regresión de los subconjuntos según el valor del factor, con la función geom_smooth().

ggplot(data)+
  geom_point(aes(x=x,y=y,colour=factor),alpha=0.75)+
  geom_smooth(aes(x=x,y=y,colour=factor),formula=y~x, method="lm",se=FALSE)+
  labs(title="Diagrama de dispersión",x="x",y="y",family="Times New Roman", font=2)+
  scale_colour_discrete(name="Factor",labels=c("A","B","C","D","E","F"))+
  theme_linedraw()

En ambas funciones o capas (geom_point y geom_smooth), la estética es la misma, aes(x=x,y=y,colour=factor), por lo que podemos situarla en una capa (escribiéndola una única vez). Además, es posible añadir otros elementos como el intervalo de confianza al nivel de confianza del 99% (con se=TRUE y level=0.99 en la función geom_smooth()).

ggplot(data)+
  aes(x=x,y=y,colour=factor)+
  geom_point(alpha=0.75)+
  geom_smooth(formula=y~x, method="lm",se=TRUE, level=0.99,)+
  labs(title="Diagrama de dispersión",x="x",y="y",family="Times New Roman", font=2)+
  scale_colour_discrete(name="Modelo",labels=c("A","B","C","D","E","F"))+
  stat_regline_equation(label.x=c(0,0,0,0,0,0),
                        label.y=c(40,36,32,28,24,20))+
  theme_light()

Histogramas

Consideremos el conjunto de datos reales sobre la estructura salarial en España en el año 2018. Sus variables son:

# Lee el conjunto de datos y lo almacena en el objeto data
# Es necesario modificar la ruta de acceso al archivo por la propia
data <- read_excel("/Users/maribelparraarevalo/Library/Mobile Documents/com~apple~CloudDocs/GraficosR/Datos/Salario2018.xlsx")

Si por ejemplo nos interesara comparar la distribución de salarios por sexo, utilizamos la función geom_histogram() con la estética aes(fill=Sexo).

ggplot(data)+
  aes(x=SalarioBase,fill=Sexo)+
  geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Seguramente no se corresponda con el gráfico esperado. Por un lado, representa histogramas apilados; para que aparezcan superpuestos debemos dar el valor identity al argumento position. Por otro lado, tal como nos indica el mensaje, por defecto se representan 30 intervalos que no tiene por qué ser una cantidad adecuada para nuestro conjunto. Se puede modificar dando otro valor a bins (o a center y/o binwidth para completar la personalización de los intervalos eligiendo su centro y anchura, respectivamente) o indicando los puntos de corte en breaks.

ggplot(data)+
  aes(x=SalarioBase,fill=Sexo)+
  geom_histogram(bins=20,position="identity")+
  labs(title="Histograma",x="Salario Base",y="")+
  guides(colour="none")+
  theme_bw()+
  geom_freqpoly(bins=20,aes(colour=Sexo))

Además hemos añadido el polígono de frecuencias, con geom_freqpoly. Las frecuencias absolutas no suelen ser la mejor opción a efectos comparativos, especialmente cuando los tamaños de las muestras son muy diferentes. En estos casos, resulta más adecuado representar las frecuencias relativas o densidades. Podemos representarlas simplemente añadiendo la estética aes(y=..density..) . Además, hemos añadido alpha=0.5 para definir transparencia y poder ver ambas distribuciones, y definido el color de los bordes además del relleno.

ggplot(data)+
  aes(x=SalarioBase,fill=Sexo,colour=Sexo)+
  geom_histogram(aes(y=..density..),bins=20,alpha=0.5,position="identity")+
  geom_density(alpha=0.5)+
  labs(title="Histograma",x="Salario Base",y="")+
  guides(colour="none")+
  theme_minimal()
## Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(density)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Para tener una imagen más suave de las frecuencias relativas, y que no dependa de los intervalos utilizados, hemos superpuesto las densidades estimadas sobre los histogramas añadiendo la capa geom_density.

Para apreciar más claramente ambas representaciones, hacemos transparente el área de la densidad (con alpha=0) y modificamos el tipo y color de las líneas añadiendo aes(linetype=Sexo,colour=Sexo).

ggplot(data)+
  aes(x=SalarioBase,fill=Sexo,colour=Sexo)+
  geom_histogram(aes(y=..density..),bins=20,alpha=0.5,position="identity")+
  geom_density(aes(linetype=Sexo,colour=Sexo), alpha=0)+
  labs(title="",x="Salario Base",y="")+
  theme_classic()

Otra opción es agregar un espacio entre los rectángulos para poder ver ambos histogramas con position="dodge"

ggplot(data)+
  aes(x=SalarioBase,fill=Sexo,colour=Sexo)+
  geom_histogram(aes(y=..density..),bins=20,alpha=0.5,position="dodge")+
  geom_density(aes(linetype=Sexo,colour=Sexo), alpha=0)+
  labs(title="",x="Salario Base",y="")+
  theme_classic()

Box-plot

Otra forma de comparar distintos subconjuntos es con diagramas de caja y bigotes, que se obtienen con geom_boxplot().

Vamos a cargar otro conjunto de datos, en este caso con extensión Rdata, sobre hábitos de compras por Internet:

load("/Users/maribelparraarevalo/Library/Mobile Documents/com~apple~CloudDocs/GraficosR/Datos/datosTICr")

Comenzamos representando el diagrama de caja y bigotes para la variable edad, separada por sexo, personalizando su aspecto y añadiendo intervalos de confianza (al 95%) para las medianas:

ggplot(datosTIC,aes(x=Edad,y=Sexo,fill=Sexo,colour=Sexo))+
  geom_boxplot(notch=TRUE)

Por defecto no añade las líneas horizontales de los bigotes, pero se pueden agregar con stat_boxplot, estableciendo geom="errorbar", y además se puede controlar su ancho con width.

ggplot(datosTIC,aes(x=Edad,y=Sexo,fill=Sexo,colour=Sexo))+
  stat_boxplot(geom="errorbar",with=0.01)+
  geom_boxplot(alpha=0.5)
## Warning in stat_boxplot(geom = "errorbar", with = 0.01): Ignoring unknown
## parameters: `with`

Se pueden añadir otros factores, como por ejemplo la confianza en las TICs

ggplot(datosTIC)+
  aes(x=Confianza, y=Edad, colour=Sexo)+
  geom_boxplot(notch = TRUE)

ggplot(datosTIC)+
  aes(x=Sexo, y=Edad, colour=Confianza)+
  geom_boxplot(notch = TRUE)

Cualquiera de los dos gráficos sirve para comprobar que no se aprecian diferencias significativas entre hombres y mujeres y que las personas más jóvenes muestran mayor confianza en las TICs, siendo las personas mayores quienes menos responden a la pregunta (valores representados como NA).

Por defecto, los valores de las variables aparecen ordenados alfanuméricamente. Si queremos modificar dicho orden, para facilitar la lectura de una representación gráfica se puede usar la función factor para reordenarlos según nuestra conveniencia.

Diagramas de violín

Este tipo de representaciones, de algún modo, combinan las cualidades de los diagramas de caja y bigotes y los histogramas, permitiendo hacer comparaciones de forma muy rápida y sencilla entre grupos sin prescindir de los detalles. Lo obtenemos con la función geom_violin().

ggplot(datosTIC)+
  aes(x=Sexo,y=Edad, fill=Confianza)+
  geom_violin()+
  labs(title="Diagrama de violín",x="Sexo",y="Edad")+
  theme_bw()

Con las distintas opciones de position podemos cambiar la posición relativa de los distintos “violines”. Por ejemplo, position="identity" los superpone:

ggplot(datosTIC)+
  aes(x=Sexo,y=Edad, fill=Confianza)+
  geom_violin(alpha=0.5,position="identity")+
  labs(title="Diagrama de violín",x="Sexo",y="Edad")+
  theme_bw()

Si queremos que sea horizontal, podemos intercambiar las variables x e y o usar coord_flip

ggplot(datosTIC)+
  aes(x=Sexo,y=Edad, fill=Confianza)+
  geom_violin(alpha=0.5,position="identity")+
  labs(title="Diagrama de violín",x="Sexo",y="Edad")+
  coord_flip()+
  theme_bw()

Además, se pueden agregar valores como la mediana o la media, junto a su intervalo de confianza con fun.data`

ggplot(datosTIC,aes(x=Sexo,y=Edad,
                    fill=Confianza,colour=Confianza))+
  labs(title="Diagrama de violín",x="Sexo",y="Edad")+
  geom_violin(alpha=0.5,position="Identity")+
  stat_summary(fun.data="mean_cl_boot",geom="pointrange")+
  theme_bw()
## Warning: Computation failed in `stat_summary()`.
## Caused by error in `fun.data()`:
## ! The package "Hmisc" is required.

Diagramas de barras

Podemos integrar en ellos la información de varias variables cualitativas o numéricas con pocos valores distintos

ggplot(datosTIC, aes(x=Confianza,fill=UsoInternet))+
  geom_bar(position="identity")

Si nos interesa separar por otra variable, incluimos una capa con facet_wrap.

ggplot(datosTIC, aes(x=Confianza,fill=UsoInternet))+
  geom_bar(position="identity")+
  facet_wrap(~Sexo)

Como ya hemos mencionado, ggplot2 se basa en toda una gramática, por lo que el abanico de posibilidades es tan amplio que solo podremos mostrar algunas de ellas. Recomendamos al lector jugar con las distintas funciones y opciones, asignando otros valores para entender mejor su efecto y probar con otras para comprobar cómo es posible obtener cualquier gráfico que nos imaginemos.

Combinar gráficos

El paquete ggplot2 no integra una funcionalidad específica para combinar varios gráficos en una única figura, pero existen otras librerías adicionales que sí lo permiten. Aunque existen varias, aquí trabajaremos con patchwork que es la más sencilla de usar.

Comenzamos creando 4 gráficos muy sencillos con los que mostrar las capacidades de la librería.

# Simulación n datos 
set.seed(5)
n=100
datos <- data.frame(x=runif(n),
                    y=rnorm(n))

# Caja y bigotes
g1 <- ggplot(datos, aes(y = y)) +
  geom_boxplot()
# Densidad
g2 <- ggplot() +
  stat_function(fun = dnorm, geom = "density",
                xlim = c(-3, 3), fill = "white") 
# Secuencia
g3 <- ggplot(datos, aes(x=x,y = y)) +
  geom_line()
# Nube de puntos
g4 <- ggplot(datos, aes(x = x, y = y)) +
  geom_point(color="red3")

Si unimos los gráficos con + patchwork intentará que la figura resultante sea lo más cuadrada posible:

g1+g2+g3+g4

Para personalizar el número de filas y columnas de la figura se puede usar la función plot_layout y para especificar las anchuras y alturas relativas de los gráficos los argumentos widths y heights, respectivamente.

diseño<-"12
33"
g1+g2+g3+plot_layout(design=diseño)

Una forma equivalente de obtener el mismo resultado

(g1|g2)/g3


  1. Esta variable no tendría por qué ser la misma.↩︎