Una vez que sabes transformar los datos, es útil saber graficarlos.

Aunque hacer tablas tiene su lugar, en general, la manera más eficiente de extraer información relevante de nuestros datos es a través de gráficos.

Para esta sección utilizaremos el conjunto diamonds con el que ya estás familiarizado, sin embargo, las casi 54,000 observaciones que lo componen hace que generar gráficas las gráficas sea muy tardado, además de que algunas visualizaciones no serían muy claras., por lo tanto, trabajaremos con una submuestra, de aproximadamente 5% del dataset origal:

library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.3.1 --
## v ggplot2 3.3.3     v purrr   0.3.4
## v tibble  3.1.2     v dplyr   1.0.5
## v tidyr   1.1.3     v stringr 1.4.0
## v readr   1.4.0     v forcats 0.5.1
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
set.seed(987654)

diamonds_sample <-  diamonds %>% 
  group_by(cut, color, clarity) %>% 
  sample_frac(0.05) %>% 
  ungroup()

diamonds_sample
## # A tibble: 2,694 x 10
##    carat cut   color clarity depth table price     x     y     z
##    <dbl> <ord> <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
##  1  0.71 Fair  D     SI2      64.6    60  2198  5.62  5.59  3.62
##  2  0.7  Fair  D     SI2      67.8    58  1770  5.51  5.44  3.71
##  3  1    Fair  D     SI2      66.5    59  3965  6.24  6.21  4.14
##  4  1.52 Fair  D     SI1      64.6    64  9704  7.18  7.16  4.63
##  5  0.91 Fair  D     SI1      64.4    60  3855  6.08  6.04  3.9 
##  6  0.7  Fair  D     SI1      66.5    55  2339  5.51  5.47  3.65
##  7  0.71 Fair  D     VS2      56.9    65  2858  5.89  5.84  3.34
##  8  0.48 Fair  E     SI2      55.2    60   907  5.29  5.25  2.91
##  9  0.5  Fair  E     SI2      68.6    56   871  4.87  4.75  3.3 
## 10  1    Fair  E     SI2      55.4    62  3011  6.63  6.59  3.66
## # ... with 2,684 more rows

De esta manera, tenemos un número más manejable de datos.

Para este ejercio supondremos que diamonds_sample es el dataset original, pues sólo es relevante para ilustrar las funcionalidades de ggplot.

IMPORTANTE: Cuando se tenga un conjunto grande de datos, debes realizar el análisis con los datos COMPLETOS. NO se deben tomar submuestras arbritrarias como método de análisis!

ggplot

ggplot es un paquete, parte de tidyverse, que contiene una gran variedad de funciones útiles para realizar gráficos.

Un ejemplo muy sencillo de un gráfico es el siguiente:

diamonds_sample %>% 
  ggplot(aes(x = carat, y = price))+
  geom_point()

Este gráfico lo realizamos para ver la relación que existe entre el peso en kilates carat el precio en dólares price de cada diamante. En este caso, cada punto corresponde a un diamante.

Observando el código, suceden varias cosas. Analicemos una por una:

  • Partimos del dataset que contiene los datos a graficar.

  • Utilizando %>%, se inicializa el plot con la función ggplot()

  • Dentro de ggplot() se utiliza la función auxiliar aes(), abreviación de aesthetics, lo cual se explica más adelante.

  • Dentro de aes(), se especifican 2 argumentos, x y y. Aquí específicamos qué variable del dataset corresponderá a cada eje de la gráfica.

  • Utilizando el signo +, se especifica la siguiente capa de la gráfica. El uso del signo + al graficar con ggplot es análogo al uso del %>% al transformar datos con el resto de tidyverse. Sólo recuerda que una vez que utilices ggplot(), deberás usar + para agregar capas.

  • Se especifica el tipo de gráfico, en este caso, con geom_point() pues se está generando un gráfico de puntos.

  • Observa que los nombres de los ejes se agregaron autmáticamente, y corresponden a los nombres de las columnas. Observa también que se definió el rango de valores de cada variable, donde x o caratva de 0 a 3, y yo price va de 0 a 20000.

Estructura general

Todos los plots comparten una estructura general

Siempre se debe empezar con geom_plot(aes()). Es como se inicializa el ambiente gráfico.

Elementos básicos:

  • Aesthetics: Son las columnas que servirán para general el gráfico. Van dentro de aes()

    • x: Variable que corresponderá al eje horizontal del gráfico

    • y: (Opcional) Variable correspondiente al eje vertical

    • color: Variable que servirá para diferenciar, por medio del color, a distintos puntos o líneas.

    • fill: Variable que define el color con el que se rellenará. Util para barras o áreas.

  • Geoms: Funciones que especifican el tipo de gráfico que se utilizará. Algunos de los más comunes son:

    • geom_point: Gráfico de puntos (de dispersión).

    • geom_bar: Gráfico de barras.

    • geom_histogram: Histograma.

    • geom_line: Gráfico de líneas, x ej, series de tiempo

  • Theme: Elementos estéticos del gráfico como color de fondo, tipo, tamaño y fuente de letra de las etiquetas, etc.

Veamos algunos ejemplos para entender mejor todas las partes.

diamonds_sample %>% 
  ggplot(aes(carat, price))

En este caso, no se definió ningún geom_. Puedes pensar en las funciones ggplot(aes()) como si fuera establecer el lienzo sobre el cuál se dibujará.

Gráficos de dispersión

Cuando sea necesario graficar 2 variables numéricas, se utilizan gráficos de dispersión (o de puntos).

diamonds_sample %>% 
  ggplot(aes(carat, price))+
  geom_point()

Una vez que agregamos geom_point(), se dibujan los puntos. Cada punto corresponde a una observación, en este caso a un diamante.

Observamos que en general, mientras mayor es el peso (carat), mayor es el precio, sin embargo existe cierta variabilidad, donde diamantes relativamente pequeños pueden ser mucho más costosos que diamantes de mayor tamaño.

Podemos visualizar una variable más, en este caso categórica, por medio de la aesthetic color. (No confundir con la columna color de nuestro dataset). En este casó utilizaremos cut.

diamonds_sample %>% 
  ggplot(aes(carat, price, color = cut))+
  geom_point()

En este caso, podemos distinguir a qué nivel de cut pertenece cada diamante. De esta manera observamos 3 variables de nuestro dataset en un mismo gráfico.

Vemos que, en general, los diamantes pequeños pero costosos son de cut Ideal, Premium o Very Good. Mientras que los diamantes grandes pero baratos, son de cut Fair. Aún así existen otros puntos que no siguen este comportamiento.

Ejercicio: Realiza 2 gráficos similares (carat vs. price), pero ahora utilizando las columnas color y clarity en la aesthetic color (1 gráfico por cada columna) para ver su comportamiento.

¿Qué patrones observas?

Gráficos de barras

geom_bar() puede utilizarse de varias maneras:

Conteo de 1 variable categórica

El comportamiento por defecto geom_bar(), para hacer un gráfico de barras, es contar las observaciones para cada nivel que se especifique en la aesthetic x =. Por ejemplo:

diamonds_sample %>% 
  ggplot(aes(x = clarity))+
  geom_bar()

Observa cómo se define únicamente la variable x dentro de aes(). Por defecto, geom_bar() calcula el número de renglones en los que aparece cada nivel de la variable. Es muy importante tener este comportamiento presente, pues puede no ser lo esperado.

Conteo de 2 variables categóricas

diamonds_sample %>% 
  ggplot(aes(x = clarity, fill = cut))+
  geom_bar()

Cuando se realizan gráficos de barras, se debe utilizar la aesthetic fill = en lugar de color =, para colorear las barras de acuerdo una segunda variable. Notarás que, una vez más, se calcularon automáticamente las ns de cada nivel de cut y clarity.

Observa que las barras en este caso están apiladas. Podemos cambiar este comportamiento con el argumento position = dentro de geom_bar():

diamonds_sample %>% 
  ggplot(aes(x = clarity, fill = cut))+
  geom_bar(position = "dodge")

De esta forma, las barras están lado a lado en lugar de una encima de otra.

Porcentajes de 2 variables categóricas

Además de conteos, geom_bar puede calcular automáticamente porcentajes, por medio del argumento position = "fill".

diamonds_sample %>% 
  ggplot(aes(x = clarity, fill = cut))+
  geom_bar(position = "fill")

Observa que el eje vertical ahora tiene valores entre 0 y 1, ya que se calculó la proporción de cada tipo de cut, por cada nivel de clarity. Por ejemplo, de todos los diamantes de clarity I1, cerca de 20% son de cut Ideal, casi 30% son de cut Premium, etc.

Ejercicio: Repite las últimas 3 gráficas, pero ahora usando la columna color para la aesthetic fill y la columna cut para la aesthetic x.

Variable numérica y varíable categórica.

Que geom_bar() realice el conteo automáticamente es útil cuando se tiene un conjunto de datos en el que cada observación es un renglón. Sin embargo, este no siempre es el caso.

Es necesario, en ocasiones, cambiar el comportamiento por defecto, de manera que sea posible proveer un valor para cada barra.

Veamos el dataset mtcars

mtcars
## # A tibble: 32 x 11
##      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
##    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
##  1  21       6  160    110  3.9   2.62  16.5     0     1     4     4
##  2  21       6  160    110  3.9   2.88  17.0     0     1     4     4
##  3  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
##  4  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1
##  5  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2
##  6  18.1     6  225    105  2.76  3.46  20.2     1     0     3     1
##  7  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
##  8  24.4     4  147.    62  3.69  3.19  20       1     0     4     2
##  9  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
## 10  19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4
## # ... with 22 more rows

Dicho dataset, se compone de 11 mediciones de distintas características que puede tener un carro, para 32 automóvilies distintos.

Convertiremos el dataset a tibble, pues el formato actual es dataframe.

mtcars <- mtcars %>% 
  rownames_to_column("car") %>% 
  as_tibble()

mtcars
## # A tibble: 32 x 12
##    car           mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
##    <chr>       <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
##  1 Mazda RX4    21       6  160    110  3.9   2.62  16.5     0     1     4     4
##  2 Mazda RX4 ~  21       6  160    110  3.9   2.88  17.0     0     1     4     4
##  3 Datsun 710   22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
##  4 Hornet 4 D~  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1
##  5 Hornet Spo~  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2
##  6 Valiant      18.1     6  225    105  2.76  3.46  20.2     1     0     3     1
##  7 Duster 360   14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
##  8 Merc 240D    24.4     4  147.    62  3.69  3.19  20       1     0     4     2
##  9 Merc 230     22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
## 10 Merc 280     19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4
## # ... with 22 more rows

En este caso todas nuestras columnas, a excepción del nombre del carro, son numéricas. Queremos realizar un gráfico de barras para poder comparar las características de los carros cuyos valores ya están dados, es decir, no es necesario calcularlos.

Veamos el rendimiento de combustible mpg (miles per gallon):

mtcars %>% 
  ggplot(aes(x = mpg, y = car))+
  geom_bar(stat = "identity")

En este caso, utilizamos la variable numérica como la aesthetic x y los nombres de los carros como y. Para evitar que geom_bar() realice el conteo, fue necesario establecer el argumento stat = "identity".

Podemos utilizar fct_reorder() para ordenar la columna car según los valores de mpg.

mtcars %>% 
  mutate(car = fct_reorder(car, mpg)) %>% 
  ggplot(aes(x = mpg, y = car))+
  geom_bar(stat = "identity")

Ejercicio: Repite el gráfico anterior asignando car a x y mpg a y. ¿Qué observas?

Conteo de 1 variable numérica

Mejor conocido como histograma, sirve para dividir una variable en compartimentos y contar cúantas observaciones caen en cada uno de dichos compartimentos.

Se utiliza geom_histogram() y una sola variable dentro de aes()

Veamos el comportamiento de price de diamonds_sample:

diamonds_sample %>% 
  ggplot(aes(x = price))+
  geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Podemos elegir el número de compartimentos con el argumento bins. Probemos a usar muchos bins graficando el comportamiento de carat:

diamonds_sample %>% 
  ggplot(aes(x = carat))+
  geom_histogram(bins = 200)

Probemos ahora con un número pequeño

diamonds_sample %>% 
  ggplot(aes(x = carat))+
  geom_histogram(bins = 10)

Observa cómo parece modificarse el comportamiento de la variable, según el número de bins que se elija. La idea es encontrar un número que no cuente cada valor por separado, pero tampoco englobe todo en muy pocos compartimentos.

diamonds_sample %>% 
  ggplot(aes(x = carat))+
  geom_histogram(bins = 70)

Ejercicio:

  • Analiza el histograma anterior. ¿Observas algo interesante en los conteos? ¿En qué valores se observan picos? ¿A qué crees que se deba?

  • Realiza un histograma de la variable ppk, calculándola de la misma manera que en el Caso 1. Prueba con varios valores de bins y elige el que mejor ilustre el comportamiento de la variable.

Series de tiempo

El uso más comun de geom_line() es para graficar datos con información temporal.

Para estos ejemplos, utilizaremos el dataset economics del paquete ggplot2. Si cargaste tidyverse lo puedes usar directamente. Escribe ?economics en tu consola para más información sobre el dataset.

economics
## # A tibble: 574 x 6
##    date         pce    pop psavert uempmed unemploy
##    <date>     <dbl>  <dbl>   <dbl>   <dbl>    <dbl>
##  1 1967-07-01  507. 198712    12.6     4.5     2944
##  2 1967-08-01  510. 198911    12.6     4.7     2945
##  3 1967-09-01  516. 199113    11.9     4.6     2958
##  4 1967-10-01  512. 199311    12.9     4.9     3143
##  5 1967-11-01  517. 199498    12.8     4.7     3066
##  6 1967-12-01  525. 199657    11.8     4.8     3018
##  7 1968-01-01  531. 199808    11.7     5.1     2878
##  8 1968-02-01  534. 199920    12.3     4.5     3001
##  9 1968-03-01  544. 200056    11.7     4.1     2877
## 10 1968-04-01  544  200208    12.3     4.6     2709
## # ... with 564 more rows

Observa que tenemos 5 columnas numéricas y 1 columna que contiene la fecha, ya en formato <date>. Esto es importante, pues de otra manera no podría graficarse como serie de tiempo.

Veamos la gráfica del número de personas desempleadas, a lo largo del tiempo:

economics %>% 
  ggplot(aes(date, unemploy))+
  geom_line()

Basta con asignar date a la aesthetic x y unemploy a la aesthetic y.

Si queremos ver todas las variables numéricas graficadas al mismo tiempo, necesitamos transformar a formato long nuestros datos, tomando todas las columnas excepto la fecha:

economics %>% 
  pivot_longer(names_to = "var", values_to = "vl", cols = -date)
## # A tibble: 2,870 x 3
##    date       var            vl
##    <date>     <chr>       <dbl>
##  1 1967-07-01 pce         507. 
##  2 1967-07-01 pop      198712  
##  3 1967-07-01 psavert      12.6
##  4 1967-07-01 uempmed       4.5
##  5 1967-07-01 unemploy   2944  
##  6 1967-08-01 pce         510. 
##  7 1967-08-01 pop      198911  
##  8 1967-08-01 psavert      12.6
##  9 1967-08-01 uempmed       4.7
## 10 1967-08-01 unemploy   2945  
## # ... with 2,860 more rows

De esta forma, basta con agregar la aesthetic color a nuestro gráfico:

economics%>% 
  pivot_longer(names_to = "var", values_to = "vl", cols = -date) %>% 
  ggplot(aes(x = date, y = vl, color = var))+
  geom_line()

Observa que todas las líneas comparten la escala del eje vertical. Esto es un problema cuando tenemos variables con distintos rangos. Quitemos pop para visualizar mejor las otras columnas:

economics %>% 
  select(-pop) %>% 
  pivot_longer(names_to = "var", values_to = "vl", cols = -date) %>% 
  ggplot(aes(x = date, y = vl, color = var))+
  geom_line()

Se observa mucho mejor el comportamiento de pce y unemploy, pues coincide que tienen rangos similares.

Veamos las 2 variables con rangos más pequeños:

economics %>% 
  select(date, psavert, uempmed) %>% 
  pivot_longer(names_to = "var", values_to = "vl", cols = -date) %>% 
  ggplot(aes(x = date, y = vl, color = var))+
  geom_line()

2 lecciones importantes:

  • El formato long es muy útil para visualizar múltiples variables en un mismo gráfico. Utilizarás constantemente pivot_longer() al graficar varias variables.

  • Cuando las variables tienen rangos muy distintos, (por ejemplo, algunas están en las decenas de millares, mientras otras están en unidades, y otras entre 0 y 1) se vuelve imposible visualizar el comportamiento en un mismo gráfico. Para esto utilizamos facet_wrap.

facet_wrap()

Graficar variables en distintas escalas

Continuando con la idea de la sección anterior, resolvamos el problema de tener diferentes escalas en los datos con ayuda de facet_wrap():

economics %>% 
  pivot_longer(names_to = "var", values_to = "vl", cols = -date) %>% 
  ggplot(aes(date, vl))+
  geom_line()+
  facet_wrap(~var)

Observa 3 puntos importantes:

  • Los datos se transforman, de nuevo, a formato long.

  • Ya no utilizamos aesthetic color.

  • Agregamos la función facet_wrap(), la cual separa cada columna en su propio panel gráfico. Es importante utilizar tilde ~ para especificar la variable que se utilizará para hacer la separación.

Observa también que, aunque ya están por separado, aún se tiene el problema de diferencias de escala. Utilizamos el argumento scales = "free_y" para permitir que cada panel tenga su propia escala del eje y.

economics %>% 
  pivot_longer(names_to = "var", values_to = "vl", cols = -date) %>% 
  ggplot(aes(date, vl))+
  geom_line()+
  facet_wrap(~var, scales = "free_y")

De esta forma, cada panel tiene su propia escala del eje vertical, y podemos visualizar cada variable por separado, pero en un mismo gráfico.

Agreguemos el argumento ncol = 2 para modificar la forma en que se muestran los páneles.

economics %>% 
  pivot_longer(names_to = "var", values_to = "vl", cols = -date) %>% 
  ggplot(aes(date, vl))+
  geom_line()+
  facet_wrap(~var, scales = "free_y", ncol = 2)

Ejercicios:

  • Repite el gráfico anterior, pero intentando con valores distintos de ncol.

  • Repite el gráfico anterior, pero utiliza el argumento nrow en lugar de ncol, dentro de facet_wrap()

  • Repite el gráfico anterior, pero utiliza scales = "free". ¿Qué diferencias notas, respecto a “free_y”? ¿En qué circunstancias sería util utilizar “free” en lugar de “free_y”

  • Repite el gráfico anterior, pero agrega la aesthetic color = var dentro de aes() y observa lo que sucede. ¿Qué desventaja encuentras de utilizar la aesthetic color al mismo tiempo que facet_wrap()?

Agregar más variables a la visualización.

Retomando el ejemplo de gráfica de barras, utilizando el dataset diamonds, teníamos lo siguiente:

diamonds_sample %>% 
  ggplot(aes(x = clarity, fill = cut))+
  geom_bar(position = "dodge")

De esta forma, visualizamos el comportamiento de 2 variables simúltaneamente.

facet_wrap() es útil para agregar una 3ra variable a la gráfica:

diamonds_sample %>% 
  ggplot(aes(x = clarity, fill = cut))+
  geom_bar(position = "dodge")+
  facet_wrap(~color, ncol = 2)

De esta forma es posible visualizar cut, clarity y color al mismo tiempo.

Ejercicio: Tenemos 3 variables, y 3 argumentos dónde utilzarlas (aes(x), aes(fill) y facet_wrap(~)). Realiza al menos 3 gráficos en donde pruebes distintas combinaciones de variables y argumentos y compara entre ellos. ¿Con cuál combinación consideras que se ven mejor los datos? Explica tu razonamiento.

Recuerda que geom_bar() realiza el conteo de observaciones por defecto, sin embargo, podemos modificar ese comportamiento con stat = "identity".

Creemos un tibble en el cual se calculan los promedios del precio por kilate, agrupados por cut, color, clarity. Similar a uno de los ejercicios del caso 1.

mean_ppk <- diamonds_sample %>% 
  mutate(ppk = price/carat) %>% 
  group_by(cut, color, clarity) %>% 
  summarise(mean_ppk = mean(ppk), .groups = "drop")

mean_ppk
## # A tibble: 244 x 4
##    cut   color clarity mean_ppk
##    <ord> <ord> <ord>      <dbl>
##  1 Fair  D     SI2        3196.
##  2 Fair  D     SI1        4654.
##  3 Fair  D     VS2        4025.
##  4 Fair  E     SI2        2398.
##  5 Fair  E     SI1        3312 
##  6 Fair  E     VS2        2362.
##  7 Fair  E     VS1        5242.
##  8 Fair  E     VVS2       3998.
##  9 Fair  F     I1         2187.
## 10 Fair  F     SI2        4534.
## # ... with 234 more rows

Esta información es muy útil para entender cómo se relacionan estas variables entre sí, sin embargo, verlas en una tabla no nos dice mucho.

Podemos agruparlas todas en una gráfica, aplicando todo lo que hemos visto:

mean_ppk  %>% 
  ggplot(aes(color, mean_ppk, fill = cut))+
  geom_bar(stat = "identity", position = "dodge")+
  facet_wrap(~clarity, ncol = 2, scales = "free_x")

Viendo esta gráfica, podemos ver el impacto que tiene cada una de las 3 variables categóricas en el precio por kilate:

  • En general parece ser que cut no es muy importante, pues las barras de todos los colores tienen tamaños similares. Esperaríamos ver constantemente que las barras de Idealsean las más altas, y de Fairlas más bajas, sin embargo eso sucede en muy pocos niveles de clarity~color. Por ejemplo, en SI1 color H, vemos el comportamiento esperado, mientras que para VS2 color I, vemos lo opuesto.

  • La variable color parece tampoco tener un impacto tan fuerte. Esperaríamos que las barras, en general, sean más altas conforme el nivel de color aumenta hacia D, y más cortas conforme decrece a J, sin embargo el patrón observado en ocassiones es lo opuesto, o en la mayoría de veces parecen mantener el mismo tamaño.

  • La variable clarity tiene un patrón más evidente, pues en general las barras parecen ser más altas conforme el nivel de claridad aumenta, siendo IF el panel con barras más altas.

Ejercicio:

  • Repite el último gráfico, pero ahora calculando mean_ppk con el tibble original diamonds en lugar de diamonds_sample.

  • ¿Qué observas? ¿Cuáles de las observaciones anteriores se conservan y cuáles cambian?

  • Después de hacer este análisis gráfico, ¿Es posible responder la pregunta del nivel 5 del caso 1?