6  Análisis de datos exploratorio


Paquetes para este capítulo


En este capítulo vamos a aplicar lo que hemos aprendido en los dos capítulos anteriores, combinando transformación de datos con visualización para entender nuestras bases de datos, buscar patrones interesantes, etc. Podéis encontrar una introducción más completa en el manual R 4 data science - exploratory data analysis.

6.1 Visualizando distribuciones

Para visualizar la distribución de nuestras variables, tendremos que seguir estrategias diferentes dependiendo de si se trata de variables categóricas o continuas.

6.1.1 Variables categóricas


ggplot(gapminder, aes(continent)) +
  geom_bar()

gapminder |> 
  count(continent)
#> # A tibble: 5 × 2
#>   continent     n
#>   <fct>     <int>
#> 1 Africa      624
#> 2 Americas    300
#> 3 Asia        396
#> 4 Europe      360
#> 5 Oceania      24

6.1.2 Variables continuas

Para ver la distribución de una variable podemos empezar con un histograma sencillo.


ggplot(gapminder, aes(lifeExp)) +
  geom_histogram(binwidth = 1)


summarise() nos permite ver medias, medianas, etc.


gapminder |>
  summarise(MEAN = mean(lifeExp),
            MEDIAN = median(lifeExp),
            SD = sd(lifeExp),
            MAX = max(lifeExp),
            MIN = min(lifeExp))
#> # A tibble: 1 × 5
#>    MEAN MEDIAN    SD   MAX   MIN
#>   <dbl>  <dbl> <dbl> <dbl> <dbl>
#> 1  59.5   60.7  12.9  82.6  23.6

Alternativamente, hay funciones como skimr::skim() que nos muestran una panorámica muy útil de las variables de nuestro data frame. Corre la función en tu Consola para ver el output completo.


skimr::skim(gapminder)
Data summary
Name gapminder
Number of rows 1704
Number of columns 6
_______________________
Column type frequency:
factor 2
numeric 4
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
country 0 1 FALSE 142 Afg: 12, Alb: 12, Alg: 12, Ang: 12
continent 0 1 FALSE 5 Afr: 624, Asi: 396, Eur: 360, Ame: 300

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
year 0 1 1979.50 17.27 1952.00 1965.75 1979.50 1993.25 2007.0 ▇▅▅▅▇
lifeExp 0 1 59.47 12.92 23.60 48.20 60.71 70.85 82.6 ▁▆▇▇▇
pop 0 1 29601212.32 106157896.74 60011.00 2793664.00 7023595.50 19585221.75 1318683096.0 ▇▁▁▁▁
gdpPercap 0 1 7215.33 9857.45 241.17 1202.06 3531.85 9325.46 113523.1 ▇▁▁▁▁

Ejercicios

Variables individuales

Usando el DF mpg, visualiza la distribución de las variables manufacturer, y hwy. Fíjate que la primera es categórica, y la segunda continua.

Vas a tener que elegir entre geom_bar() y geom_histogram().

Puedes ver que pasa si usas el parámetro binwidth = 1 en geom_histogram().

Usamos geom_bar() para visualizar variables discretas:

Para variables continuas, geom_histogram():


Ejercicios avanzados

Usando como base éste código:

ggplot(gapminder, aes(lifeExp, fill = continent)) +
  geom_histogram(binwidth = 1)

¿Podrías replicar la visualización de abajo? Queremos mostrar un histograma por continente.

1) Empieza con el histograma de arriba.
2) recuerda que puedes usar el parámetro fill (dentro de aes), para asignar un color de relleno por nivel de una variable categórica.
3) Finalmente, usando facetas podrás crear una gráfica para cada nivel de la variable categórica facet_wrap()!

¿Como podemos añadir el histograma general para poder entender donde se ubica cada continente?

El paquete gghighlight es justo lo que necesitas

También queremos ver los descriptivos por continente, ordenados por el promedio:

#> # A tibble: 5 × 6
#>   continent  MEAN MEDIAN    SD   MAX   MIN
#>   <fct>     <dbl>  <dbl> <dbl> <dbl> <dbl>
#> 1 Africa     48.9   47.8  9.15  76.4  23.6
#> 2 Asia       60.1   61.8 11.9   82.6  28.8
#> 3 Americas   64.7   67.0  9.35  80.7  37.6
#> 4 Europe     71.9   72.2  5.43  81.8  43.6
#> 5 Oceania    74.3   73.7  3.80  81.2  69.1

6.1.3 Visualizando datasets completos

Cuando nos llega una nueva base de datos, una de las primeras cosas que haremos será familiarizarnos con los datos. Cómo se distribuyen, cual es la relación entre distintas variables, etc.

Convertimos la base a formato largo para poder filtrar los valores perdidos (i.e. 999), y crear una columna donde introducir únicamente valores numéricos:

d <- gapminder |>
  pivot_longer(everything(), values_transform = list(value = as.character)) |>
  filter(value != 999) |> # Si existiera algún código para missing values, filtrar
  mutate(value_NUM = as.numeric(value))

Visualiza las variables numéricas usando la variables value_NUM que hemos creado:

d |>
  drop_na(value_NUM) |>
  ggplot(aes(value_NUM)) +
    facet_wrap(~ name, scales = "free") +
    geom_histogram(bins = 15) #+ scale_x_log10()

Visualiza variables no numéricas. Para ello, nos quedamos solo con aquellas filas donde value_NUM es una valor perdido. Esto es porque cuando antes hicimos mutate(value_NUM = as.numeric(value)), aquellos valores de value que no se pudieron convertir a numérico, quedaron como NA en value_NUM:


d |>
  drop_na(value) |>
  filter(is.na(value_NUM)) |>
  ggplot(aes(value)) +
    facet_wrap(~ name, scales = "free") +
    geom_bar() +
    coord_flip()

Hay algunos paquetes que ayudan a la exploración inicial de bases de datos, localización de datos perdidos, etc. Por ejemplo {inspectdf}.

6.2 Covariación

6.2.1 Variable categórica y continua

Podemos contar el numero de elementos por nivel de la variable o ver densidad, etc. Esto funciona bien si tenemos pocos niveles de la variable categórica.


ggplot(gapminder, aes(lifeExp, colour = continent)) +
  geom_freqpoly(binwidth = 2)


Podemos usar geom_density_ridges() para combinar puntos con distribuciones:


ggplot(gapminder, aes(lifeExp, continent, fill = continent)) +
  ggridges::geom_density_ridges(
    stat = "binline",
    bins = 20,
    scale = 0.95,
    draw_baseline = FALSE,
    alpha = .3
  ) +
  ggridges::geom_density_ridges(
    jittered_points = TRUE,
    position = "raincloud",
    alpha = 0.5,
    scale = 0.9
  )


¿Qué estamos viendo exactamente arriba? Hay un punto por cada país, y por cada año, lo que da lugar a algo difícil de interpretar.

Podemos ver los datos únicamente del último año:


gapminder |> filter(year == max(year)) |> 
  
  ggplot(aes(lifeExp, continent, fill = continent)) +
  ggridges::geom_density_ridges(
    stat = "binline",
    bins = 20,
    scale = 0.95,
    draw_baseline = FALSE,
    alpha = .3
  ) +
  ggridges::geom_density_ridges(
    jittered_points = TRUE,
    position = "raincloud",
    alpha = 0.5,
    scale = 0.9
  )


6.2.2 Ejercicio avanzado - Introducción

En este ejercicio vamos a intentar mostrar la como la distribución de esperanza de vida ha cambiado a lo largo del tiempo. Para ello, usando la base gapminder, compararemos los valores máximos y mínimos.

Empezamos creando dos gráficos. En cada uno filtramos por el año deseado (e.g. filter(year == min(year))). Fíjate que usamos scale_x_continuous(n.breaks = 10, limits = c(20, 90)) para que ambas gráficas compartan la misma escala en el eje x:


A = ggplot(gapminder |> filter(year == min(year)),
           aes(lifeExp, continent, fill = continent)) +
  ggridges::geom_density_ridges(
    stat = "binline",
    bins = 20,
    scale = 0.95,
    draw_baseline = FALSE,
    alpha = .3
  ) +
  ggridges::geom_density_ridges(
    jittered_points = TRUE,
    position = "raincloud",
    alpha = 0.5,
    scale = 0.9
  ) +
  theme(legend.position = "none") +
  scale_x_continuous(n.breaks = 10, limits = c(20, 90)) +
  ggtitle("min")

B = ggplot(gapminder |> filter(year == max(year)),
           aes(lifeExp, continent, fill = continent)) +
  ggridges::geom_density_ridges(
    stat = "binline",
    bins = 20,
    scale = 0.95,
    draw_baseline = FALSE,
    alpha = .3
  ) +
  ggridges::geom_density_ridges(
    jittered_points = TRUE,
    position = "raincloud",
    alpha = 0.5,
    scale = 0.9
  ) +
  theme(legend.position = "none") +
  scale_x_continuous(n.breaks = 10, limits = c(20, 90)) +
  ggtitle("max")

cowplot::plot_grid(A, B)

Para visualizar la diferencia entre los valores máximos y mínimos, podemos calcular primero cuanto ha cambiado la esperanza de vida en cada país de cada continente, y crear una gráfica con esa variable:


# Cálculo de la diferencia entre el máximo y mínimo de lifeExp para cada country. 
  # Incluimos continent en group_by() para poder usar esa variable en la gráfica
DF_gapminder_max_min = gapminder |>
  group_by(continent, country) |>
  summarise(lifeExp = max(lifeExp) - min(lifeExp))

ggplot(DF_gapminder_max_min, aes(lifeExp, continent, fill = continent)) +
  ggridges::geom_density_ridges(
    stat = "binline",
    bins = 20,
    scale = 0.95,
    draw_baseline = FALSE,
    alpha = .3
  ) +
  ggridges::geom_density_ridges(
    jittered_points = TRUE,
    position = "raincloud",
    alpha = 0.5,
    scale = 0.9
  ) +
  theme(legend.position = "none") +
  ggtitle("Diferencia entre max y min por país")

6.2.3 Ejercicio

Arriba estamos restando la esperanza de vida máxima y mínima de cada país. Sabemos que la esperanza de vida ha mejorado con el tiempo, por lo que asumíamos que era equivalente a comparar los datos más antiguos y los más nuevos.

Pero lo que realmente queremos ver es la diferencia entre 2007 y 1952 ¿Podrías rehacer el cálculo para mostrar la diferencia entre 2007 y 1952?

1. Crear un DF para cada 2007 y otro para 1952, renombrando la variable lifeExp (e.g. max_lifeExp y min_lifeExp, respectivamente)
2. Usando la función full_join(), juntamos ambas bases (tendrás que usar el parámetro by = c("country", "continent")).
3. Con mutate() calculamos la diferencia.

6.2.4 Dos variables categóricas

Podemos contar el número de valores para los niveles de dos variables categóricas con count():


diamonds |>
  count(color, cut)
#> # A tibble: 35 × 3
#>   color cut           n
#>   <ord> <ord>     <int>
#> 1 D     Fair        163
#> 2 D     Good        662
#> 3 D     Very Good  1513
#> 4 D     Premium    1603
#> 5 D     Ideal      2834
#> 6 E     Fair        224
#> # ℹ 29 more rows

Una manera de visualizar esto es con geom_tile():


diamonds |>
  count(color, cut) |>
  ggplot(aes(color, cut, fill = n)) +
    geom_tile()

6.2.5 Dos variables continuas

Una herramienta esencial para ver como covarían dos variables continuas es un scatterplot. Usaremos geom_point():


ggplot(gapminder, aes(lifeExp, gdpPercap)) +
  geom_point()

Si añadimos color y cambiamos a escala logarítmica, podemos hacernos una mejor idea:

ggplot(gapminder, aes(lifeExp, gdpPercap, color = continent)) +
  geom_point(alpha = 1 / 2) +
  scale_y_log10()

Con geom_violin() podemos hacernos una idea rápida de las distribuciones. Usamos group = cut_width(lifeExp, 10) para dividir la variable continua lifeExp en chunks de 10 unidades:

ggplot(gapminder, aes(lifeExp, gdpPercap)) +
  geom_violin(draw_quantiles = .5, aes(group = cut_width(lifeExp, 10))) +
  scale_y_log10(labels = scales::comma, n.breaks = 6)

Con corrplot podemos visualizar las correlaciones entre variables numéricas de nuestra base de datos.

corrplot(cor(gapminder |> select(where(is.numeric))), method = "circle")
corrplot(cor(gapminder |> select(where(is.numeric))), method = "number", type = "upper")

6.2.6 Ejercicio covariación 2

Usando el DF mpg, visualiza la covariación entre:

  • manufacturer y hwy
  • class y hwy
  • hwy y cty

6.3 Ejercicios finales

6.3.1 Ejercicio exploración base nueva

Usando la base del paper Cancer Screening Risk Literacy of Physicians in Training, haz un primer análisis exploratorio que incluya:

  • histogramas de todas las variables numéricas y no-numéricas
  • scatterplots de la relación entre comprensión y numeracy, y entre comprensión y screenbeliefs

Puedes ir al enlace anterior y descargar el archivo Cancer screening risk literacy R1.sav en la carpeta Data and results, o directamente usar el código de abajo.


# Usamos haven::read_sav() para leer los archivos .sav
DF_dafina = haven::read_sav(here::here("data/files/Dafina", "Cancer screening risk literacy R1.sav")) |> as_tibble()

Bibliografía

Wickham, H., & Grolemund, G. (2016). R for data science: import, tidy, transform, visualize, and model data. O’Reilly Media, Inc. https://r4ds.had.co.nz/