1. OBJETIVO DE LA SESIÓN

El objetivo de la sesión es aprender los conceptos básicos para poder realizar gráficos de gran calidad. Este tutorial es solo una introducción porque, como veremos inmediatamente, hay muchas teclas que tocar si queremos hacer un gráfico. En R podemos diseñar hasta el más mínimo detalle de un gráfico para obtener un resultado profesional.

IMPORTANTE: Los materiales de esta sesión (v.1.0) fueron compilados el día 2022-05-25 y pueden ser susceptibles de modificación (corrección de errores, ampliación de contenidos, etc.)

2. INTRODUCCIÓN

En R tenemos disponibles distintos sistemas gráficos, aunque los más comunes son:

  • Sistema gráfico base de R (autor: R Core Team and contributors). El núcleo (core en inglés) gráfico en R se encuentra en los paquetes graphics (contiene las funciones de gráficos base: plot, hist, etc.) y grDevices (que implementa los distintos dispositivos gráficos: pdf, ps, png, …).
  • Lattice (autor:Deepayan Sarkar). El libro Lattice. Multivariate Data Visualization with R (http://www.springer.com/gp/book/9780387759685) proporciona una gran cantidad de ejemplos (con código R).
Lattice (Deepayan Sarkar, 2008)

Lattice (Deepayan Sarkar, 2008)


  • ggplot2 (autor: Hadley Wickham). Hadley Wickham ha escrito un libro en el que explica de forma muy didáctica los fundamentos para elaborar gráficos con este paquete.
ggplot2 (Hadley Wickham, 2016)

ggplot2 (Hadley Wickham, 2016)


En este módulo vamos a aprender a crear gráficos (básicos, LA VISUALIZACIÓN ES TODO UN MUNDO!!!), puesto que constituyen herramientas enormemente valiosas para transmitir información. Nosotros vamos a centrarnos en hacer gráficos con ggplot, que forma parte de tidyverse.

Esta es la Cheat Sheet de RStudio con la ayuda para la visualización de datos con ggplot2.

Un gráfico realizado con ggplot2 presenta, al menos, tres elementos:

  • Datos (Data) que queremos representar (que serán un data frame).
  • Características estéticas (aesthetic mappings) que describen cómo queremos que los datos se vean en el gráfico. Para más información podemos consultar la vignette (vignette(“ggplot2-specs”)). Como luego veremos, se introducen con la función aes() y se refieren a:
    • posición (en los ejes)
    • color exterior (color) y de relleno (fill)
    • forma de puntos (shape)
    • tipo de línea (linetype)
    • tamaño (size)
  • Objetos geométricos (Geom) representan lo que vemos en un gráficos (puntos, líneas, etc.). Todo gráfico tiene, como mínimo, una geometría. La geometría determina el tipo de gráfico:
    • geom_point (para puntos)
    • geom_lines (para lineas)
    • geom_histogram (para histograma)
    • geom_boxplot (para boxplot)
    • geom_bar (para barras)
    • geom_smooth (líneas suavizadas)
    • geom_polygons (para polígonos en un mapa)
    • etc. (si ejecutáis el comando help.search(“geom_”, package = “ggplot2”) podéis ver el listado de objetos geométricos)

Gráficamente, la idea es:


Por tanto, para construir un gráfico con ggplot2 comenzamos con la siguiente estructura de código:

ggplot(datos, aes()) + geom_tipo()


A partir de esta estructura básica puede mejorarse la presentación de los gráficos introduciendo, por ejemplo, características estéticas en los objetos geométricos, rotulando los gráficos, etc.

Otros elementos que conviene tener presente en un gráfico de ggplot2 son:

  • Stat (Stat), transformaciones estadísticas para, generalmente, resumir datos (por ejemplo: contar frecuencias, número de intervalos en los histogramas, etc.).
  • Escalas (Scale). Las escalas, por ejemplo, convierten datos en características estéticas (colores, etc.), crean leyendas…
  • Coordenadas (coord): sistema de coordenadas cartesianas, polares, proyecciones, etc.
  • Faceting (Faceting), permite representar gráficos separados para subconjuntos de los datos originales.

Vamos a realizar algunos gráficos con ggplot. Si no lo hemos hecho ya, cargamos la librería ggplot2 o alternativamente tidyverse.

library(tidyverse)

3. DATOS UTILIZADOS EN EL TUTORIAL

En los ejemplos que siguen tratamos de ir introduciendo poco a poco distintos elementos y argumentos para mejorar la apariencia de los gráficos. Para realizar los gráficos de este módulo vamos a utilizar datos salarios_usa_1985.xlsx. Más información aquí.
Los datos se encuentran en la siguiente dirección:
https://www.uv.es/vcoll/curso_R_sfp/salarios_usa_1985.xlsx

Cargmos los datos y los asignamos al objeto salarios. Puedes realizar esta operación de diversas formas. Yo he optado por leer los datos directamente utilizando el paquete rio.

library(rio)
salarios <- import("https://www.uv.es/vcoll/curso_R_sfp/salarios_usa_1985.xlsx")

¿Cuántas observaciones hay? ¿Qué variables contiene la base de datos salarios?

dim(salarios)
## [1] 534  11
names(salarios)
##  [1] "wage"       "education"  "experience" "age"        "ethnicity" 
##  [6] "region"     "gender"     "occupation" "sector"     "union"     
## [11] "married"

Así pues, tenemos un total de 554 observaciones y las siguientes 11 variables:

  • Wage: Wage (dollars per hour).
  • Education: Number of years of education.
  • Experience: Number of years of work experience.
  • Age: Age (years).
  • Ethnicity: Race (1=Other, 2=Hispanic, 3=White).
  • Region: Indicator variable for Southern Region (1=Person lives in South, 0=Person lives elsewhere).
  • Gender: Indicator variable for sex (1=Female, 0=Male).
  • Occupation: Occupational category (1=Management, 2=Sales, 3=Clerical,4 =Service, 5=Professional, 6=Other).
  • Sector: Sector (0=Other, 1=Manufacturing, 2=Construction).
  • Union: Indicator variable for union membership (1=Union member, 0=Not union member).
  • Married: Marital Status (0=Unmarried, 1=Married)

Antes de comenzar con los gráficos, vamos a obtener un resumen de las variables.

summary(salarios)
##       wage          education       experience         age       
##  Min.   : 1.000   Min.   : 2.00   Min.   : 0.00   Min.   :18.00  
##  1st Qu.: 5.250   1st Qu.:12.00   1st Qu.: 8.00   1st Qu.:28.00  
##  Median : 7.780   Median :12.00   Median :15.00   Median :35.00  
##  Mean   : 9.024   Mean   :13.02   Mean   :17.82   Mean   :36.83  
##  3rd Qu.:11.250   3rd Qu.:15.00   3rd Qu.:26.00   3rd Qu.:44.00  
##  Max.   :44.500   Max.   :18.00   Max.   :55.00   Max.   :64.00  
##   ethnicity            region             gender           occupation       
##  Length:534         Length:534         Length:534         Length:534        
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##     sector             union             married         
##  Length:534         Length:534         Length:534        
##  Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character  
##                                                          
##                                                          
## 

Recordad que en el caso de variables cuantitativas, la función summary() proporciona como resumen de la variable los “cinco números” (mínimo, cuartil 1, cuartil 2, cuartil 3, máximo) y la media. Si la variable es categórica (factor) proporciona el total de observaciones en cada categoría.

Bien. Ya tenemos una idea de los datos. Ahora…a representarlos!!!!


4. HISTOGRAMA

Vamos a comenzar haciendo un histograma muy sencillo del Salario (wage). Para ello, recordemos que la instrucción comienza con la función ggplot(), en la que incluimos los datos y la estética con la que queremos que se presenten en el gráfico. Seguidamente le añadimos (+) la geometría (tipo histograma) con la función geom_histogram().

Muy importante: con ggplot2 añadimos capas (layers) con el símbolo +.

El histograma del Salario será:

ggplot(salarios, aes(x = wage)) + 
  geom_histogram()

Dos cosas a considerar del histograma anterior:

  1. Por defecto, el número de intervalos es de 30. Es posible establecer el número de intervalos (bins), la amplitud del intervalo (binwidth) o fijar los puntos de corte de los intervalos (breaks).
  2. El eje Y corresponde al número de observaciones (frecuencias absolutas). Si estamos interesados en representar un histogramas de forma que el área del mismo sume 1, entonces tenemos que cambiar la estética de la siguiente forma:
ggplot(salarios, aes(x = wage)) + 
  geom_histogram(aes(y = ..density..))

A partir de esta estructura básica, podemos ir añadiendo elementos para mejorar la presentación. Observad la diferencia entre los siguientes dos histogramas.

# Histograma con 20 intervalos
ggplot(salarios, aes(x = wage)) + 
  geom_histogram(bins = 20, color = "white", fill = "blue")

# Histograma con amplitud de intervalo igual a 5. Línea discontinua para el contorno del intervalo
ggplot(salarios, aes(x = wage)) + 
  geom_histogram(binwidth = 5, color = "white", fill = "blue", linetype = 2)

Ahora vamos a insertar un título al gráfico y también rotularemos los ejes. Para el título podemos utilizar la función ggtitle() y para los ejes las funciones xlab() y ylab(). Si, por ejemplo, quisiéramos omitir la etiqueta del eje Y: ylab(NULL). No obstante, suele ser más habitual introducir esta contenido tal y como se muestra en el siguiente gráfico.

También podemos cambiar los límites de los ejes, para ello se emplean las funciones xlim() y ylim().

ggplot(salarios, aes( x = wage)) + 
  geom_histogram(bins = 20, color = "white", fill = "blue") +
  labs(title = "Distribución del salario (dólares por hora)",
       x = "Salario",
       y = NULL) +
  ylim(c(0,150))

Para tratar de observar las diferencias en la distribución del salario según distintos grupo, por ejemplo, el género podemos:

  1. Visualizar cada subconjunto de datos (salario para hombres y salario para mujeres) en distintos paneles. Para ello, utilizamos el elemento facet.
    Separar en facets es muy útil
ggplot(salarios, aes(x = wage)) + 
  geom_histogram(aes(y = ..density..), bins = 20, color = "white", fill = "blue") +
  facet_wrap( ~ gender)   # separa en facets un gráfico según una sola variable


ggplot(salarios, aes(x = wage)) + 
  geom_histogram(aes(y = ..density..), bins = 20, color = "white", fill = "blue") +
  facet_grid(gender ~ ethnicity) # separa en facets un gráfico según combinaciones de 2 variables


ggplot(salarios, aes(x = wage)) + 
  geom_histogram(aes(y = ..density..), bins = 20, color = "white", fill = "blue") +
  facet_grid(gender ~ . ) # si no queremos separar en facets filas o columnas se puede reemplazar por un . el nombre de alguna de las variables
  1. Hacer el histograma pero usando la variable género para colorear y observar las diferencias entre mujeres y hombres (Nota: no son las mejores opciones).


Para concluir con los histogramas, vamos a añadir dos líneas para situar la media y la mediana.

ggplot(salarios, aes(x = wage)) + 
  geom_histogram(aes(fill = ..count..), bins = 20, color = "white") +
  geom_vline(aes(xintercept = mean(wage)), color = "red") +
  geom_vline(aes(xintercept = median(wage)), color = "darkgreen") +
  labs(title = "Distribución del salario (dólares por hora)",
       x = "Salario",
       y = "Número de empleados") +
  scale_fill_continuous(name = "Empleados") + # probar: trans = 'reverse'

  theme_classic() 

y ahora la curva normal.

ggplot(salarios, aes(x = wage)) + 
  geom_histogram(aes(y = ..density..), bins = 20, color = "white", fill = "grey") + 
  stat_function(fun = dnorm, colour = "red",
                args = list(mean = mean(salarios$wage, na.rm = TRUE),
                             sd = sd(salarios$wage, na.rm = TRUE))) +
  geom_density(color = "blue") # alternativa: geom_line(stat = "density", color = "blue")

Observación: Podemos cambiar los límites de los ejes con: xlim(), ylim(), expand_limits(), scale_x_continuous() y scale_y_continuous(). Estas dos últimas opciones tienen como argumentos: name (etiquetas de eje), breaks (controlar marcas en el eje), labels (etiquetas de las marcas de los ejes), limits (límites del eje) y trans (para transformar los ejes).

Podéis consultar las siguientes dos páginas web para aprender más sobre cómo crear histogramas con ggplot2:

5. POLÍGONOS DE FRECUENCIAS

Los polígonos de frecuencia se obtienen la unir los puntos medios de los lados superiores de los rectángulos de un histograma.

 ggplot(salarios, aes(x = wage)) +
  geom_freqpoly(bins = 20, aes(y =..density.., color = gender)) +
  labs(color = "Género")

Ahora el histograma junto con el polígono de frecuencias.

ggplot(salarios, aes(x = wage, y = ..density..)) +
  geom_histogram(fill="blue") +
  geom_freqpoly(color="red") +
  theme_classic()

y el polígono de frecuencias acumulado:

ggplot(salarios,aes(wage)) +
  #geom_histogram(aes(y=cumsum(..count..)),bins=20, fill = "white") +
  stat_bin(aes(y=cumsum(..count..)),bins = 20, geom="line",color="red") +
  labs(title="Polígono de frecuencias acumulado",
       x = "Salario (dólares por hora)",
       y = "Frecuencia absoluta acumulada")

6. HORA DE HABLAR DE…COLORES

En los gráficos realizados hemos utilizado poco los colores (azul, rojo y colores por defecto de ggplot2), pero los colores son otro universo paralelo a los gráficos.

Nombres de colores:

colors() # o colours()

y aquí algunas paletas muy utilizadas (de rcolorbrewer):

Paleta del paquete: RColorBrewer

Paleta del paquete: RColorBrewer

Otra paleta que me gusta, por sencilla, es:

Paleta en Cookbook for R

Paleta en Cookbook for R


# este código es de R cookbook
# The palette with grey:
cbPalette <- c("#999999", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")

# The palette with black:
cbbPalette <- c("#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")

# To use for fills, add
  scale_fill_manual(values = cbPalette)

# To use for line and point colors, add
  scale_colour_manual(values = cbPalette)

Es frecuente utilizar colores con el código hexadecimal (como en el gráfico anterior):


Aquí una cheat sheet de colores que me gusta mucho.

Estos dos recursos os pueden ser de mucha utilidad para combinar y seleccionar colores para vuestros gráficos:


Ahora, ha llegado el momento de que practiquéis lo visto en este apartado del tutorial. A partir del dataset tips

  1. Tarea: Representad la distribución del gasto en restauración mediante un histograma completo.
  2. Tarea: Comparad la distribución del gasto en restauración según se trate de comida o cena.


7. DIAGRAMA DE DISPERSIÓN

Ya hemos visto que el salario presenta una gran dispersión y su distribución es asimétrica. Sin embargo, si transformamos el salario y trabajamos con el logaritmo del salario, tanto la dispersión como la asimetría se reducen. Cuando trabajamos con variables monetarias esta situación suele ser frecuente y, por tanto, normalmente se trabaja con logaritmos.

¿Cómo es la relación entre el salario (en logaritmo) y la experiencia?

Hacemos el diagrama de dispersión para ver la posible relación entre ambas variables.

ggplot(salarios, aes(experience, log(wage))) +
  geom_point() +
  labs(title = "Diagrama de dispersión",
       subtitle = "ScatterPlot",
       caption = "Fuente: paquete AER",
       x = "Experiencia (en años)",
       y = "Salario (en logaritmo)")

Si queremos que los puntos del diagrama de dispersión tengan color tenemos varias alternativas. Observar las diferencias entre los siguientes tres gráficos.


# Opción 1
ggplot(salarios, aes(experience, log(wage), color = "red")) +
  geom_point()


# Opción 2
ggplot(salarios, aes(experience, log(wage))) +
  geom_point(aes(color = "red"))


# Opción 3
ggplot(salarios, aes(experience, log(wage))) +
  geom_point(color = "red")

Las dos primeras opciones -en las que color se ha introducido como una característica estética (con aes())- tienen un efecto similar (no exactamente igual, más tarde lo veremos): se crea una variable color que toma el valor red y automáticamente se muestra una leyenda. En la tercera opción el color se introduce como una constante.

Si tenemos muchos datos es muy probable que se produzca un importante solapamiento de las observaciones. Si “muchos datos” no son “muchísimos”, podemos utilizar jitter para introducir una pequeña perturbación en las observaciones y mejorar la visualización de los datos con el diagrama de dispersión.

ggplot(salarios, aes(experience, log(wage))) +
  geom_jitter(width =.2, alpha = 0.5) +
  labs(title = "Diagrama de dispersión",
       subtitle = "ScatterPlot",
       caption = "Fuente: paquete AER",
       x = "Experiencia (en años)",
       y = "Salario (en logaritmo)")

Ahora, en el diagrama de dispersión vamos a colorear los puntos según el género. Observar las diferencias entres los siguientes tres diagramas de dispersión. ¿Alguna diferencia entre los siguientes dos gráficos?

# Opción 1
ggplot(salarios, aes(experience, log(wage), color = gender)) +
  geom_point() +
  labs(title = "Diagrama de dispersión",
       subtitle = "ScatterPlot",
       caption = "Fuente: paquete AER",
       x = "Experiencia (en años)",
       y = "Salario (en logaritmo)") +
  scale_color_discrete(name = "Género", labels =  c("Mujer","Hombre"))

# Opción 2
ggplot(salarios, aes(experience, log(wage))) +
  geom_point(aes(color = gender)) +
    labs(title = "Diagrama de dispersión",
       subtitle = "ScatterPlot",
       caption = "Fuente: paquete AER",
       x = "Experiencia (en años)",
       y = "Salario (en logaritmo)") +
  scale_color_discrete("Género", labels =  c("Mujer","Hombre"))

Como comentamos anteriormente, aparentemente no hay diferencias entre los dos gráficos, pero fijémonos qué sucede si añadimos la recta de regresión. Esto último lo vamos a hacer añadiendo la geometría geom_smooth() y especificando como método de estimación lm (linear model).

# Opción 1
ggplot(salarios, aes(experience, log(wage), color = gender)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE) +  
  labs(title = "Diagrama de dispersión",
       subtitle = "ScatterPlot",
       caption = "Fuente: paquete AER",
       x = "Experiencia (en años)",
       y = "Salario (en logaritmo)") +
  scale_color_discrete("Género", labels =  c("Mujer","Hombre"))

# Opción 2
ggplot(salarios, aes(experience, log(wage))) +
  geom_point(aes(color = gender)) +
  geom_smooth(method = "lm") +
  labs(title = "Diagrama de dispersión",
       subtitle = "ScatterPlot",
       caption = "Fuente: paquete AER",
       x = "Experiencia (en años)",
       y = "Salario (en logaritmo)") +
  scale_color_discrete("Género", labels =  c("Mujer","Hombre"))

Ya para concluir:

ggplot(salarios, aes(experience, log(wage), shape = gender, color = gender)) +
  geom_point() +
  geom_smooth(method = "lm",
              se = FALSE,
              fullrange = TRUE) +  # extiende las rectas
  labs(title = "Diagrama de dispersión",
       subtitle = "ScatterPlot",
       caption = "Fuente: paquete AER",
       x = "Experiencia (en años)",
       y = "Salario (en logaritmo)") +
  scale_shape_manual(values = c(2, 4), name = "Género", labels =  c("Mujer","Hombre")) + 
  scale_colour_manual(values = c("chartreuse4", "chocolate"), name = "Género", labels =  c("Mujer","Hombre"))

Símbolos para las formas:

Para más detalles prácticos sobre los diagramas de dispersión podéis consultar, por ejemplo, ggplot2 scatter plots : Quick start guide - R software and data visualization o Scatterplots (ggplot2) en Cookbook for R.

Es el momento de realizar una nueva tarea…

Tarea: A partir del dataset tips, realizar un diagrama de dispersión. Incluid en el gráfico colores según una determinada variable categórica, rectas de regresión, cambiar la posición de la leyenda y cualquier otro detalle que consideréis oportuno.


8. GRÁFICO DE LÍNEAS

Le toca el turno a los gráficos de líneas. La geometría para realizar un gráfico de líneas es geom_line. Para representar un típico gráfico de líneas vamos a analizar la evolución de una magnitud en el tiempo. Para ello, he descargado de Infomercados datos históricos de cotización del IBEX. Los datos se encuentran en el fichero cotizacion_ibex.txten la carpeta datos.

cotizacion <- read.table("./datos/cotizacion_ibex.txt",sep = "",dec = ",",skip = 1)
names(cotizacion) <- c("Ticker","Fecha","apertura","maximo","minimo","ultimo","volumen")

Antes de hacer el gráfico, vamos a incluir una nueva variable (Time) con formato fecha al dataframe cotizacion (observad que la variable fecha es un factor). Para hacer esto utilizamos el package lubridate

library(lubridate)
cotizacion$Time <- dmy(cotizacion$Fecha)

Dibujamos el gráfico de líneas con la geometría: geom_line().

ggplot(cotizacion, aes(Time, apertura) ) +
  geom_line(color = "blue")

¿Algo raro? ¿A qué puede deberse? ¿Cómo podemos arreglar esto?

cotizacion2 <- cotizacion %>% mutate(apertura = case_when(apertura == 0  ~ ultimo,
                                                          apertura != 0 ~ apertura))
ggplot(cotizacion2, aes(Time, apertura) ) +
  geom_line(color = "blue")

Como los gráficos de líneas son un poco “sosos”, vamos a darle un poco de “vidilla”..

#install.packages("gganimate")
library(gganimate)

#install.packages("gifski")
library(gifski)


p <- ggplot(cotizacion2, aes(Time, apertura) ) +
  geom_line(color = "blue", size = 1) +
  geom_point(color="red", size = 3) +
  theme_minimal() +
  transition_reveal(Time)

animate(p, renderer = gifski_renderer(), fps = 5)

9. DIAGRAMA DE CAJA Y BIGOTES

Para realizar un diagrama de caja y bigotes se utiliza la geometría: geom_boxplot().

ggplot(salarios, aes(x = gender, y = wage)) +
  geom_boxplot()

Podemos colorear las cajas del gráfico incluyendo en las características estéticas la variable que utilizaremos para el relleno. Automáticamente se crea una leyenda para facilitar la lectura del gráfico, que podemos cambiar de posición o eliminar si se considera que no aporta información relevante.


# Diagrama de caja con color de relleno
ggplot(salarios, aes(x = gender, y = wage, fill = gender)) +
  geom_boxplot() +
  labs(x = "Género", y = "Salario", fill = "Género") +   # titulo ejes y leyenda
  scale_x_discrete(labels =  c("Mujer","Hombre")) +   # etiquetas del eje x
  scale_fill_discrete(labels =  c("Mujer","Hombre"))  # etiquetas claves leyenda


# Diagrama de caja con cambio de posición de la leyenda
ggplot(salarios, aes(x = gender, y = wage, fill = gender)) +
  geom_boxplot() +
  labs(x = "Género", y = "Salario", fill = "Género") +  
  scale_x_discrete(labels =  c("Mujer","Hombre")) +
  scale_fill_discrete(labels =  c("Mujer","Hombre")) + 
  theme(legend.position = "top")   # cambio posición de leyenda


# Diagrama de caja sin leyenda por considerar redundante la información
ggplot(salarios, aes(x = gender, y = wage, fill = gender)) +
  geom_boxplot(outlier.colour = "blue") +             # color de los outliers
  labs(x = "Género", y = "Salario") +  
  scale_x_discrete(labels =  c("Mujer","Hombre")) +
  guides(fill = FALSE)                                  # eliminamos la leyenda

Para cambiar la orientación de las cajas y que en lugar de representarse verticalmente se representen horizontalmente se utiliza la función coord_flip(). Esta forma de presentar los diagramas de caja es muy frecuente porque facilita la interpretación de los mismos al situar la variable cuantitativa en el eje de abscisas.

  ggplot(salarios, aes(x = gender, y = wage, fill = gender)) +
  geom_boxplot(outlier.colour = "blue",
               outlier.shape = 4,
               outlier.size = 3,
               notch = TRUE) +    # mostrará un intervalo de confianza alrededor de la mediana        
  labs(x = "Género", y = "Salario") +  
  scale_x_discrete(labels =  c("Mujer","Hombre")) +
  guides(fill = FALSE) +
  coord_flip()                       # cambio dirección de las cajas

Puede resultar interesante localizar la media de cada grupo en los diagramas de caja. Para ello, hacemos uso del elemento stat (transformación estadística). Se puede añadir stat, básicamente, de dos formas:

    1. añadir directamente una función stat_() y de esa forma anular su valor por defecto en la geometría.
    1. añadir una función geom_() e introducir el elemento stat para anular el valor por defecto.

Veamos las dos formas comentadas de añadir .


# Añadimos la media utilizando la función stat_()
ggplot(salarios, aes(x = gender, y = wage, fill = gender)) +
  geom_boxplot() +
  labs(x = "Género", y = "Salario") +  
  scale_x_discrete(labels =  c("Mujer","Hombre")) +
  guides(fill = FALSE) +
  coord_flip() +                  
  stat_summary(fun.y = mean, geom = "point", shape = 5, size = 4)


# Añadimos la media utilizando la función geom_() y modificando el elemento stat
ggplot(salarios, aes(x = gender, y = wage, fill = gender) ) +
  geom_boxplot(alpha = 0.3, outlier.colour = "blue") +
  labs(x = "Género", y = "Salario") +  
  scale_x_discrete(labels =  c("Mujer","Hombre")) +
  guides(fill = FALSE) +
  coord_flip() +              
  geom_point(stat =  "summary", fun.y = mean, shape = 16, size = 2, color = "red")

Por último, si queremos hacernos una idea de cómo se distribuyen nuestras observaciones/individuos respecto del diagrama de caja podemos hacer uso de la función position_jitter(). Cuando hay muchos datos las observaciones se superponen. La función position_jitter() lo que hace es añadir un pequeño “ruido” en cada posición. También podemos utilizar la geometría geom_jitter(). A continuación se muestran los diagramas de caja con estas dos opciones.

ggplot(salarios, aes(x = gender, y = wage, fill = gender) ) +
  geom_boxplot(alpha = 0.3, outlier.colour = "blue") +
  labs(x = "Género", y = "Salario") +
  scale_x_discrete(labels =  c("Mujer","Hombre")) +
  guides(fill = FALSE) +
  coord_flip() +              
  geom_point(stat =  "summary", fun.y = mean, shape = 16, size = 4, color = "red") +
  geom_point(position = position_jitter(width = 0.1), alpha = 0.2) 



ggplot(salarios, aes(x = gender, y = wage, fill = gender)) +
  geom_boxplot(color = "blue", alpha = 0.3, outlier.colour = "blue") +
  labs(x = "Género", y = "Salario") +
  scale_x_discrete(labels =  c("Mujer","Hombre")) +
  guides(fill = FALSE) +
  coord_flip() +              
  geom_point(stat =  "summary", fun.y = mean, shape = 16, size = 4, color = "red") +
  geom_jitter(width = 0.1, alpha = 0.2)



ggplot(salarios, aes(x = gender, y = wage, color =  gender, fill = gender)) +
  geom_boxplot(outlier.colour = "blue") +
  labs(x = "Género", y = "Salario") +
  scale_x_discrete(labels =  c("Mujer","Hombre")) +
  scale_fill_brewer(palette = "Dark2") +
  scale_color_manual(values = c("green","chocolate4")) +
  guides(fill = FALSE) +
  coord_flip() +              
  geom_point(stat =  "summary", fun.y = mean, shape = 16, size = 4, color = "yellow") +
  geom_jitter(width = 0.1, alpha = 0.2)

Ahora, un diagrama de caja con dos grupos: género y casado (es una variable dicotómica):


ggplot(salarios, aes(x = gender, y = wage, fill = married) ) +
  geom_boxplot(alpha = 0.3, outlier.colour = "blue") + # con position=position_dodge(.5) cambiamos la posición
  labs(x = "Género", y = "Salario") +
  scale_x_discrete(labels = c("Mujer","Hombre")) +
  geom_point(aes(group = married), stat = "summary", fun.y = mean, shape =16, size = 4, color = "yellow",
             position = position_dodge(.8)) +
  coord_flip()

Por último, podemos representar un gráfico de violín. Estos gráficos son similares a los diagrama de caja pero tienen la particularidad de mostrar la densidad de probabilidad de los datos. Todo lo visto en los diagramas de caja es aplicable a los diagramas de violín, solo que ahora la geometría que necesitamos es geom_violin.

ggplot(salarios, aes(x = gender, y = wage, fill = married)) +
  geom_violin(alpha = 0.3) + # con position=position_dodge(.5) cambiamos la posición
  geom_point(aes(group = married), stat = "summary", fun.y = mean, shape =16, size = 4, color = "yellow",
             position = position_dodge(.8)) +
  stat_summary(fun.y = median, geom = "point", size = 2, color = "red", position = position_dodge(.8)) +
  labs(x = "Género", y = "Salario") +
  scale_x_discrete(labels =  c("Mujer","Hombre")) +
  scale_fill_discrete(name = "Estado civil", labels = c("yes" = "Casado/a", "no" = "No casado/a")) +
  coord_flip()

Para practicar lo aprendido proponemos la siguiente tarea:

Tarea: A partir del dataset tips, representad el diagrama de caja de la factura total del restaurante (total_bill) en función del día de la semana (day) y el género que quien paga la factura (sex) . Incorporad al gráfico toda la información que se considere oportuna.

10. DIAGRAMA DE BARRAS

El diagrama de barras, como ya sabemos, puede utilizarse para representar variables categóricas (atributos u ordinales) y variables cuantitativas discretas.

ggplot(salarios, aes(ethnicity)) +
  geom_bar()

ggplot(salarios, aes(education)) +
  geom_bar()

Como siempre, lo primero sería dar título al gráfico y a los ejes.

ggplot(salarios, aes(ethnicity)) +
  geom_bar() +
  labs(title = "Diagrama de barras",
       x ="Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros"))

ggplot(salarios, aes(education)) +
  geom_bar() +
    labs(title = "Diagrama de barras",
         x = "Educación (en años)",
         y = "Empleados")

Coloreamos cada barra con un color distinto y eliminamos leyenda porque no resulta informativa:

ggplot(salarios, aes(ethnicity, fill = ethnicity)) +
  geom_bar() +
  labs(title = "Diagrama de barras",
       x = "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +  
  guides(fill = FALSE)     # eliminamos la leyendoa porque no aporta

Incorporamos información sobre el número de casos en las barras correspondientes a cada categoría y eliminamos el eje Y.

ggplot(salarios, aes(ethnicity, fill = ethnicity)) +
  geom_bar() +
  labs(title = "Diagrama de barras",
       x = "Raza",
       y = NULL) +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +  
  guides(fill = FALSE) +
  geom_text(stat = 'count',aes(label = ..count..), vjust = -0.5, size = 3) 

Ahora estamos interesados en ver cómo se distribuye el género según la raza, podemos realizar un gráfico de barras apilado.

ggplot(salarios, aes(ethnicity, fill = gender)) +
  geom_bar() +
  labs(title = "Diagrama de barras",
       x = "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +
  scale_fill_discrete("Género", labels =  c("Mujer","Hombre"))

Si queremos otros colores los podemos introducir manualmente o utilizando una paleta:

ggplot(salarios, aes(ethnicity, fill = gender)) +
  geom_bar() +
  labs(title = "Diagrama de barras",
       x =  "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +
  scale_fill_manual(values = c("#E69F00", "#D55E00"), 
                    "Género",
                    labels = c("Mujer","Hombre"))


# Ejemplo 1. Paleta de colores
ggplot(salarios, aes(ethnicity, fill = gender)) +
  geom_bar() +
  labs(title = "Diagrama de barras",
       x =  "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +
  scale_fill_manual(values = cm.colors(2), # probad con: heat.colors o rainbow (estas 3 son paletas de colores de R base) 
                    "Género",
                    labels =  c("Mujer","Hombre"))


# Ejemplo 2. Paleta de colores
ggplot(salarios, aes(ethnicity, fill = gender)) +
  geom_bar() +
  labs(title = "Diagrama de barras",
       x =  "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +
  scale_fill_brewer(palette = "Accent",
                    "Género",
                    labels =  c("Mujer","Hombre"))

Como hicimos antes, incluimos el número de casos como parte de la información que proporciona el diagrama de barras.

ggplot(salarios, aes(ethnicity, fill = gender)) +
  geom_bar() +
  labs(title = "Diagrama de barras",
       x =  "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +
  scale_fill_brewer(palette = "Accent",
                    "Género",
                    labels = c("Mujer","Hombre")) +
  geom_text(stat = 'count',aes(label = ..count..),
            position = "stack",
            vjust = 1,
            size = 2,
            color = "red")

El mismo diagrama de barras apilado pero ahora en porcentajes:

ggplot(salarios, aes(ethnicity,y = (..count..)/sum(..count..), fill = gender)) +
  geom_bar() +
  labs(title = "Diagrama de barras",
       x =  "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +
  scale_fill_brewer(palette = "Accent",
                    "Género",
                    labels =  c("Mujer","Hombre")) +
  geom_text(stat = "count", 
            aes(label = paste(round((..count..)/sum(..count..)*100), "%")),
            position = "stack",
            vjust = 1,
            size = 2,
            color = "red")

Y para ver la proporción de hombres y mujeres en cada categoría hacemos uso del parámetro position = fill dentro de la geometría.

ggplot(salarios, aes(ethnicity, fill = gender)) +
  geom_bar(position = "fill") +
  labs(title = "Diagrama de barras",
       x =  "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +
  scale_fill_brewer(palette = "Accent",
                    "Género",
                    labels = c("Mujer","Hombre")) 

Como hemos podido comprobar hasta ahora, cuando hacemos un diagrama de barras en el que consideramos más de una variable categórica, las barras aparecen apiladas (la opción por defecto es position = “stack”). Podemos hacer que las barras se sitúen unas junto a las otras con position = “dodge”.

ggplot(salarios, aes(ethnicity, fill = gender)) +
  geom_bar(position = "dodge") +            # también: position=position_dodge()
  labs(title = "Diagrama de barras",
       x =  "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +
  scale_fill_brewer(palette = "Accent",
                    "Género",
                    labels =  c("Mujer","Hombre"))

y, finalmente, podemos cambiar la orientación de las barras de vertical a horizontal.

ggplot(salarios, aes(ethnicity, fill = gender)) +
  geom_bar(position = "dodge") +            # también: position=position_dodge()
  labs(title = "Diagrama de barras",
       x =  "Raza",
       y = "Empleados") +
  scale_x_discrete(labels = c("Caucásico", "Hispano", "Otros")) +
  scale_fill_brewer(palette = "Accent",
                    "Género",
                    labels =  c("Mujer","Hombre")) +
  coord_flip()

En R para muy principiantes encontraréis una serie de cuatro video-blogs sobre cómo elaborar un diagrama de barras en ggplot2.


Para practicar lo aprendido proponemos la siguiente tarea:

Tarea: A partir del dataset tips, dibujad un diagrama de barras de la factura media del restaurante, según el día y el género del pagador, en el que se muestre las barras de error para el intervalo de confianza de la media al 95% de confianza.

11. GRÁFICO DE RADAR

Para dibujar un diagrama de Radar (o tela de araña) podemos utilizar los paquetes ggradar o fmsb. Para hacer el dibujo con estos paquetes tenemos que organizar previamente los datos, de forma que las categorías que representemos en los ejes aparezcan como columnas en un dataframe.

df <- salarios %>% select(gender,ethnicity,experience) %>%
  group_by(gender,ethnicity) %>%
  summarize(Media_años_experiencia = round(mean(experience)))

df2 <- df %>% pivot_wider(names_from = ethnicity, values_from= Media_años_experiencia)
colnames(df2) <- c("Género","Caucásico","Hispano","Otros")

#devtools::install_github("ricardo-bion/ggradar", dependencies = TRUE)
library(ggradar)

ggradar(df2,
        values.radar = c("0","10","30"),
        grid.min=0, grid.mid=15,grid.max=30,
        legend.position = "bottom")

# con ggplot2
ggplot(df, aes(x = ethnicity, y = Media_años_experiencia, group = gender, color= gender)) +
  geom_point() +
  geom_polygon(fill=NA) +
  ylim(10,30)+
  geom_hline(aes(yintercept=20), size = 1, lwd=1, lty=2) +
  scale_color_manual(values= c("darkred", "darkblue"),
                     name= "Género",
                     labels = c("Mujer","Hombre")) +
  coord_polar()

Aquí tenéis un tutorial sobre cómo realizar un gráfico Radar con el paquete fmsb.

12. GRÁFICO CON BARRAS DE ERROR

En el siguiente ejemplo se combina un gráfico de puntos (geom_point) con uno de líneas; pero antes, vamos a resumir los datos (no es necesario, es un ejemplo).

df <- salarios %>% group_by(gender, ethnicity) %>% 
  summarize(salario_medio = mean(wage)) 
df
## # A tibble: 6 x 3
## # Groups:   gender [2]
##   gender ethnicity salario_medio
##   <chr>  <chr>             <dbl>
## 1 female cauc               8.06
## 2 female hispanic           5.80
## 3 female other              7.49
## 4 male   cauc              10.3 
## 5 male   hispanic           8.66
## 6 male   other              8.46

Ahora, leed el código que aparece más abajo y e intentad dibujar a mano el gráfico que pensáis va a realizar.

ggplot(df, aes(x = ethnicity, y = salario_medio, group = gender, shape = gender, color = gender)) + 
  geom_line(aes(linetype = gender), size = 1) +     
  geom_point(size = 3, fill = "white") +       
  scale_colour_hue(name = "Género",
                   labels = c("Mujer","Hombre"),
                   l = 30)  +                 
  scale_shape_manual(name = "Género",
                     labels = c("Mujer","Hombre"),
                     values = c(22,21)) +      
  scale_linetype_discrete(name = "Género",
                          labels = c("Mujer","Hombre")) +
  scale_x_discrete(breaks = c("cauc","hispanic","other"),
                   labels = c("Caucásico", "Hispano", "Otros")) + 
  scale_y_continuous(breaks = seq(4.5, 10.5, 0.5), limits = c(4.5,10.5)) +
  labs(title = "Salario medio según raza y género",
       subtitle = "(gráfico de líneas)",
       x = "Raza del empleado",
       y = "Salario medio (dólares/hora)") + 
  theme_bw() +
  theme(legend.position = "right") + 
  theme(legend.position = c(0.8,0.2)) +   
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())

También podemos incluir los habituales intervalos de error al gráfico de líneas. Para ello hay que calcular los estadísticos necesarios.

df <- salarios %>% group_by(gender, ethnicity) %>% 
  summarize(salario_medio = mean(wage), 
            sd = round(sd(wage),4),
            se = round(sd/sqrt(n()),4),
            em = qt(0.975, n()-1, lower.tail = TRUE) * se)
df
## # A tibble: 6 x 6
## # Groups:   gender [2]
##   gender ethnicity salario_medio    sd    se    em
##   <chr>  <chr>             <dbl> <dbl> <dbl> <dbl>
## 1 female cauc               8.06  4.85 0.340 0.670
## 2 female hispanic           5.80  2.22 0.616 1.34 
## 3 female other              7.49  4.41 0.834 1.71 
## 4 male   cauc              10.3   5.33 0.347 0.683
## 5 male   hispanic           8.66  7.23 1.93  4.17 
## 6 male   other              8.46  3.83 0.613 1.24

y utilizando la geometría geom_errorbar incluir en nuestro gráfico las barras de error.


# intervalos: error típico de la media
ggplot(df, aes(x = ethnicity, y = salario_medio, color = gender, group = gender)) +
  geom_errorbar(aes(ymin = salario_medio - se, ymax = salario_medio + se), width = .1) +
  geom_line(aes(linetype = gender), size = 1) +     
  geom_point(size = 3, fill = "white")     



# intervalo de confianza (nc=95%) de la media
ggplot(df, aes(x = ethnicity, y = salario_medio, color = gender, group = gender)) + 
  geom_errorbar(aes(ymin = salario_medio - em, ymax = salario_medio + em), width = .1) +
  geom_line(aes(linetype = gender), size = 1) +     
  geom_point(size = 3, fill = "white")     

Como podemos ver, los intervalos de confianza de la media para hombres y mujeres se solapan. Introduciendo position = position_dodge(0.1) conseguimos desplazar las barras de error 0.5 a la derecha y a la izquierda (¡¡¡pero también hay que desplazar los puntos y las líneas!!!.

13. MISCELÁNEA

Quería cerrar este tutorial con algún gráfico más avanzado para que veáis las potencialidades gráficas en R. Antes de ver algún ejemplo más avanzado de gráfico, comentar que tenéis acceso a una gran cantidad de gráficos, con su correspodiente código en R, en estos dos sitios:

También hay una selección de extensiones de ggplot2 aquí

13.1. GRÁFICO DE BARRAS (ANIMADO)

# library(gganimate)
# library(gifski)
# cargamos los datos de gapminder
df <- import("https://www.uv.es/vcoll/curso_R_sfp/gapminder.xlsx")

df2 <- df %>%
  group_by(year) %>%
  arrange(year, desc(gdpPercap)) %>%
  mutate(ranking = row_number()) %>%
  filter(ranking <=10)

p <-  df2 %>%
  ggplot() +
  geom_col(aes(ranking, gdpPercap, fill = country)) +
  geom_text(aes(ranking, gdpPercap, label = format(round(gdpPercap))), hjust=-0.1) +
  geom_text(aes(ranking, y=0 , label = country), hjust=1.1) +
  geom_text(aes(x=15, y=max(gdpPercap) , label = as.factor(year)), vjust = 0.2, alpha = 0.5,  col = "gray", size = 20) +
  coord_flip(clip = "off", expand = FALSE) + 
  scale_x_reverse() +
  labs(title = "Diagrama de barras ordenado: Evolución del Producto Interior Bruto per cápita") +
  theme_minimal() + 
  theme(
    panel.grid = element_blank(),
    legend.position = "none",
    axis.ticks.x = element_blank(),
    axis.text.x = element_blank(),
    axis.ticks.y = element_blank(),
    axis.title.y = element_blank(),
    axis.text.y = element_blank(),
    plot.margin = margin(1, 4, 1, 3, "cm")) +
  transition_states(year, state_length = 0, transition_length = 2) +
  enter_fade() +
  exit_fade() +
  ease_aes('quadratic-in-out')

animate(p, renderer = gifski_renderer(), width = 700, height = 432, duration=30)

13.2. MAPA EVOLUCIÓN COVID MUNICIPIOS VALENCIA

Vamos a dibujar un mapa para ver la incidéncia del COVID en la Comunitat. Los datos se refieren a finales de diciembre de 2020. Datos actualizados los podéis descargar del porta de datos abiertos de la Generalitat Valenciana (http://coronavirus.san.gva.es/es/estadisticas y https://dadesobertes.gva.es/va/dataset?tags=COVID-19).
El fichero con los límites municipales y los datos que vamos a representar están accesibles en Aula Virtual.

# Riesgo por Covid-19 según la IA en los municipios de la Comunitat Valenciana
# fuente datos: GVA

# rio no lee bien los acentos de las variables
# datos de diciembre 2020 - enero 2021
df <- read_csv("./datos/data-oB121.csv")

names(df) <- c("Municipi",
               "Casos_PCR",
               "Incidència_acumulada_PCR",
               "Casos_PCR14",
               "Incidència_acumulada_PCR14",
               "Defuncions",
               "Taxa_de_defunció",
               "Codes",
               "Habitantes",
               "Codigo")

df$Codes <- as.character(df$Codes)

############
# obtenemos los límites municipales del Centro Nacional de Información Geográfica
# https://centrodedescargas.cnig.es/CentroDescargas/catalogo.do?Serie=NGMEN

library(sf)

file <- "./municipios/recintos_municipales_inspire_peninbal_etrs89.shp"
espanya_SF <- st_read(file, quiet = TRUE)

#inicio <- Sys.time()
#ggplot(espanya_SF) +
#  geom_sf()
#fin <- Sys.time()
#tiempo <- fin - inicio

# paciencia, tarda un poco en dibujar


# Juntamos los datos con la geometría
df <- left_join(df, espanya_SF, by = c("Codes" = "NATCODE"))

#hay que especificar la geometría (si primero uno españa y luego df1 no)
ggplot(df) +
  geom_sf(aes(geometry = geometry))

#### ALTERNATIVA
# Juntamos ESPAÑA Y DF, PERO AHORA TENEMOS QUE SELECCIONAR SOLO LA COMUNITAT

codigos_cv <- unique(df$Codigo)

# genero la variable codigo para que coincida con la de df (solo por practicar)

espanya_SF2 <- espanya_SF %>% 
  mutate(Codigo = as.character(substr(NATCODE, 7, 11))) %>% 
  filter(Codigo %in% codigos_cv)  
  

df2 <- left_join(espanya_SF2, df, by = "Codigo")

ggplot(df2) +
  geom_sf()

# NOS QUEDAMOS CON DF2
# YA PODEMOS PINTAR EN FUNCIÓN DE UNA VARAIBLE

p <- ggplot(df2) +
  geom_sf(aes(fill = Incidència_acumulada_PCR14)) +
  scale_fill_continuous(name= "Incidencia acumulada PRC+14", trans = "reverse") 
p

p2 <- ggplot(df2, aes(Municipi = Municipi)) +
  geom_sf(aes(fill = Incidència_acumulada_PCR14),color = "white") +
  scale_fill_distiller(name= "Incidencia acumulada PRC+14", palette= "YlOrRd", direction = 1) +
  theme_void()

p2

# Incorporamos algo de interactividad
#install.packages("plotly")
library(plotly)
ggplotly(p2,
         tooltip = c("Municipi","Incidència_acumulada_PCR14"))

14. Más referencias y sugerencias.

A lo largo del módulo se han facilitado algunas referencias de consulta. Aquí tenéis una selección.

Libros: