MCAP1 - Cómo filtrar datos

Author

DASR

Introducción al manejo de datos {dplyr}

Intro

[dplyr] es un paquete para el manejo de datos, desarrollado por Hadley Wickham y Romain Francois. Se instala como parte de [tidyverse][tidyverse-web] al cargar library(tidyverse).

El origen de dplyr está en un paquete anterior llamado [plyr], que implementa el enfoque [“divide-aplica-combina” para análisis de datos]. El paquete dplyr se enfoca en conjunto de datos estructurados o “tibbles”.

Carga dplyr y gapminder

Primero, segúrate de cargar tidyverse, lo cual a su vez, descargará dplyr y otros paquetes que usaremos más adelante. También trabajaremos con una versión traducida de los datos precargados en tidyverse.

library(tidyverse)
Warning: package 'tidyverse' was built under R version 4.2.3
Warning: package 'ggplot2' was built under R version 4.2.3
Warning: package 'tibble' was built under R version 4.2.3
Warning: package 'tidyr' was built under R version 4.2.3
Warning: package 'readr' was built under R version 4.2.3
Warning: package 'purrr' was built under R version 4.2.3
Warning: package 'dplyr' was built under R version 4.2.3
Warning: package 'stringr' was built under R version 4.2.2
Warning: package 'forcats' was built under R version 4.2.3
Warning: package 'lubridate' was built under R version 4.2.3
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.2     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.4     ✔ tibble    3.2.1
✔ lubridate 1.9.2     ✔ tidyr     1.3.0
✔ purrr     1.0.1     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

Carga el paquete con los datos traducidos.

#install.packages("datos")
library(datos)
library(gapminder)

Primeros tibble de gapminder

Los datos de gapminder tienen una estructura de datos particular:el tibble.

gapminder
# A tibble: 1,704 × 6
   country     continent  year lifeExp      pop gdpPercap
   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
 1 Afghanistan Asia       1952    28.8  8425333      779.
 2 Afghanistan Asia       1957    30.3  9240934      821.
 3 Afghanistan Asia       1962    32.0 10267083      853.
 4 Afghanistan Asia       1967    34.0 11537966      836.
 5 Afghanistan Asia       1972    36.1 13079460      740.
 6 Afghanistan Asia       1977    38.4 14880372      786.
 7 Afghanistan Asia       1982    39.9 12881816      978.
 8 Afghanistan Asia       1987    40.8 13867957      852.
 9 Afghanistan Asia       1992    41.7 16317921      649.
10 Afghanistan Asia       1997    41.8 22227415      635.
# ℹ 1,694 more rows
#install.packages("datos")
#library(datos)
#datos

Para convertir cualquier conjunto de datos a tibble usa as_tibble():

as_tibble(iris)
# A tibble: 150 × 5
   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          <dbl>       <dbl>        <dbl>       <dbl> <fct>  
 1          5.1         3.5          1.4         0.2 setosa 
 2          4.9         3            1.4         0.2 setosa 
 3          4.7         3.2          1.3         0.2 setosa 
 4          4.6         3.1          1.5         0.2 setosa 
 5          5           3.6          1.4         0.2 setosa 
 6          5.4         3.9          1.7         0.4 setosa 
 7          4.6         3.4          1.4         0.3 setosa 
 8          5           3.4          1.5         0.2 setosa 
 9          4.4         2.9          1.4         0.2 setosa 
10          4.9         3.1          1.5         0.1 setosa 
# ℹ 140 more rows

Antes de que crees secciones de tus datos…

Si piensas que sería buena idea extraer solo una parte de tus datos:

(canada <- gapminder[241:252, ])
# A tibble: 12 × 6
   country continent  year lifeExp      pop gdpPercap
   <fct>   <fct>     <int>   <dbl>    <int>     <dbl>
 1 Canada  Americas   1952    68.8 14785584    11367.
 2 Canada  Americas   1957    70.0 17010154    12490.
 3 Canada  Americas   1962    71.3 18985849    13462.
 4 Canada  Americas   1967    72.1 20819767    16077.
 5 Canada  Americas   1972    72.9 22284500    18971.
 6 Canada  Americas   1977    74.2 23796400    22091.
 7 Canada  Americas   1982    75.8 25201900    22899.
 8 Canada  Americas   1987    76.9 26549700    26627.
 9 Canada  Americas   1992    78.0 28523502    26343.
10 Canada  Americas   1997    78.6 30305843    28955.
11 Canada  Americas   2002    79.8 31902268    33329.
12 Canada  Americas   2007    80.7 33390141    36319.

Pregúntate primero…

¿Quiero crear mini conjuntos de datos para cada nivel o factor de mis datos?

Si la respuesta es SÍ, utiliza técnicas de agregación de datos o crea facetac con ggplot2 – no hagas subconjuntos de datos. Puedes, de forma temporal, hacer subconjuntos de datos temporalmente en lo que desarrollas un código más rápido y elegante.

Si la respuesta es NO, probablemente realizar subconjuntos funcione para ti. Considera si puedes usar el argumento subset =.

LAs copias y sub conjuntos consumen espacio en tu ambiente de trabajo, pueden crear confusiones y generar errores. Evita en la medida de lo posible.

Utiliza filter() para obtener un conjunto de datos por filas

filter() toma expresiones lógicas y devuelve filas donde dicha expresión es verdadera TRUE.

filter(gapminder, lifeExp < 29)
# A tibble: 2 × 6
  country     continent  year lifeExp     pop gdpPercap
  <fct>       <fct>     <int>   <dbl>   <int>     <dbl>
1 Afghanistan Asia       1952    28.8 8425333      779.
2 Rwanda      Africa     1992    23.6 7290203      737.
filter(gapminder, country == "Rwanda", year > 1979)
# A tibble: 6 × 6
  country continent  year lifeExp     pop gdpPercap
  <fct>   <fct>     <int>   <dbl>   <int>     <dbl>
1 Rwanda  Africa     1982    46.2 5507565      882.
2 Rwanda  Africa     1987    44.0 6349365      848.
3 Rwanda  Africa     1992    23.6 7290203      737.
4 Rwanda  Africa     1997    36.1 7212583      590.
5 Rwanda  Africa     2002    43.4 7852401      786.
6 Rwanda  Africa     2007    46.2 8860588      863.
filter(gapminder, country %in% c("Rwanda", "Afghanistan"))
# A tibble: 24 × 6
   country     continent  year lifeExp      pop gdpPercap
   <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
 1 Afghanistan Asia       1952    28.8  8425333      779.
 2 Afghanistan Asia       1957    30.3  9240934      821.
 3 Afghanistan Asia       1962    32.0 10267083      853.
 4 Afghanistan Asia       1967    34.0 11537966      836.
 5 Afghanistan Asia       1972    36.1 13079460      740.
 6 Afghanistan Asia       1977    38.4 14880372      786.
 7 Afghanistan Asia       1982    39.9 12881816      978.
 8 Afghanistan Asia       1987    40.8 13867957      852.
 9 Afghanistan Asia       1992    41.7 16317921      649.
10 Afghanistan Asia       1997    41.8 22227415      635.
# ℹ 14 more rows

Compara con código base de R que también realiza las mismas funciones:

gapminder[gapminder$lifeExp < 29, ] ## la repetición de `gapminder` puede confundir, [i, j] la forma de indexar distrae.

subset(gapminder, country == "Rwanda") ## similar a filter

Bajo ninguna circunstancia realices subconjuntos de datos como en el primer ejemplo:

excerpt <- gapminder[241:252, ]

¿Por qué es una mala idea?

  • No nos dice nada acerca de los datos. Debes conocer perfectamente la base de datos para saber qué hay de especial en las filas 241 a 252?

  • Es frágil en el sentido de que este código producirá resultados diferentes si alguien cambia el orden de las filas en gapminder, por ejemplo, si ordena los datos de mayor a menor en el script.

filter(gapminder, country == "Mexico")

En cambio, esta línea de código se explica a sí misma, sabemos que el filtro se está realizando por un determinado país.

Conoce el operador pipe

Debemos conocer el operador pipe que utiliza tidyverse del paquete [magrittr] de Stefan Bache. Con este operador ya no es necesario anidar operaciones múltiples con comandos idem, como muñecas tradicionales rusas. El uso de este operador nos lleva a una nueva sintaxis que es más sencilla de escribir y de leer.

Este es el operador pipe: %>%. En RStudio utiliza la combinación de teclas: Ctrl+Shift+M (Windows), Cmd+Shift+M (Mac).

gapminder %>% head()
# A tibble: 6 × 6
  country     continent  year lifeExp      pop gdpPercap
  <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
1 Afghanistan Asia       1952    28.8  8425333      779.
2 Afghanistan Asia       1957    30.3  9240934      821.
3 Afghanistan Asia       1962    32.0 10267083      853.
4 Afghanistan Asia       1967    34.0 11537966      836.
5 Afghanistan Asia       1972    36.1 13079460      740.
6 Afghanistan Asia       1977    38.4 14880372      786.

Esto es equivalente a head(gapminder). El operador toma la instrucción del lado izquierdo y lo lleva pipes directo a la función de la derecha como si fuera el primer argumento.

Aún se pueden especificar otros argumentos en esta función, por ejemplo para ver las primeras 3 filas degapminder, indicaríamos head(gapminder, 3) o, con pipe:

gapminder %>% head(3)
# A tibble: 3 × 6
  country     continent  year lifeExp      pop gdpPercap
  <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
1 Afghanistan Asia       1952    28.8  8425333      779.
2 Afghanistan Asia       1957    30.3  9240934      821.
3 Afghanistan Asia       1962    32.0 10267083      853.

En otros momentos te he aconsejado que pienses en “asignación” cuando ves el operador<-. De forma similar, te aconsejo que pienses “después o en consecuencia” cuando veas el operador pipe , %>%.

Utiliza select() para hacer un subconjunto de datos en variables o columnas de tu interés.

Utiliza select() para hacer un subconjunto de datos:

select(gapminder, year, lifeExp)
# A tibble: 1,704 × 2
    year lifeExp
   <int>   <dbl>
 1  1952    28.8
 2  1957    30.3
 3  1962    32.0
 4  1967    34.0
 5  1972    36.1
 6  1977    38.4
 7  1982    39.9
 8  1987    40.8
 9  1992    41.7
10  1997    41.8
# ℹ 1,694 more rows

Esta es la misma operación pero escrita con el operador pipe y el comandohead():

gapminder %>%

select(year, lifeExp) %>%

head(4)
# A tibble: 4 × 2
   year lifeExp
  <int>   <dbl>
1  1952    28.8
2  1957    30.3
3  1962    32.0
4  1967    34.0

La instrucción aquí es: “Toma gapminder, posteriormente selecciona las variables year y lifeExp, después muestra las primeras 4 filas.”

La conveniencia del operador %>%

Estos son los datos para México, pero solo algunas variables:

gapminder %>%

filter(country == "Mexico") %>%

select(year, lifeExp)
# A tibble: 12 × 2
    year lifeExp
   <int>   <dbl>
 1  1952    50.8
 2  1957    55.2
 3  1962    58.3
 4  1967    60.1
 5  1972    62.4
 6  1977    65.0
 7  1982    67.4
 8  1987    69.5
 9  1992    71.5
10  1997    73.7
11  2002    74.9
12  2007    76.2

De esta forma se llamarían los mismos datos con R base:

gapminder[gapminder$country == "Mexico", c("year", "lifeExp")]
# A tibble: 12 × 2
    year lifeExp
   <int>   <dbl>
 1  1952    50.8
 2  1957    55.2
 3  1962    58.3
 4  1967    60.1
 5  1972    62.4
 6  1977    65.0
 7  1982    67.4
 8  1987    69.5
 9  1992    71.5
10  1997    73.7
11  2002    74.9
12  2007    76.2

Funciones puras

Funciones que se entienden facilmente y no tienen impacto en el ambiente de trabajo. Es decir, no tienen “efectos secundarios” más allá del valor que nos muestran.

El conjunto de datos es siempre el primer argumento de las funciones verbo.Lo veremos a detalle más adelante.