Capítulo 19 Depuración y transformación

  • De aquí en adelante intentaremos que nuestros datos sean Tidy data
  • Conocemos ya un formato amable de almacenar los datos como son los data.frame de tipo tibble

Como hemos comentado antes, muchas veces los datos no los tenemos en el formato deseado, o directamente queremos realizar algunas transformaciones en los mismos, crear nuevas variables u obtener resúmenes numéricos. En esta sección empezaremos a explorar, procesar, depurar y transformar los datos, haciendo uso principalmente del entorno de paquetes tidyverse.

Imagen extraída de <https://sporella.github.io/datos_espaciales_presentacion/#30>

Imagen/gráfica 19.1: Imagen extraída de https://sporella.github.io/datos_espaciales_presentacion/#30

El entorno tidyverse es una de las herramientas más importantes en el manejo de datos en R, una colección de paquetes pensada para el manejo, la exploración, el análisis y la visualización de datos, compartiendo una misma filosofía y gramática.

19.1 Conjunto starwars

Esta sección pretende ser una introducción a dicho entorno, y lo haremos principalmente con el conjunto de datos starwars, del paquete dplyr (ya cargado en tidyverse).

starwars
## # A tibble: 87 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Dicho conjunto de datos, extraído de la Star Wars API, recopila diferentes datos y características de los personajes de la saga Star Wars. Como habrás advertido al escribir el conjunto en consola, starwars es una tabla en un formato ya conocido, un tibble, en el que se nos especifica al inicio de la tabla de qué tipo son cada columna:

  • int: números enteros.
  • dbl: números reales (o racionales).
  • chr: cadenas de texto.
  • lgl: valores lógicos (TRUE o FALSE).
  • fct: factores, variables cualitativas (categorías).
  • date: fechas.
  • list: listas.

Nuestro conjunto de datos tiene 87 registros y 14 variables.

# Tipo de datos
class(starwars)
## [1] "tbl_df"     "tbl"        "data.frame"
# Dimensión de los datos
dim(starwars)
## [1] 87 14

Las funciones que veremos a lo largo de esta sección siempre van a compartir una misma metodología: primero escribimos el nombre un conjunto de datos tibble y después lo que queremos hacer a dicho conjunto de datos (con las variables SIN comillas) encandenando órdenes con %>%, de forma que obtendremos una nueva tabla en el mismo formato de entrada.

Con glimpse(starwars) podemos obtener una visión global de las variables que tenemos en nuestros datos.

# Resumen por columnas
glimpse(starwars)
## Rows: 87
## Columns: 14
## $ name       <chr> "Luke Skywalker", "C-3PO", "R2-D2", "Darth Vader", "Leia Or…
## $ height     <int> 172, 167, 96, 202, 150, 178, 165, 97, 183, 182, 188, 180, 2…
## $ mass       <dbl> 77.0, 75.0, 32.0, 136.0, 49.0, 120.0, 75.0, 32.0, 84.0, 77.…
## $ hair_color <chr> "blond", NA, NA, "none", "brown", "brown, grey", "brown", N…
## $ skin_color <chr> "fair", "gold", "white, blue", "white", "light", "light", "…
## $ eye_color  <chr> "blue", "yellow", "red", "yellow", "brown", "blue", "blue",…
## $ birth_year <dbl> 19.0, 112.0, 33.0, 41.9, 19.0, 52.0, 47.0, NA, 24.0, 57.0, …
## $ sex        <chr> "male", "none", "none", "male", "female", "male", "female",…
## $ gender     <chr> "masculine", "masculine", "masculine", "masculine", "femini…
## $ homeworld  <chr> "Tatooine", "Tatooine", "Naboo", "Tatooine", "Alderaan", "T…
## $ species    <chr> "Human", "Droid", "Droid", "Human", "Human", "Human", "Huma…
## $ films      <list> <"The Empire Strikes Back", "Revenge of the Sith", "Return…
## $ vehicles   <list> <"Snowspeeder", "Imperial Speeder Bike">, <>, <>, <>, "Imp…
## $ starships  <list> <"X-wing", "Imperial shuttle">, <>, <>, "TIE Advanced x1",…

Los datos también podemos verlos escribiendo el nombre de la tabla en la consola (recuerda que si es un tibble, para evitar saturar la consola, te saca un extracto, no todas las columnas y filas) o bien con la función print(), indicándole número de filas (n = 15 por ejemplo) y número de columnas (width = Inf si queremos mostrar todas).

# Imprimir personalizado
print(starwars, n = 5, width = Inf)
## # A tibble: 87 × 14
##   name           height  mass hair_color skin_color  eye_color birth_year sex   
##   <chr>           <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> 
## 1 Luke Skywalker    172    77 blond      fair        blue            19   male  
## 2 C-3PO             167    75 <NA>       gold        yellow         112   none  
## 3 R2-D2              96    32 <NA>       white, blue red             33   none  
## 4 Darth Vader       202   136 none       white       yellow          41.9 male  
## 5 Leia Organa       150    49 brown      light       brown           19   female
##   gender    homeworld species films     vehicles  starships
##   <chr>     <chr>     <chr>   <list>    <list>    <list>   
## 1 masculine Tatooine  Human   <chr [5]> <chr [2]> <chr [2]>
## 2 masculine Tatooine  Droid   <chr [6]> <chr [0]> <chr [0]>
## 3 masculine Naboo     Droid   <chr [7]> <chr [0]> <chr [0]>
## 4 masculine Tatooine  Human   <chr [4]> <chr [0]> <chr [1]>
## 5 feminine  Alderaan  Human   <chr [5]> <chr [1]> <chr [0]>
## # … with 82 more rows

Fíjate que las 3 últimas variables son de tipo lista (echa un vistazo a Listas). Por ejemplo, en starwars$films se guardan para cada personaje la colección de películas de la saga en las que aparece (algunos tendrán 1 solo nombre, otros 7). Por ejemplo, para extraer las películas en las que aparecen los tres primeros personajes, basta con ejecutar starwars$films[1:3] (del conjunto starwars –> accedemos a la variable films con $)

# Las películas de los tres primeros personajes
starwars$films[1:3]
## [[1]]
## [1] "The Empire Strikes Back" "Revenge of the Sith"    
## [3] "Return of the Jedi"      "A New Hope"             
## [5] "The Force Awakens"      
## 
## [[2]]
## [1] "The Empire Strikes Back" "Attack of the Clones"   
## [3] "The Phantom Menace"      "Revenge of the Sith"    
## [5] "Return of the Jedi"      "A New Hope"             
## 
## [[3]]
## [1] "The Empire Strikes Back" "Attack of the Clones"   
## [3] "The Phantom Menace"      "Revenge of the Sith"    
## [5] "Return of the Jedi"      "A New Hope"             
## [7] "The Force Awakens"
Cheet sheet de las opciones del paquete dplyr para la manipulación de datos extraída de <https://github.com/rstudio/cheatsheets/blob/master/data-transformation.pdf>

Imagen/gráfica 19.2: Cheet sheet de las opciones del paquete dplyr para la manipulación de datos extraída de https://github.com/rstudio/cheatsheets/blob/master/data-transformation.pdf

19.2 Operaciones con filas

Las operaciones con filas que podemos hacer son principalmente:

  • Seleccionar filas
  • Reordenar filas
  • Eliminar filas
  • Tratar filas duplicadas
  • Añadir registros nuevos

19.2.1 Seleccionar filas (filter y slice)

Empecemos por aprender a seleccionar filas. Principalmente tenemos tres opciones:

  1. Seleccionarlas en base a algún filtro o condición (solo los mayores de tal edad, por ejemplo).
  2. Extraer filas por su índice de fila (la fila que ocupa tal personaje).
  3. Extraer filas aleatoriamente (uniforme) (por ejemplo, para un sorteo).

 

En el primer caso, la función filter() nos permite seleccionar filas en base al cumplimiento de una o varias condiciones lógicas respecto a las variables. Para usarla basta con que introduzcamos como argumento el conjunto de condiciones que debe cumplir (recuerda: el nombre de columnas sin comillas). Supongamos que queremos por ejemplo seleccionar los personajes con ojos marrones: nos bastará con usar filter() con la condición eye_color == "brown".

starwars %>% filter(eye_color == "brown") # con ojos marrones
## # A tibble: 21 × 14
##    name     height  mass hair_color skin_color eye_color birth_year sex   gender
##    <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Leia Or…    150  49   brown      light      brown           19   fema… femin…
##  2 Biggs D…    183  84   black      light      brown           24   male  mascu…
##  3 Han Solo    180  80   brown      fair       brown           29   male  mascu…
##  4 Yoda         66  17   white      green      brown          896   male  mascu…
##  5 Boba Fe…    183  78.2 black      fair       brown           31.5 male  mascu…
##  6 Lando C…    177  79   black      dark       brown           31   male  mascu…
##  7 Arvel C…     NA  NA   brown      fair       brown           NA   male  mascu…
##  8 Wicket …     88  20   brown      brown      brown            8   male  mascu…
##  9 Quarsh …    183  NA   black      dark       brown           62   <NA>  <NA>  
## 10 Shmi Sk…    163  NA   black      fair       brown           72   fema… femin…
## # … with 11 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Esta forma de trabajar los datos sería algo más difícil de leer fuera del entorno tidyverse (a pesar de que la operación es sencilla).

# A la «antigua» usanza
starwars[starwars$eye_color == "brown", ]
## # A tibble: 21 × 14
##    name     height  mass hair_color skin_color eye_color birth_year sex   gender
##    <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Leia Or…    150  49   brown      light      brown           19   fema… femin…
##  2 Biggs D…    183  84   black      light      brown           24   male  mascu…
##  3 Han Solo    180  80   brown      fair       brown           29   male  mascu…
##  4 Yoda         66  17   white      green      brown          896   male  mascu…
##  5 Boba Fe…    183  78.2 black      fair       brown           31.5 male  mascu…
##  6 Lando C…    177  79   black      dark       brown           31   male  mascu…
##  7 Arvel C…     NA  NA   brown      fair       brown           NA   male  mascu…
##  8 Wicket …     88  20   brown      brown      brown            8   male  mascu…
##  9 Quarsh …    183  NA   black      dark       brown           62   <NA>  <NA>  
## 10 Shmi Sk…    163  NA   black      fair       brown           72   fema… femin…
## # … with 11 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Con %>% y filter(), en tan solo una línea, hemos hecho más de lo que parece: R ha «recorrido» (algo similar a un bucle) cada una de las filas y ha ido comprobando que filas cumplen la condición y cuales no.

Con la misma lógica podemos seleccionar los que NO TIENEN ojos marrones, cambiando == por !=

starwars %>% filter(eye_color != "brown") # NO tengan ojos marrones
## # A tibble: 66 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  6 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  7 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  8 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
##  9 Anakin…    188    84 blond       fair       blue            41.9 male  mascu…
## 10 Wilhuf…    180    NA auburn, gr… fair       blue            64   male  mascu…
## # … with 56 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

También se pueden seleccionar los personajes que tienen los ojos de una serie de colores permitidos: con %in% le indicaremos que seleccione solo aquellas filas cuyo valor de la variable eye_color esté dentro de unos valores candidatos.

starwars %>% # con ojos marrones, azules o rojos
  filter(eye_color %in% c("brown", "blue", "red"))
## # A tibble: 45 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  3 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  4 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  5 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  6 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  7 Biggs …    183    84 black       light      brown           24   male  mascu…
##  8 Anakin…    188    84 blond       fair       blue            41.9 male  mascu…
##  9 Wilhuf…    180    NA auburn, gr… fair       blue            64   male  mascu…
## 10 Chewba…    228   112 brown       unknown    blue           200   male  mascu…
## # … with 35 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Si es una variable numérica también podemos seleccionar por rango con between(), por ejemplo, los personajes cuya altura está entre 120 y 160 cm.

# Con between filtramos por rango
starwars %>% filter(between(height, 120, 160))
## # A tibble: 6 × 14
##   name     height  mass hair_color skin_color eye_color birth_year sex   gender 
##   <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr>  
## 1 Leia Or…    150    49 brown      light      brown             19 fema… femini…
## 2 Mon Mot…    150    NA auburn     fair       blue              48 fema… femini…
## 3 Nien Nu…    160    68 none       grey       black             NA male  mascul…
## 4 Watto       137    NA black      blue, grey yellow            NA male  mascul…
## 5 Gasgano     122    NA none       white, bl… black             NA male  mascul…
## 6 Cordé       157    NA brown      light      brown             NA fema… femini…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

Las condiciones se pueden concatenar y complejizar todo lo que queramos, pudiendo en pocas líneas realizar un filtro complejo, por ejemplo, seleccionando los personajes con ojos marrones Y ADEMÁS que sean humanos.

# humanos con ojos marrones
starwars %>% filter(eye_color == "brown", species == "Human")
## # A tibble: 17 × 14
##    name     height  mass hair_color skin_color eye_color birth_year sex   gender
##    <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Leia Or…    150  49   brown      light      brown           19   fema… femin…
##  2 Biggs D…    183  84   black      light      brown           24   male  mascu…
##  3 Han Solo    180  80   brown      fair       brown           29   male  mascu…
##  4 Boba Fe…    183  78.2 black      fair       brown           31.5 male  mascu…
##  5 Lando C…    177  79   black      dark       brown           31   male  mascu…
##  6 Arvel C…     NA  NA   brown      fair       brown           NA   male  mascu…
##  7 Shmi Sk…    163  NA   black      fair       brown           72   fema… femin…
##  8 Mace Wi…    188  84   none       dark       brown           72   male  mascu…
##  9 Gregar …    185  85   black      dark       brown           NA   male  mascu…
## 10 Cordé       157  NA   brown      light      brown           NA   fema… femin…
## 11 Dormé       165  NA   brown      light      brown           NA   fema… femin…
## 12 Dooku       193  80   white      fair       brown          102   male  mascu…
## 13 Bail Pr…    191  NA   black      tan        brown           67   male  mascu…
## 14 Jango F…    183  79   black      tan        brown           66   male  mascu…
## 15 Raymus …    188  79   brown      light      brown           NA   male  mascu…
## 16 Poe Dam…     NA  NA   brown      light      brown           NA   male  mascu…
## 17 Padmé A…    165  45   brown      light      brown           46   fema… femin…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

Otro ejemplo: vamos a filtrar personajes con ojos marrones, azules o rojos, que no sean humanos, y con menos de 50 años.

# humanos con ojos marrones, azules o rojos, que no sean humanos, y menos de 50a
starwars %>% 
  filter(eye_color %in% c("brown", "blue", "red"),
         species != "Human", birth_year < 50)
## # A tibble: 4 × 14
##   name      height  mass hair_color skin_color eye_color birth_year sex   gender
##   <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
## 1 R2-D2         96    32 <NA>       white, bl… red               33 none  mascu…
## 2 IG-88        200   140 none       metal      red               15 none  mascu…
## 3 Wicket S…     88    20 brown      brown      brown              8 male  mascu…
## 4 Barriss …    166    50 black      yellow     blue              40 fema… femin…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

 

Si estamos en el segundo caso mencionado al inicio, querremos extraer filas simplemente por el lugar que ocupan, sin hacer que dependa de ninguna condición, por ejemplo, la primera fila. Para ello haremos uso de la función slice().

# slice: extramos filas por índice de fila.
starwars %>% slice(1) # primera fila
## # A tibble: 1 × 14
##   name     height  mass hair_color skin_color eye_color birth_year sex   gender 
##   <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr>  
## 1 Luke Sk…    172    77 blond      fair       blue              19 male  mascul…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

Podemos extraer varias a la vez, por ejemplo, filtrando las filas quinta, sexta, séptima, octava, novena y décima.

# slice: extramos filas por índice de fila.
starwars %>% slice(5:10) # filas de la 5 a la 10
## # A tibble: 6 × 14
##   name     height  mass hair_color  skin_color eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
## 1 Leia Or…    150    49 brown       light      brown             19 fema… femin…
## 2 Owen La…    178   120 brown, grey light      blue              52 male  mascu…
## 3 Beru Wh…    165    75 brown       light      blue              47 fema… femin…
## 4 R5-D4        97    32 <NA>        white, red red               NA none  mascu…
## 5 Biggs D…    183    84 black       light      brown             24 male  mascu…
## 6 Obi-Wan…    182    77 auburn, wh… fair       blue-gray         57 male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

Como sucedía cuando trabajábamos con vectores, también podemos combinar los índices para extraer, por ejemplo solo las filas pares, o filas concretas separadas por los espacios que queramos.

starwars %>% slice(seq(2, 10, by = 2)) # filas pares hasta la décima
## # A tibble: 5 × 14
##   name     height  mass hair_color  skin_color eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
## 1 C-3PO       167    75 <NA>        gold       yellow         112   none  mascu…
## 2 Darth V…    202   136 none        white      yellow          41.9 male  mascu…
## 3 Owen La…    178   120 brown, grey light      blue            52   male  mascu…
## 4 R5-D4        97    32 <NA>        white, red red             NA   none  mascu…
## 5 Obi-Wan…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>
starwars %>% slice(c(3, 4, 10, 20, 33)) # filas 3, 4, 10, 20 y 33
## # A tibble: 5 × 14
##   name     height  mass hair_color  skin_color eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
## 1 R2-D2        96    32 <NA>        white, bl… red             33   none  mascu…
## 2 Darth V…    202   136 none        white      yellow          41.9 male  mascu…
## 3 Obi-Wan…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## 4 Palpati…    170    75 grey        pale       yellow          82   male  mascu…
## 5 Finis V…    170    NA blond       fair       blue            91   male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

Disponemos además de opciones por defecto para directamente extraer las primeras o últimas filas con slice_head() y slice_tail().

# Podemos extraer directamente las primeras o últimas filas
starwars %>% slice_head(n = 5) # 5 primeras filas 
## # A tibble: 5 × 14
##   name     height  mass hair_color skin_color  eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr> 
## 1 Luke Sk…    172    77 blond      fair        blue            19   male  mascu…
## 2 C-3PO       167    75 <NA>       gold        yellow         112   none  mascu…
## 3 R2-D2        96    32 <NA>       white, blue red             33   none  mascu…
## 4 Darth V…    202   136 none       white       yellow          41.9 male  mascu…
## 5 Leia Or…    150    49 brown      light       brown           19   fema… femin…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>
starwars %>% slice_tail(n = 2) # 2 últimas filas 
## # A tibble: 2 × 14
##   name     height  mass hair_color skin_color eye_color birth_year sex    gender
##   <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr>  <chr> 
## 1 Captain…     NA    NA unknown    unknown    unknown           NA <NA>   <NA>  
## 2 Padmé A…    165    45 brown      light      brown             46 female femin…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

 

La tercera opción habitual es querer extraer un número de filas pero AL AZAR, pudiendo hacerlo sin reemplazamiento (una vez extraída, no se puede volver a extraer) o con reemplazamiento (con replace = TRUE).

# También podemos hacer una extracción al azar de filas
starwars %>% slice_sample(n = 5) # 5 al azar
## # A tibble: 5 × 14
##   name     height  mass hair_color skin_color  eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr> 
## 1 Ackbar      180    83 none       brown mott… orange            41 male  mascu…
## 2 Barriss…    166    50 black      yellow      blue              40 fema… femin…
## 3 Tarfful     234   136 brown      brown       blue              NA male  mascu…
## 4 Jek Ton…    180   110 brown      fair        blue              NA male  mascu…
## 5 Palpati…    170    75 grey       pale        yellow            82 male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>
# 100 con reemplazamiento
# (si fuera sin reemplazamiento, daría error)
starwars %>% slice_sample(n = 100, replace = TRUE) 
## # A tibble: 100 × 14
##    name    height  mass hair_color skin_color  eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr> 
##  1 IG-88      200   140 none       metal       red             15   none  mascu…
##  2 Mon Mo…    150    NA auburn     fair        blue            48   fema… femin…
##  3 Biggs …    183    84 black      light       brown           24   male  mascu…
##  4 Anakin…    188    84 blond      fair        blue            41.9 male  mascu…
##  5 Jocast…    167    NA white      fair        blue            NA   fema… femin…
##  6 Nute G…    191    90 none       mottled gr… red             NA   male  mascu…
##  7 BB8         NA    NA none       none        black           NA   none  mascu…
##  8 Gasgano    122    NA none       white, blue black           NA   male  mascu…
##  9 Dexter…    198   102 none       brown       yellow          NA   male  mascu…
## 10 Poe Da…     NA    NA brown      light       brown           NA   male  mascu…
## # … with 90 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Por último, tenemos slice_min() y slice_max() que nos permite extraer filas en función del mínimo y máximo de una variable. Por defecto, si hay empate, mostrará todas las filas (aunque supere el número n indicado), salvo que with_ties = FALSE, que decidirá en caso de empate cual mostrarte. Por ejemplo, vamos a filtrar con slice_min(height, n = 5) los 5 personasjes más bajitos, y con starwars %>% slice_max(mass, n = 7) los 7 personajes más pesados.

# Podemos extraer filas en función del mín/máx de una variable
starwars %>% slice_min(height, n = 5) # los 5 más bajitos
## # A tibble: 6 × 14
##   name      height  mass hair_color skin_color eye_color birth_year sex   gender
##   <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
## 1 Yoda          66    17 white      green      brown            896 male  mascu…
## 2 Ratts Ty…     79    15 none       grey, blue unknown           NA male  mascu…
## 3 Wicket S…     88    20 brown      brown      brown              8 male  mascu…
## 4 Dud Bolt      94    45 none       blue, grey yellow            NA male  mascu…
## 5 R2-D2         96    32 <NA>       white, bl… red               33 none  mascu…
## 6 R4-P17        96    NA none       silver, r… red, blue         NA none  femin…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>
starwars %>% # los 5 más bajitos (sin empates, exactamente 5)
  slice_min(height, n = 5, with_ties = FALSE) 
## # A tibble: 5 × 14
##   name      height  mass hair_color skin_color eye_color birth_year sex   gender
##   <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
## 1 Yoda          66    17 white      green      brown            896 male  mascu…
## 2 Ratts Ty…     79    15 none       grey, blue unknown           NA male  mascu…
## 3 Wicket S…     88    20 brown      brown      brown              8 male  mascu…
## 4 Dud Bolt      94    45 none       blue, grey yellow            NA male  mascu…
## 5 R2-D2         96    32 <NA>       white, bl… red               33 none  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>
starwars %>% slice_max(mass, n = 7) # los 7 más pesados
## # A tibble: 7 × 14
##   name    height  mass hair_color  skin_color  eye_color birth_year sex   gender
##   <chr>    <int> <dbl> <chr>       <chr>       <chr>          <dbl> <chr> <chr> 
## 1 Jabba …    175  1358 <NA>        green-tan,… orange         600   herm… mascu…
## 2 Grievo…    216   159 none        brown, whi… green, y…       NA   male  mascu…
## 3 IG-88      200   140 none        metal       red             15   none  mascu…
## 4 Darth …    202   136 none        white       yellow          41.9 male  mascu…
## 5 Tarfful    234   136 brown       brown       blue            NA   male  mascu…
## 6 Owen L…    178   120 brown, grey light       blue            52   male  mascu…
## 7 Bossk      190   113 none        green       red             53   male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

19.2.2 Reordenar filas (arrange)

Otra operación habitual en las filas es ordenarlas en función del valor de alguna de las variables. Para ello tenemos la función arrange(), que usaremos pasándole como argumento el nombre de la variable que usaremos para la ordenación. Por ejemplo, vamos a ordenar nuestra tabla de personajes por altura, de bajitos a altos.

# Con arrange ordenamos en base al orden de la variable que introduzcamos
starwars %>% arrange(height) # de bajitos a altos, yoda al poder
## # A tibble: 87 × 14
##    name     height  mass hair_color skin_color eye_color birth_year sex   gender
##    <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Yoda         66    17 white      green      brown            896 male  mascu…
##  2 Ratts T…     79    15 none       grey, blue unknown           NA male  mascu…
##  3 Wicket …     88    20 brown      brown      brown              8 male  mascu…
##  4 Dud Bolt     94    45 none       blue, grey yellow            NA male  mascu…
##  5 R2-D2        96    32 <NA>       white, bl… red               33 none  mascu…
##  6 R4-P17       96    NA none       silver, r… red, blue         NA none  femin…
##  7 R5-D4        97    32 <NA>       white, red red               NA none  mascu…
##  8 Sebulba     112    40 none       grey, red  orange            NA male  mascu…
##  9 Gasgano     122    NA none       white, bl… black             NA male  mascu…
## 10 Watto       137    NA black      blue, grey yellow            NA male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Por defecto la ordenación es de menor a mayor pero podemos invertirlo usando desc().

# Por defecto lo hace ascendente pero podemos cambiarlo
starwars %>% arrange(desc(height))
## # A tibble: 87 × 14
##    name    height  mass hair_color skin_color  eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>      <chr>       <chr>          <dbl> <chr> <chr> 
##  1 Yarael…    264    NA none       white       yellow          NA   male  mascu…
##  2 Tarfful    234   136 brown      brown       blue            NA   male  mascu…
##  3 Lama Su    229    88 none       grey        black           NA   male  mascu…
##  4 Chewba…    228   112 brown      unknown     blue           200   male  mascu…
##  5 Roos T…    224    82 none       grey        orange          NA   male  mascu…
##  6 Grievo…    216   159 none       brown, whi… green, y…       NA   male  mascu…
##  7 Taun We    213    NA none       grey        black           NA   fema… femin…
##  8 Rugor …    206    NA none       green       orange          NA   male  mascu…
##  9 Tion M…    206    80 none       grey        black           NA   male  mascu…
## 10 Darth …    202   136 none       white       yellow          41.9 male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

La ordenación también puede realizarse en base al valor de dos o más variables: ordenará las filas en base a la primera, en caso de empate usará la segunda, en caso de empate la tercera, y así sucesivamente. Vamos a ordenar los personajes por altura y, luego, por peso.

# Podemos combinar varios criterios: ordenados de bajitos a altos,
# y en caso de empate, de pesados a ligeros. Un dato NA va siempre al final
starwars %>% arrange(height, desc(mass))
## # A tibble: 87 × 14
##    name     height  mass hair_color skin_color eye_color birth_year sex   gender
##    <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Yoda         66    17 white      green      brown            896 male  mascu…
##  2 Ratts T…     79    15 none       grey, blue unknown           NA male  mascu…
##  3 Wicket …     88    20 brown      brown      brown              8 male  mascu…
##  4 Dud Bolt     94    45 none       blue, grey yellow            NA male  mascu…
##  5 R2-D2        96    32 <NA>       white, bl… red               33 none  mascu…
##  6 R4-P17       96    NA none       silver, r… red, blue         NA none  femin…
##  7 R5-D4        97    32 <NA>       white, red red               NA none  mascu…
##  8 Sebulba     112    40 none       grey, red  orange            NA male  mascu…
##  9 Gasgano     122    NA none       white, bl… black             NA male  mascu…
## 10 Watto       137    NA black      blue, grey yellow            NA male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

 

WARNING: valores ausentes

Si te fijas los valores ausentes van siempre al final de la ordenación. Luego veremos como eliminarlos si quisiéramos.

 

Como estarás imaginando podemos combinar varias acciones en pocas líneas, filtrando a la vez que ordenamos el filtro resultante. Como ejemplo vamos a seleccionar solo los personajes que sean humanos, hombres y de ojos marrones, para después ordernarlos en altura descendente y peso ascendente.

# Podemos combinar varias acciones en pocas líneas
starwars %>%
  filter(eye_color == "brown",
         species == "Human", sex == "male") %>%
  arrange(height, desc(mass))
## # A tibble: 12 × 14
##    name     height  mass hair_color skin_color eye_color birth_year sex   gender
##    <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Lando C…    177  79   black      dark       brown           31   male  mascu…
##  2 Han Solo    180  80   brown      fair       brown           29   male  mascu…
##  3 Biggs D…    183  84   black      light      brown           24   male  mascu…
##  4 Jango F…    183  79   black      tan        brown           66   male  mascu…
##  5 Boba Fe…    183  78.2 black      fair       brown           31.5 male  mascu…
##  6 Gregar …    185  85   black      dark       brown           NA   male  mascu…
##  7 Mace Wi…    188  84   none       dark       brown           72   male  mascu…
##  8 Raymus …    188  79   brown      light      brown           NA   male  mascu…
##  9 Bail Pr…    191  NA   black      tan        brown           67   male  mascu…
## 10 Dooku       193  80   white      fair       brown          102   male  mascu…
## 11 Arvel C…     NA  NA   brown      fair       brown           NA   male  mascu…
## 12 Poe Dam…     NA  NA   brown      light      brown           NA   male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

19.2.3 Eliminar filas y duplicados (slice y distinct)

La misma lógica que hemos usado para seleccionar filas podemos usarla para eliminar filas con slice(), simplemente negando la condición de filtrado. Por ejemplo, vamos a eliminar las 5 primeras filas.

# Eliminamos por índices
starwars %>% slice(-(1:5)) # eliminamos las 5 primeras filas
## # A tibble: 82 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  2 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  3 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  4 Biggs …    183    84 black       light      brown           24   male  mascu…
##  5 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
##  6 Anakin…    188    84 blond       fair       blue            41.9 male  mascu…
##  7 Wilhuf…    180    NA auburn, gr… fair       blue            64   male  mascu…
##  8 Chewba…    228   112 brown       unknown    blue           200   male  mascu…
##  9 Han So…    180    80 brown       fair       brown           29   male  mascu…
## 10 Greedo     173    74 <NA>        green      black           44   male  mascu…
## # … with 72 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Otra opción a la hora de eliminar filas es eliminar las filas duplicadas (o filas con valores duplicados en alguna columna). Para ello deberemos aplicar la función distinct(), pasándole como argumentos el nombre de las variables que usaremos para quitar duplicados, por ejemplo, aquellos personajes con igual par de color de pelo y ojos.

# Eliminamos duplicados
starwars %>% # Eliminamos registros con igual par (color_pelo, color_ojos)
  distinct(hair_color, eye_color)
## # A tibble: 35 × 2
##    hair_color    eye_color
##    <chr>         <chr>    
##  1 blond         blue     
##  2 <NA>          yellow   
##  3 <NA>          red      
##  4 none          yellow   
##  5 brown         brown    
##  6 brown, grey   blue     
##  7 brown         blue     
##  8 black         brown    
##  9 auburn, white blue-gray
## 10 auburn, grey  blue     
## # … with 25 more rows

Si te fijas además nos ha extraído solo las dos columnas en base a las cuales hemos eliminado duplicados. Si queremos que nos mantenga toda la tabla deberemos explicitarlo con .keep_all = TRUE.

# Eliminamos duplicados
starwars %>% # Eliminamos registros con igual par (color_pelo, color_ojos)
  distinct(hair_color, eye_color,
           .keep_all = TRUE) # .keep_all = TRUE mantiene todas columnas
## # A tibble: 35 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 Biggs …    183    84 black       light      brown           24   male  mascu…
##  9 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## 10 Wilhuf…    180    NA auburn, gr… fair       blue            64   male  mascu…
## # … with 25 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Si no incluimos ningún nombre de columna nos eliminará solo los registros que tengan TODOS los campos duplicados.

# Duplicamos el conjunto para probarlo
duplicado_starwars <- rbind(starwars, starwars)
dim(duplicado_starwars)
## [1] 174  14
# Eliminamos duplicados (filas exactamente iguales)
duplicado_starwars %>% distinct()
## # A tibble: 87 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>
dim(duplicado_starwars %>% distinct())
## [1] 87 14

19.2.4 Añadir filas (bind_rows)

No suele ser habitual ya que normalmente los datos los tendremos cargados, y nuestro objetivo será depurar (que implicará tener las mismas o menos filas). Pero si quisiéramos añadir un nuevo registro manualmente, podremos hacerlo con bind_rows(), asegurándonos que las variables en el nuevo registro son las mismas que en el original. Vamos a añadir al fichero original los 3 primeros registros (al final).

starwars_nuevo <- bind_rows(starwars, starwars[1:3, ])
dim(starwars)
## [1] 87 14
dim(starwars_nuevo)
## [1] 90 14
starwars_nuevo
## # A tibble: 90 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 80 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

19.3 Operaciones con columnas

Ya hemos visto algunas opciones para manejar filas. Pasemos a manipular columnas.

19.3.1 Seleccionar columnas (select)

La opción más sencilla para seleccionar variables/columnas es usar la función select(), pasándole como argumentos los nombres de columnas (¡SIN COMILLAS!) qque ueremos seleccionar, por ejemplo, color de pelo.

# select: columnas a seleccionar sin comillas
# seleccionamos solo 1 columna: color de pelo
starwars %>%  select(hair_color)
## # A tibble: 87 × 1
##    hair_color   
##    <chr>        
##  1 blond        
##  2 <NA>         
##  3 <NA>         
##  4 none         
##  5 brown        
##  6 brown, grey  
##  7 brown        
##  8 <NA>         
##  9 black        
## 10 auburn, white
## # … with 77 more rows

Podemos seleccionar varias a la vez concatenando sus nombres, por ejemplo, sellecionando las variables de color de pelo, piel y ojos.

# seleccionamos solo 3 columnas: pelo, piel y ojos
starwars %>% select(hair_color, skin_color, eye_color)
## # A tibble: 87 × 3
##    hair_color    skin_color  eye_color
##    <chr>         <chr>       <chr>    
##  1 blond         fair        blue     
##  2 <NA>          gold        yellow   
##  3 <NA>          white, blue red      
##  4 none          white       yellow   
##  5 brown         light       brown    
##  6 brown, grey   light       blue     
##  7 brown         light       blue     
##  8 <NA>          white, red  red      
##  9 black         light       brown    
## 10 auburn, white fair        blue-gray
## # … with 77 more rows

Si las columnas a seleccionar son consecutivas, podemos indicarle que las seleccionemos desde una columna inicial hasta una columna inicial pasando por todas con :.

# desde nombre hasta año nacim.
starwars %>% select(name:birth_year)
## # A tibble: 87 × 7
##    name               height  mass hair_color    skin_color eye_color birth_year
##    <chr>               <int> <dbl> <chr>         <chr>      <chr>          <dbl>
##  1 Luke Skywalker        172    77 blond         fair       blue            19  
##  2 C-3PO                 167    75 <NA>          gold       yellow         112  
##  3 R2-D2                  96    32 <NA>          white, bl… red             33  
##  4 Darth Vader           202   136 none          white      yellow          41.9
##  5 Leia Organa           150    49 brown         light      brown           19  
##  6 Owen Lars             178   120 brown, grey   light      blue            52  
##  7 Beru Whitesun lars    165    75 brown         light      blue            47  
##  8 R5-D4                  97    32 <NA>          white, red red             NA  
##  9 Biggs Darklighter     183    84 black         light      brown           24  
## 10 Obi-Wan Kenobi        182    77 auburn, white fair       blue-gray       57  
## # … with 77 more rows

Las columnas también las podemos seleccionar a través de expresiones regulares, por ejemplo, seleccionando solo aquellas columnas que compartan un sufijo común en el nombre, con ends_with() dentro de órdenes como select()

# Podemos seleccionar columnas por sufijo y prefijo
starwars %>% select(ends_with("color")) # acaban en "color"
## # A tibble: 87 × 3
##    hair_color    skin_color  eye_color
##    <chr>         <chr>       <chr>    
##  1 blond         fair        blue     
##  2 <NA>          gold        yellow   
##  3 <NA>          white, blue red      
##  4 none          white       yellow   
##  5 brown         light       brown    
##  6 brown, grey   light       blue     
##  7 brown         light       blue     
##  8 <NA>          white, red  red      
##  9 black         light       brown    
## 10 auburn, white fair        blue-gray
## # … with 77 more rows

De una manera similar podemos seleccionar columnas cuyo nombre empiecen o contengan alguna cadena de texto concreta, con starts_with() o contains().

starwars %>% select(starts_with("h")) # empiezan por h
## # A tibble: 87 × 3
##    height hair_color    homeworld
##     <int> <chr>         <chr>    
##  1    172 blond         Tatooine 
##  2    167 <NA>          Tatooine 
##  3     96 <NA>          Naboo    
##  4    202 none          Tatooine 
##  5    150 brown         Alderaan 
##  6    178 brown, grey   Tatooine 
##  7    165 brown         Tatooine 
##  8     97 <NA>          Tatooine 
##  9    183 black         Tatooine 
## 10    182 auburn, white Stewjon  
## # … with 77 more rows
starwars %>% select(contains("h")) # contienen la h
## # A tibble: 87 × 6
##    height hair_color    birth_year homeworld vehicles  starships
##     <int> <chr>              <dbl> <chr>     <list>    <list>   
##  1    172 blond               19   Tatooine  <chr [2]> <chr [2]>
##  2    167 <NA>               112   Tatooine  <chr [0]> <chr [0]>
##  3     96 <NA>                33   Naboo     <chr [0]> <chr [0]>
##  4    202 none                41.9 Tatooine  <chr [0]> <chr [1]>
##  5    150 brown               19   Alderaan  <chr [1]> <chr [0]>
##  6    178 brown, grey         52   Tatooine  <chr [0]> <chr [0]>
##  7    165 brown               47   Tatooine  <chr [0]> <chr [0]>
##  8     97 <NA>                NA   Tatooine  <chr [0]> <chr [0]>
##  9    183 black               24   Tatooine  <chr [0]> <chr [1]>
## 10    182 auburn, white       57   Stewjon   <chr [1]> <chr [5]>
## # … with 77 more rows

Todo lo que hemos usado para seleccionar lo podemos usar para eliminar columnas

starwars %>% # seleccionamos todas menos 3 columnas: pelo, piel y ojos
  select(-c(hair_color, skin_color, eye_color))
## # A tibble: 87 × 11
##    name    height  mass birth_year sex   gender homeworld species films vehicles
##    <chr>    <int> <dbl>      <dbl> <chr> <chr>  <chr>     <chr>   <lis> <list>  
##  1 Luke S…    172    77       19   male  mascu… Tatooine  Human   <chr… <chr [2…
##  2 C-3PO      167    75      112   none  mascu… Tatooine  Droid   <chr… <chr [0…
##  3 R2-D2       96    32       33   none  mascu… Naboo     Droid   <chr… <chr [0…
##  4 Darth …    202   136       41.9 male  mascu… Tatooine  Human   <chr… <chr [0…
##  5 Leia O…    150    49       19   fema… femin… Alderaan  Human   <chr… <chr [1…
##  6 Owen L…    178   120       52   male  mascu… Tatooine  Human   <chr… <chr [0…
##  7 Beru W…    165    75       47   fema… femin… Tatooine  Human   <chr… <chr [0…
##  8 R5-D4       97    32       NA   none  mascu… Tatooine  Droid   <chr… <chr [0…
##  9 Biggs …    183    84       24   male  mascu… Tatooine  Human   <chr… <chr [0…
## 10 Obi-Wa…    182    77       57   male  mascu… Stewjon   Human   <chr… <chr [1…
## # … with 77 more rows, and 1 more variable: starships <list>

Incluso nos permite seleccionar aquellas columnas que son del mismo tipo (imagina que necesitamos solo las columnas numéricas para calcular ciertas estadísticas) con where().

# Seleccionamos solo la columnas numéricas
starwars %>% select(where(is.numeric))
## # A tibble: 87 × 3
##    height  mass birth_year
##     <int> <dbl>      <dbl>
##  1    172    77       19  
##  2    167    75      112  
##  3     96    32       33  
##  4    202   136       41.9
##  5    150    49       19  
##  6    178   120       52  
##  7    165    75       47  
##  8     97    32       NA  
##  9    183    84       24  
## 10    182    77       57  
## # … with 77 more rows

19.3.2 Extraer columnas (pull)

También podemos no tanto seleccionar sino extraer columnas: las saca fuera de la tabla y las convierte en un vector fuera del tibble, con la función pull().

starwars %>% select(name)
## # A tibble: 87 × 1
##    name              
##    <chr>             
##  1 Luke Skywalker    
##  2 C-3PO             
##  3 R2-D2             
##  4 Darth Vader       
##  5 Leia Organa       
##  6 Owen Lars         
##  7 Beru Whitesun lars
##  8 R5-D4             
##  9 Biggs Darklighter 
## 10 Obi-Wan Kenobi    
## # … with 77 more rows
starwars %>% pull(name)
##  [1] "Luke Skywalker"        "C-3PO"                 "R2-D2"                
##  [4] "Darth Vader"           "Leia Organa"           "Owen Lars"            
##  [7] "Beru Whitesun lars"    "R5-D4"                 "Biggs Darklighter"    
## [10] "Obi-Wan Kenobi"        "Anakin Skywalker"      "Wilhuff Tarkin"       
## [13] "Chewbacca"             "Han Solo"              "Greedo"               
## [16] "Jabba Desilijic Tiure" "Wedge Antilles"        "Jek Tono Porkins"     
## [19] "Yoda"                  "Palpatine"             "Boba Fett"            
## [22] "IG-88"                 "Bossk"                 "Lando Calrissian"     
## [25] "Lobot"                 "Ackbar"                "Mon Mothma"           
## [28] "Arvel Crynyd"          "Wicket Systri Warrick" "Nien Nunb"            
## [31] "Qui-Gon Jinn"          "Nute Gunray"           "Finis Valorum"        
## [34] "Jar Jar Binks"         "Roos Tarpals"          "Rugor Nass"           
## [37] "Ric Olié"              "Watto"                 "Sebulba"              
## [40] "Quarsh Panaka"         "Shmi Skywalker"        "Darth Maul"           
## [43] "Bib Fortuna"           "Ayla Secura"           "Dud Bolt"             
## [46] "Gasgano"               "Ben Quadinaros"        "Mace Windu"           
## [49] "Ki-Adi-Mundi"          "Kit Fisto"             "Eeth Koth"            
## [52] "Adi Gallia"            "Saesee Tiin"           "Yarael Poof"          
## [55] "Plo Koon"              "Mas Amedda"            "Gregar Typho"         
## [58] "Cordé"                 "Cliegg Lars"           "Poggle the Lesser"    
## [61] "Luminara Unduli"       "Barriss Offee"         "Dormé"                
## [64] "Dooku"                 "Bail Prestor Organa"   "Jango Fett"           
## [67] "Zam Wesell"            "Dexter Jettster"       "Lama Su"              
## [70] "Taun We"               "Jocasta Nu"            "Ratts Tyerell"        
## [73] "R4-P17"                "Wat Tambor"            "San Hill"             
## [76] "Shaak Ti"              "Grievous"              "Tarfful"              
## [79] "Raymus Antilles"       "Sly Moore"             "Tion Medon"           
## [82] "Finn"                  "Rey"                   "Poe Dameron"          
## [85] "BB8"                   "Captain Phasma"        "Padmé Amidala"

Esta opción es útil cuando los valores de una variable lo vamos a sacar del flujo de datos, para ser usada en otros procesos (como predicción).

19.3.3 Renombrar (rename) y reordenar (relocate) columnas

A veces también podemos querer modificar la «metainformación» de la tabla, no modificando los datos sino renombrando el orden de las columnas. Para ello debremos usar la función rename() poniendo primero el nombre nuevo y luego el antiguo. Como ejemplo, vamos a traducir el nombre de las columnas name, height, mass a castellano.

# rename: renombrar columnas, primero el nuevo y luego el antiguo
starwars %>% rename(nombre = name, altura = height, peso = mass)
## # A tibble: 87 × 14
##    nombre   altura  peso hair_color skin_color eye_color birth_year sex   gender
##    <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke Sk…    172    77 blond      fair       blue            19   male  mascu…
##  2 C-3PO       167    75 <NA>       gold       yellow         112   none  mascu…
##  3 R2-D2        96    32 <NA>       white, bl… red             33   none  mascu…
##  4 Darth V…    202   136 none       white      yellow          41.9 male  mascu…
##  5 Leia Or…    150    49 brown      light      brown           19   fema… femin…
##  6 Owen La…    178   120 brown, gr… light      blue            52   male  mascu…
##  7 Beru Wh…    165    75 brown      light      blue            47   fema… femin…
##  8 R5-D4        97    32 <NA>       white, red red             NA   none  mascu…
##  9 Biggs D…    183    84 black      light      brown           24   male  mascu…
## 10 Obi-Wan…    182    77 auburn, w… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

De la misma manera podemos reordenar las columnas, indicando el nombre de columnas que queremos mover, y con .after y/o .before para indicar antes o después de que columnas queremos moverlas.

starwars %>% # altura y masa detrás de color de piel
  relocate(height, mass, .after = skin_color) 
## # A tibble: 87 × 14
##    name    hair_color  skin_color height  mass eye_color birth_year sex   gender
##    <chr>   <chr>       <chr>       <int> <dbl> <chr>          <dbl> <chr> <chr> 
##  1 Luke S… blond       fair          172    77 blue            19   male  mascu…
##  2 C-3PO   <NA>        gold          167    75 yellow         112   none  mascu…
##  3 R2-D2   <NA>        white, bl…     96    32 red             33   none  mascu…
##  4 Darth … none        white         202   136 yellow          41.9 male  mascu…
##  5 Leia O… brown       light         150    49 brown           19   fema… femin…
##  6 Owen L… brown, grey light         178   120 blue            52   male  mascu…
##  7 Beru W… brown       light         165    75 blue            47   fema… femin…
##  8 R5-D4   <NA>        white, red     97    32 red             NA   none  mascu…
##  9 Biggs … black       light         183    84 brown           24   male  mascu…
## 10 Obi-Wa… auburn, wh… fair          182    77 blue-gray       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>
starwars %>% # color de piel, pelo y ojos antes de peso
  relocate(hair_color, skin_color, eye_color,
           .before = mass) 
## # A tibble: 87 × 14
##    name    height hair_color  skin_color eye_color  mass birth_year sex   gender
##    <chr>    <int> <chr>       <chr>      <chr>     <dbl>      <dbl> <chr> <chr> 
##  1 Luke S…    172 blond       fair       blue         77       19   male  mascu…
##  2 C-3PO      167 <NA>        gold       yellow       75      112   none  mascu…
##  3 R2-D2       96 <NA>        white, bl… red          32       33   none  mascu…
##  4 Darth …    202 none        white      yellow      136       41.9 male  mascu…
##  5 Leia O…    150 brown       light      brown        49       19   fema… femin…
##  6 Owen L…    178 brown, grey light      blue        120       52   male  mascu…
##  7 Beru W…    165 brown       light      blue         75       47   fema… femin…
##  8 R5-D4       97 <NA>        white, red red          32       NA   none  mascu…
##  9 Biggs …    183 black       light      brown        84       24   male  mascu…
## 10 Obi-Wa…    182 auburn, wh… fair       blue-gray    77       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Otra opción es hacerlo con select(), indicándole el orden de las columnas manualmente, pudiendo hacer uso de everything() para incluir el resto de columnas no mencionadas anteriormente.

starwars %>% select(name, homeworld, everything())
## # A tibble: 87 × 14
##    name  homeworld height  mass hair_color skin_color eye_color birth_year sex  
##    <chr> <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr>
##  1 Luke… Tatooine     172    77 blond      fair       blue            19   male 
##  2 C-3PO Tatooine     167    75 <NA>       gold       yellow         112   none 
##  3 R2-D2 Naboo         96    32 <NA>       white, bl… red             33   none 
##  4 Dart… Tatooine     202   136 none       white      yellow          41.9 male 
##  5 Leia… Alderaan     150    49 brown      light      brown           19   fema…
##  6 Owen… Tatooine     178   120 brown, gr… light      blue            52   male 
##  7 Beru… Tatooine     165    75 brown      light      blue            47   fema…
##  8 R5-D4 Tatooine      97    32 <NA>       white, red red             NA   none 
##  9 Bigg… Tatooine     183    84 black      light      brown           24   male 
## 10 Obi-… Stewjon      182    77 auburn, w… fair       blue-gray       57   male 
## # … with 77 more rows, and 5 more variables: gender <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Muchas de las funciones vistas pueden ir acompañadas de at o o if, como select_at o rename_if, que nos permite hacer operaciones más finas y personalizadas. Por ejemplo, vamos a renombrar con rename_if() las columnas, pasándolas a mayúscula con toupper, pero solo aquellas que sean de tipo numérico.

starwars %>% rename_if(is.numeric, toupper)
## # A tibble: 87 × 14
##    name    HEIGHT  MASS hair_color  skin_color eye_color BIRTH_YEAR sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

19.4 Crear nuevas variables (mutate)

A veces no queremos modificar variables ya existentes sino crear nuevas variables/columnas en base a variables ya existentes. Para ello tenemos la opción de mutate() que nos permite crear una nueva variable en función de las existentes. Para empezar, vamos a crear una nueva variable height_m cuyo cálculo sea la altura en metros (la variable original height está en centímetros).

# Mutate: nos añade nuevas columnas usando funciones
# aplicadas a nuestras variables
starwars %>% mutate(height_m = height / 100) # altura en metros
## # A tibble: 87 × 15
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 6 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>, height_m <dbl>

Recuerda que para ver todas las columnas debes indicarle que quieres imprimir todas.

print(starwars %>% mutate(height_m = height / 100), width = Inf)
## # A tibble: 87 × 15
##    name               height  mass hair_color    skin_color  eye_color
##    <chr>               <int> <dbl> <chr>         <chr>       <chr>    
##  1 Luke Skywalker        172    77 blond         fair        blue     
##  2 C-3PO                 167    75 <NA>          gold        yellow   
##  3 R2-D2                  96    32 <NA>          white, blue red      
##  4 Darth Vader           202   136 none          white       yellow   
##  5 Leia Organa           150    49 brown         light       brown    
##  6 Owen Lars             178   120 brown, grey   light       blue     
##  7 Beru Whitesun lars    165    75 brown         light       blue     
##  8 R5-D4                  97    32 <NA>          white, red  red      
##  9 Biggs Darklighter     183    84 black         light       brown    
## 10 Obi-Wan Kenobi        182    77 auburn, white fair        blue-gray
##    birth_year sex    gender    homeworld species films     vehicles  starships
##         <dbl> <chr>  <chr>     <chr>     <chr>   <list>    <list>    <list>   
##  1       19   male   masculine Tatooine  Human   <chr [5]> <chr [2]> <chr [2]>
##  2      112   none   masculine Tatooine  Droid   <chr [6]> <chr [0]> <chr [0]>
##  3       33   none   masculine Naboo     Droid   <chr [7]> <chr [0]> <chr [0]>
##  4       41.9 male   masculine Tatooine  Human   <chr [4]> <chr [0]> <chr [1]>
##  5       19   female feminine  Alderaan  Human   <chr [5]> <chr [1]> <chr [0]>
##  6       52   male   masculine Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  7       47   female feminine  Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  8       NA   none   masculine Tatooine  Droid   <chr [1]> <chr [0]> <chr [0]>
##  9       24   male   masculine Tatooine  Human   <chr [1]> <chr [0]> <chr [1]>
## 10       57   male   masculine Stewjon   Human   <chr [6]> <chr [1]> <chr [5]>
##    height_m
##       <dbl>
##  1     1.72
##  2     1.67
##  3     0.96
##  4     2.02
##  5     1.5 
##  6     1.78
##  7     1.65
##  8     0.97
##  9     1.83
## 10     1.82
## # … with 77 more rows

Otra opción es quedarnos solo con las columnas nuevas creadas con transmute()

starwars %>% transmute(height_m = height / 100)
## # A tibble: 87 × 1
##    height_m
##       <dbl>
##  1     1.72
##  2     1.67
##  3     0.96
##  4     2.02
##  5     1.5 
##  6     1.78
##  7     1.65
##  8     0.97
##  9     1.83
## 10     1.82
## # … with 77 more rows

Si queremos añadir varias variables en función de las ya existentes podemos hacerlo ya que permite incorporar los datos de forma secuencial sin duplicar órdenes de mutate() para cada una. Además de pasar la altura a metros, vamos a calcular para cada personaje el IMC.

print(starwars %>%
  # Calculamos altura en metros y el IMC
  mutate(height_m = height / 100,
         BMI = mass / (height_m^2)), width = Inf)
## # A tibble: 87 × 16
##    name               height  mass hair_color    skin_color  eye_color
##    <chr>               <int> <dbl> <chr>         <chr>       <chr>    
##  1 Luke Skywalker        172    77 blond         fair        blue     
##  2 C-3PO                 167    75 <NA>          gold        yellow   
##  3 R2-D2                  96    32 <NA>          white, blue red      
##  4 Darth Vader           202   136 none          white       yellow   
##  5 Leia Organa           150    49 brown         light       brown    
##  6 Owen Lars             178   120 brown, grey   light       blue     
##  7 Beru Whitesun lars    165    75 brown         light       blue     
##  8 R5-D4                  97    32 <NA>          white, red  red      
##  9 Biggs Darklighter     183    84 black         light       brown    
## 10 Obi-Wan Kenobi        182    77 auburn, white fair        blue-gray
##    birth_year sex    gender    homeworld species films     vehicles  starships
##         <dbl> <chr>  <chr>     <chr>     <chr>   <list>    <list>    <list>   
##  1       19   male   masculine Tatooine  Human   <chr [5]> <chr [2]> <chr [2]>
##  2      112   none   masculine Tatooine  Droid   <chr [6]> <chr [0]> <chr [0]>
##  3       33   none   masculine Naboo     Droid   <chr [7]> <chr [0]> <chr [0]>
##  4       41.9 male   masculine Tatooine  Human   <chr [4]> <chr [0]> <chr [1]>
##  5       19   female feminine  Alderaan  Human   <chr [5]> <chr [1]> <chr [0]>
##  6       52   male   masculine Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  7       47   female feminine  Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  8       NA   none   masculine Tatooine  Droid   <chr [1]> <chr [0]> <chr [0]>
##  9       24   male   masculine Tatooine  Human   <chr [1]> <chr [0]> <chr [1]>
## 10       57   male   masculine Stewjon   Human   <chr [6]> <chr [1]> <chr [5]>
##    height_m   BMI
##       <dbl> <dbl>
##  1     1.72  26.0
##  2     1.67  26.9
##  3     0.96  34.7
##  4     2.02  33.3
##  5     1.5   21.8
##  6     1.78  37.9
##  7     1.65  27.5
##  8     0.97  34.0
##  9     1.83  25.1
## 10     1.82  23.2
## # … with 77 more rows

Como ves por defecto añade las columnas al final pero podemos reordenar las columnas como ya hemos visto.

starwars %>%
  mutate(height_m = height / 100,
         BMI = mass / (height_m^2)) %>%
  relocate(height_m, BMI, .after = name)
## # A tibble: 87 × 16
##    name   height_m   BMI height  mass hair_color skin_color eye_color birth_year
##    <chr>     <dbl> <dbl>  <int> <dbl> <chr>      <chr>      <chr>          <dbl>
##  1 Luke …     1.72  26.0    172    77 blond      fair       blue            19  
##  2 C-3PO      1.67  26.9    167    75 <NA>       gold       yellow         112  
##  3 R2-D2      0.96  34.7     96    32 <NA>       white, bl… red             33  
##  4 Darth…     2.02  33.3    202   136 none       white      yellow          41.9
##  5 Leia …     1.5   21.8    150    49 brown      light      brown           19  
##  6 Owen …     1.78  37.9    178   120 brown, gr… light      blue            52  
##  7 Beru …     1.65  27.5    165    75 brown      light      blue            47  
##  8 R5-D4      0.97  34.0     97    32 <NA>       white, red red             NA  
##  9 Biggs…     1.83  25.1    183    84 black      light      brown           24  
## 10 Obi-W…     1.82  23.2    182    77 auburn, w… fair       blue-gray       57  
## # … with 77 more rows, and 7 more variables: sex <chr>, gender <chr>,
## #   homeworld <chr>, species <chr>, films <list>, vehicles <list>,
## #   starships <list>

También podemos pasarle una función propia que queramos definir, no solo funciones de R. Vamos a definir la función que nos calcula el IMC, a la que llamaremos BMI_fun, que necesita solo de dos argumentos: la altura en metros y el peso.

BMI_fun <- function(m, h) {
  
  return(m / h^2)
}
BMI_fun(90, 1.6)
## [1] 35.15625

La función definida como BMI_fun() podemos ahora aplicarla dentro de mutate().

starwars %>%
  mutate(height_m = height / 100,
         BMI = BMI_fun(mass, height_m)) %>%
  # Las movemos al inicio (por defecto las mete al final)
  relocate(height_m, BMI, .after = name)
## # A tibble: 87 × 16
##    name   height_m   BMI height  mass hair_color skin_color eye_color birth_year
##    <chr>     <dbl> <dbl>  <int> <dbl> <chr>      <chr>      <chr>          <dbl>
##  1 Luke …     1.72  26.0    172    77 blond      fair       blue            19  
##  2 C-3PO      1.67  26.9    167    75 <NA>       gold       yellow         112  
##  3 R2-D2      0.96  34.7     96    32 <NA>       white, bl… red             33  
##  4 Darth…     2.02  33.3    202   136 none       white      yellow          41.9
##  5 Leia …     1.5   21.8    150    49 brown      light      brown           19  
##  6 Owen …     1.78  37.9    178   120 brown, gr… light      blue            52  
##  7 Beru …     1.65  27.5    165    75 brown      light      blue            47  
##  8 R5-D4      0.97  34.0     97    32 <NA>       white, red red             NA  
##  9 Biggs…     1.83  25.1    183    84 black      light      brown           24  
## 10 Obi-W…     1.82  23.2    182    77 auburn, w… fair       blue-gray       57  
## # … with 77 more rows, and 7 more variables: sex <chr>, gender <chr>,
## #   homeworld <chr>, species <chr>, films <list>, vehicles <list>,
## #   starships <list>

También se pueden aplicar funciones más complejas como la función map() del paquete purrr para manejo de listas: dado que las listas no se pueden vectorizar, esta función nos permite aplicar operaciones a listas, elemento a elemento de cada una de ellas. Veamos un ejemplo: imagina que queremos calcular el número de películas en las que sale cada personaje de la saga.

# Películas de los 3 primeros personajes
starwars$films[1:3]
## [[1]]
## [1] "The Empire Strikes Back" "Revenge of the Sith"    
## [3] "Return of the Jedi"      "A New Hope"             
## [5] "The Force Awakens"      
## 
## [[2]]
## [1] "The Empire Strikes Back" "Attack of the Clones"   
## [3] "The Phantom Menace"      "Revenge of the Sith"    
## [5] "Return of the Jedi"      "A New Hope"             
## 
## [[3]]
## [1] "The Empire Strikes Back" "Attack of the Clones"   
## [3] "The Phantom Menace"      "Revenge of the Sith"    
## [5] "Return of the Jedi"      "A New Hope"             
## [7] "The Force Awakens"

Las películas de cada personaje están en modo lista: no podíamos guardarlo en un data.frame ya que cada personaje ha podido participar en un número distinto (en una tabla, todas las columnas tienen la misma longitud). Para saber la cantidad de películas en las que ha participado el primer personaje basta con usar el comando length() (nos dará el número de elementos de la lista).

length(starwars$films[1])
## [1] 1

¿Cómo aplicar dicha a función a cada personaje? Con map() y la función a aplicar en cada elemento de la lista.

# Mapeamos la lista con length
n_films <- starwars$films %>% map(length)
n_films[1:5]
## [[1]]
## [1] 5
## 
## [[2]]
## [1] 6
## 
## [[3]]
## [1] 7
## 
## [[4]]
## [1] 4
## 
## [[5]]
## [1] 5

Si te fijas con los 5 primeros, lo que nos devuelve por defecto a su vez una lista, cuando a nosotros nos gustaría que nos devolviera un vector que poder incluir como columna. Para ello existen diversas funciones como map_chr(), map_dbl() para devolverlo en un formato concreto (caracter o numérico).

# Mapeamos la lista con length pero devolvemos un vector de números enteros
starwars$films %>% map_int(length)
##  [1] 5 6 7 4 5 3 3 1 1 6 3 2 5 4 1 3 3 1 5 5 3 1 1 2 1 2 1 1 1 1 1 3 1 2 1 1 1 2
## [39] 1 1 2 1 1 3 1 1 1 3 3 3 2 2 2 1 3 2 1 1 1 2 2 1 1 2 2 1 1 1 1 1 1 1 2 1 1 2
## [77] 1 1 2 2 1 1 1 1 1 1 3

Ya solo nos falta incorporar dicha operación a una nueva columna con mutate()

starwars %>%
  mutate("n_peliculas" = map_int(films, length)) %>%
  select(c(name, homeworld, n_peliculas))
## # A tibble: 87 × 3
##    name               homeworld n_peliculas
##    <chr>              <chr>           <int>
##  1 Luke Skywalker     Tatooine            5
##  2 C-3PO              Tatooine            6
##  3 R2-D2              Naboo               7
##  4 Darth Vader        Tatooine            4
##  5 Leia Organa        Alderaan            5
##  6 Owen Lars          Tatooine            3
##  7 Beru Whitesun lars Tatooine            3
##  8 R5-D4              Tatooine            1
##  9 Biggs Darklighter  Tatooine            1
## 10 Obi-Wan Kenobi     Stewjon             6
## # … with 77 more rows

19.4.1 Recategorizar columnas (cut y case_when)

Una operación también muy habitual es querer recategorizar nuestras variables: tenemos categorías ya existentes o variables numéricas que queremos convertir a categoría (se conocen como factores en R). Supongamos por ejemplo que queremos crear una categoría para cada registro en función de su altura: menos de 120, entre 120 y 180, y más de 180. Para ello podemos hacer uso de la función cut(), a la que en el argumento breaks le debemos indicar los «cortes» o saltos de la variable.

print(starwars %>%
        mutate("estat_categoria" =
                 cut(height,
                     breaks = c(-Inf, 120, 180, Inf))),
      width = Inf)
## # A tibble: 87 × 15
##    name               height  mass hair_color    skin_color  eye_color
##    <chr>               <int> <dbl> <chr>         <chr>       <chr>    
##  1 Luke Skywalker        172    77 blond         fair        blue     
##  2 C-3PO                 167    75 <NA>          gold        yellow   
##  3 R2-D2                  96    32 <NA>          white, blue red      
##  4 Darth Vader           202   136 none          white       yellow   
##  5 Leia Organa           150    49 brown         light       brown    
##  6 Owen Lars             178   120 brown, grey   light       blue     
##  7 Beru Whitesun lars    165    75 brown         light       blue     
##  8 R5-D4                  97    32 <NA>          white, red  red      
##  9 Biggs Darklighter     183    84 black         light       brown    
## 10 Obi-Wan Kenobi        182    77 auburn, white fair        blue-gray
##    birth_year sex    gender    homeworld species films     vehicles  starships
##         <dbl> <chr>  <chr>     <chr>     <chr>   <list>    <list>    <list>   
##  1       19   male   masculine Tatooine  Human   <chr [5]> <chr [2]> <chr [2]>
##  2      112   none   masculine Tatooine  Droid   <chr [6]> <chr [0]> <chr [0]>
##  3       33   none   masculine Naboo     Droid   <chr [7]> <chr [0]> <chr [0]>
##  4       41.9 male   masculine Tatooine  Human   <chr [4]> <chr [0]> <chr [1]>
##  5       19   female feminine  Alderaan  Human   <chr [5]> <chr [1]> <chr [0]>
##  6       52   male   masculine Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  7       47   female feminine  Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  8       NA   none   masculine Tatooine  Droid   <chr [1]> <chr [0]> <chr [0]>
##  9       24   male   masculine Tatooine  Human   <chr [1]> <chr [0]> <chr [1]>
## 10       57   male   masculine Stewjon   Human   <chr [6]> <chr [1]> <chr [5]>
##    estat_categoria
##    <fct>          
##  1 (120,180]      
##  2 (120,180]      
##  3 (-Inf,120]     
##  4 (180, Inf]     
##  5 (120,180]      
##  6 (120,180]      
##  7 (120,180]      
##  8 (-Inf,120]     
##  9 (180, Inf]     
## 10 (180, Inf]     
## # … with 77 more rows

Recuerda que si quieres mostrar solo las nuevas columnas creadas puedes usar transmute()

starwars %>%
  transmute("estat_categoria" =
              cut(height, breaks = c(-Inf, 120, 180, Inf)))
## # A tibble: 87 × 1
##    estat_categoria
##    <fct>          
##  1 (120,180]      
##  2 (120,180]      
##  3 (-Inf,120]     
##  4 (180, Inf]     
##  5 (120,180]      
##  6 (120,180]      
##  7 (120,180]      
##  8 (-Inf,120]     
##  9 (180, Inf]     
## 10 (180, Inf]     
## # … with 77 more rows

Si queremos que la categoría tenga un nombre explícito (y no el intervalo en sí), podemos indicárselo en labels = el vector de nombres de las categorías.

starwars %>%
  transmute("estat_categoria" = cut(height, breaks = c(-Inf, 120, 180, Inf),
                                    labels = c("bajos", "medios", "altos")))
## # A tibble: 87 × 1
##    estat_categoria
##    <fct>          
##  1 medios         
##  2 medios         
##  3 bajos          
##  4 altos          
##  5 medios         
##  6 medios         
##  7 medios         
##  8 bajos          
##  9 altos          
## 10 altos          
## # … with 77 more rows

Otra opción es hacerlo con la función factor que nos transforma una variable en categoría.

# Vamos a categorizar en bajitos a los que miden menos de 180 cm
starwars %>%
  mutate(talla = factor(height < 180, labels = c("bajos", "altos")))
## # A tibble: 87 × 15
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 6 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>, talla <fct>

Otra opción para no encadenar if-else anidados es case_when, en el que le podemos indicar que valores asignamos en función de condiciones. Vamos a crearuna variable nueva altura

  • Si height > 180 –> serán "altos"
  • Si no se cumple lo anterior y height > 120 –> serán "bajos"
  • Si no se cumple lo anterior y height > 0 –> serán "enanos"
  • Si no se cumple lo anterior –> serán "ausentes"
starwars %>%
  transmute(altura =
              case_when(height > 180 ~ "altos",
                        height > 120 ~ "bajos",
                        height > 0 ~ "enanos",
                        TRUE ~ "ausentes"))
## # A tibble: 87 × 1
##    altura
##    <chr> 
##  1 bajos 
##  2 bajos 
##  3 enanos
##  4 altos 
##  5 bajos 
##  6 bajos 
##  7 bajos 
##  8 enanos
##  9 altos 
## 10 altos 
## # … with 77 more rows

19.5 Datos ausentes y outliers (drop_na y mutate)

Como ya vimos en Tipos de datos I: vectores, los datos ausentes en R pueden venir representados por valores NA o por valores NaN (en realidad este no sería ausente siendo estrictos, es simplemente un resultado no numérico dentro de los reales). ¿Qué hacer con dichos valores?

Una primera opción puede ser eliminar los registros que contengan campos ausentes en alguna de sus variables, haciendo uso de la función drop_na().

starwars %>% drop_na()
## # A tibble: 6 × 14
##   name     height  mass hair_color  skin_color eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
## 1 Luke Sk…    172    77 blond       fair       blue            19   male  mascu…
## 2 Obi-Wan…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## 3 Anakin …    188    84 blond       fair       blue            41.9 male  mascu…
## 4 Chewbac…    228   112 brown       unknown    blue           200   male  mascu…
## 5 Wedge A…    170    77 brown       fair       hazel           21   male  mascu…
## 6 Darth M…    175    80 none        red        yellow          54   male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

Como ves solo 6 de los 87 registros tienen todos los campos completos. Como quizás no todos los campos sean igual de importantes en nuestros, podemos indicarle que nos elimine aquellos registros que tengan datos ausentes algunos campos en concreto (o una coelcción de ellos)

starwars %>% drop_na(mass, height, sex, gender, birth_year)
## # A tibble: 36 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 Biggs …    183    84 black       light      brown           24   male  mascu…
##  9 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## 10 Anakin…    188    84 blond       fair       blue            41.9 male  mascu…
## # … with 26 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Otra opción es imputar su valor en el caso de variables cuantitativas por, por ejemplo, la media de la variable sin datos ausentes. Con across() podremos seleccionar todas las columnas que cumplan ciertas condiciones que le indiquemos, para después aplicar la función.

starwars %>%
  # Variables cuanti
  mutate(across(where(is.numeric),
                ~replace(.x, is.na(.x), mean(.x, na.rm = TRUE))))
## # A tibble: 87 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <dbl> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             87.6 none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

 

Muchas veces tendremos variables con valores muy alejados de la centralidad, centralidad normalmente entendida como media o mediana, valores muy extremos (conocidos como valores atípicos o outliers).

  • Respecto a la mediana/percentiles (iqr): una definición de outlier muy habitual es definirlo como un valor muy alejado del primer/tercer cuartil. La definición que se suele usar es que el dato sea interior/superior al primer/tercer cuartil una amplitud de X veces el rango intercuartílico (conocido como IQR, calculado como el tercer cuartil menos el primer cuartil) por debajo/encima.

  • Respecto a la media (gesd): el criterio utilizado suele ser el conocido como GESD (Generlized Extreme Studentized Deviate Test), un contraste de hipótesis cuya hipótesis nula es que no hay atípicos en los datos. Puedes ver la formulación matemática en https://www.itl.nist.gov/div898/handbook/eda/section3/eda35h3.htm

Para su localización automática usaremos el paquete anomalize, y la función homónima de dicho paquete, indicándole la columna individual en la que queremos detectar outliers y el método a usar. Con el método method = "gesd" realizaremos el contraste dato a dato de forma secuencial (si hay outlier lo quita individualmente y vuelve a ejecutarlo). Con method = "iqr", ese factor X que nos distanciamos del primer y tercer cuartil es calculado como 0.15/alpha, de forma que con alpha = 0.05 (por ejemplo), dicho factor sería X = 3. Es MUY IMPORTANTE quitar antes los valores ausentes (para que pueda ejecutarse correctamente).

library(anomalize)
# Importante: quitar antes missings
starwars %>% drop_na(mass) %>% anomalize(mass, method = "gesd")
## # A tibble: 59 × 17
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 49 more rows, and 8 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>, mass_l1 <dbl>,
## #   mass_l2 <dbl>, anomaly <chr>

En las columnas mass_l1 y mass_l2 se nos han guardado los límites para considerar o no outlier, y en anomaly tendremos si el valor es un outlier o no. Podemos renombrar dicha columna y recategorizarlo con TRUE/FALSE

print(starwars %>%
        drop_na(mass) %>%
        anomalize(mass, method = "gesd") %>%
        rename(outlier = anomaly) %>%
        # Eliminamos la variables auxiliares creadas
        select(-c(mass_l1, mass_l2)) %>%
        mutate(outlier = (outlier == "Yes")), width = Inf)
## # A tibble: 59 × 15
##    name               height  mass hair_color    skin_color  eye_color
##    <chr>               <int> <dbl> <chr>         <chr>       <chr>    
##  1 Luke Skywalker        172    77 blond         fair        blue     
##  2 C-3PO                 167    75 <NA>          gold        yellow   
##  3 R2-D2                  96    32 <NA>          white, blue red      
##  4 Darth Vader           202   136 none          white       yellow   
##  5 Leia Organa           150    49 brown         light       brown    
##  6 Owen Lars             178   120 brown, grey   light       blue     
##  7 Beru Whitesun lars    165    75 brown         light       blue     
##  8 R5-D4                  97    32 <NA>          white, red  red      
##  9 Biggs Darklighter     183    84 black         light       brown    
## 10 Obi-Wan Kenobi        182    77 auburn, white fair        blue-gray
##    birth_year sex    gender    homeworld species films     vehicles  starships
##         <dbl> <chr>  <chr>     <chr>     <chr>   <list>    <list>    <list>   
##  1       19   male   masculine Tatooine  Human   <chr [5]> <chr [2]> <chr [2]>
##  2      112   none   masculine Tatooine  Droid   <chr [6]> <chr [0]> <chr [0]>
##  3       33   none   masculine Naboo     Droid   <chr [7]> <chr [0]> <chr [0]>
##  4       41.9 male   masculine Tatooine  Human   <chr [4]> <chr [0]> <chr [1]>
##  5       19   female feminine  Alderaan  Human   <chr [5]> <chr [1]> <chr [0]>
##  6       52   male   masculine Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  7       47   female feminine  Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  8       NA   none   masculine Tatooine  Droid   <chr [1]> <chr [0]> <chr [0]>
##  9       24   male   masculine Tatooine  Human   <chr [1]> <chr [0]> <chr [1]>
## 10       57   male   masculine Stewjon   Human   <chr [6]> <chr [1]> <chr [5]>
##    outlier
##    <lgl>  
##  1 FALSE  
##  2 FALSE  
##  3 TRUE   
##  4 TRUE   
##  5 FALSE  
##  6 TRUE   
##  7 FALSE  
##  8 FALSE  
##  9 FALSE  
## 10 FALSE  
## # … with 49 more rows

Dicha variable auxiliar la podremos usar para decidir si incluir o no el registro en futuros pasos, pero también podemos usarla para imputarle un valor (por ejemplo, la media).

print(starwars %>%
        drop_na(mass) %>%
        anomalize(mass, method = "gesd") %>%
        rename(outlier = anomaly) %>%
        # Eliminamos la variables auxiliares creadas
        select(-c(mass_l1, mass_l2)) %>%
        mutate(outlier = (outlier == "Yes"),
               mass = ifelse(outlier, mean(mass), mass)),
      width = Inf)
## # A tibble: 59 × 15
##    name               height  mass hair_color    skin_color  eye_color
##    <chr>               <int> <dbl> <chr>         <chr>       <chr>    
##  1 Luke Skywalker        172  77   blond         fair        blue     
##  2 C-3PO                 167  75   <NA>          gold        yellow   
##  3 R2-D2                  96  97.3 <NA>          white, blue red      
##  4 Darth Vader           202  97.3 none          white       yellow   
##  5 Leia Organa           150  49   brown         light       brown    
##  6 Owen Lars             178  97.3 brown, grey   light       blue     
##  7 Beru Whitesun lars    165  75   brown         light       blue     
##  8 R5-D4                  97  32   <NA>          white, red  red      
##  9 Biggs Darklighter     183  84   black         light       brown    
## 10 Obi-Wan Kenobi        182  77   auburn, white fair        blue-gray
##    birth_year sex    gender    homeworld species films     vehicles  starships
##         <dbl> <chr>  <chr>     <chr>     <chr>   <list>    <list>    <list>   
##  1       19   male   masculine Tatooine  Human   <chr [5]> <chr [2]> <chr [2]>
##  2      112   none   masculine Tatooine  Droid   <chr [6]> <chr [0]> <chr [0]>
##  3       33   none   masculine Naboo     Droid   <chr [7]> <chr [0]> <chr [0]>
##  4       41.9 male   masculine Tatooine  Human   <chr [4]> <chr [0]> <chr [1]>
##  5       19   female feminine  Alderaan  Human   <chr [5]> <chr [1]> <chr [0]>
##  6       52   male   masculine Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  7       47   female feminine  Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  8       NA   none   masculine Tatooine  Droid   <chr [1]> <chr [0]> <chr [0]>
##  9       24   male   masculine Tatooine  Human   <chr [1]> <chr [0]> <chr [1]>
## 10       57   male   masculine Stewjon   Human   <chr [6]> <chr [1]> <chr [5]>
##    outlier
##    <lgl>  
##  1 FALSE  
##  2 FALSE  
##  3 TRUE   
##  4 TRUE   
##  5 FALSE  
##  6 TRUE   
##  7 FALSE  
##  8 FALSE  
##  9 FALSE  
## 10 FALSE  
## # … with 49 more rows

19.6 Visualizar el flujo de datos

Antes de pasar a los ejercicios, existe una reciente herramienta que nos va a permitir entender mejor y visualizar el flujo de trabajo de las funciones que hemos visto en este entorno tidyverse: https://tidydatatutor.com/. Basta con poner el código que queremos ejecutar, y nos muestra visualmente las operaciones en los datos.

Tidytutor.

Imagen/gráfica 19.3: Tidytutor.

Tidytutor.

Imagen/gráfica 19.4: Tidytutor.

Tidytutor.

Imagen/gráfica 19.5: Tidytutor.

Tidytutor.

Imagen/gráfica 19.6: Tidytutor.

Tidytutor.

Imagen/gráfica 19.7: Tidytutor.

Tidytutor.

Imagen/gráfica 19.8: Tidytutor.

19.7 📝 Ejercicios

(haz click en las flechas para ver soluciones)

📝Ejercicio 1: carga el conjunto de datos starwars y determina el tipo de variables, el númwero de filas y el número de columnas.

  • Solución:
# carga
starwars
## # A tibble: 87 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>
# dimensiones
nrow(starwars)
## [1] 87
ncol(starwars)
## [1] 14
dim(starwars)
## [1] 87 14
# Tipos de variables
glimpse(starwars)
## Rows: 87
## Columns: 14
## $ name       <chr> "Luke Skywalker", "C-3PO", "R2-D2", "Darth Vader", "Leia Or…
## $ height     <int> 172, 167, 96, 202, 150, 178, 165, 97, 183, 182, 188, 180, 2…
## $ mass       <dbl> 77.0, 75.0, 32.0, 136.0, 49.0, 120.0, 75.0, 32.0, 84.0, 77.…
## $ hair_color <chr> "blond", NA, NA, "none", "brown", "brown, grey", "brown", N…
## $ skin_color <chr> "fair", "gold", "white, blue", "white", "light", "light", "…
## $ eye_color  <chr> "blue", "yellow", "red", "yellow", "brown", "blue", "blue",…
## $ birth_year <dbl> 19.0, 112.0, 33.0, 41.9, 19.0, 52.0, 47.0, NA, 24.0, 57.0, …
## $ sex        <chr> "male", "none", "none", "male", "female", "male", "female",…
## $ gender     <chr> "masculine", "masculine", "masculine", "masculine", "femini…
## $ homeworld  <chr> "Tatooine", "Tatooine", "Naboo", "Tatooine", "Alderaan", "T…
## $ species    <chr> "Human", "Droid", "Droid", "Human", "Human", "Human", "Huma…
## $ films      <list> <"The Empire Strikes Back", "Revenge of the Sith", "Return…
## $ vehicles   <list> <"Snowspeeder", "Imperial Speeder Bike">, <>, <>, <>, "Imp…
## $ starships  <list> <"X-wing", "Imperial shuttle">, <>, <>, "TIE Advanced x1",…

 

📝Ejercicio 2: del conjunto starwars encuentra todos los personajes cuyo peso esté entre 60kg y 90kg. Imprime todas las columnas del filtro.

  • Solución:
# todas columnas
filtro <-
  starwars %>% filter(between(mass, 60, 90))
print(filtro, width = Inf)
## # A tibble: 32 × 14
##    name               height  mass hair_color    skin_color eye_color birth_year
##    <chr>               <int> <dbl> <chr>         <chr>      <chr>          <dbl>
##  1 Luke Skywalker        172    77 blond         fair       blue            19  
##  2 C-3PO                 167    75 <NA>          gold       yellow         112  
##  3 Beru Whitesun lars    165    75 brown         light      blue            47  
##  4 Biggs Darklighter     183    84 black         light      brown           24  
##  5 Obi-Wan Kenobi        182    77 auburn, white fair       blue-gray       57  
##  6 Anakin Skywalker      188    84 blond         fair       blue            41.9
##  7 Han Solo              180    80 brown         fair       brown           29  
##  8 Greedo                173    74 <NA>          green      black           44  
##  9 Wedge Antilles        170    77 brown         fair       hazel           21  
## 10 Palpatine             170    75 grey          pale       yellow          82  
##    sex    gender    homeworld species films     vehicles  starships
##    <chr>  <chr>     <chr>     <chr>   <list>    <list>    <list>   
##  1 male   masculine Tatooine  Human   <chr [5]> <chr [2]> <chr [2]>
##  2 none   masculine Tatooine  Droid   <chr [6]> <chr [0]> <chr [0]>
##  3 female feminine  Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  4 male   masculine Tatooine  Human   <chr [1]> <chr [0]> <chr [1]>
##  5 male   masculine Stewjon   Human   <chr [6]> <chr [1]> <chr [5]>
##  6 male   masculine Tatooine  Human   <chr [3]> <chr [2]> <chr [3]>
##  7 male   masculine Corellia  Human   <chr [4]> <chr [0]> <chr [2]>
##  8 male   masculine Rodia     Rodian  <chr [1]> <chr [0]> <chr [0]>
##  9 male   masculine Corellia  Human   <chr [3]> <chr [1]> <chr [1]>
## 10 male   masculine Naboo     Human   <chr [5]> <chr [0]> <chr [0]>
## # … with 22 more rows

 

📝Ejercicio 3: añadido al filtro anterior, encuentra todos los personajes que además tengan los ojos que no sean azules.

  • Solución:
# todas columnas
filtro <-
  starwars %>%
  filter(between(mass, 60, 90) & eye_color != "blue")
print(filtro, width = Inf)
## # A tibble: 27 × 14
##    name              height  mass hair_color    skin_color   eye_color
##    <chr>              <int> <dbl> <chr>         <chr>        <chr>    
##  1 C-3PO                167  75   <NA>          gold         yellow   
##  2 Biggs Darklighter    183  84   black         light        brown    
##  3 Obi-Wan Kenobi       182  77   auburn, white fair         blue-gray
##  4 Han Solo             180  80   brown         fair         brown    
##  5 Greedo               173  74   <NA>          green        black    
##  6 Wedge Antilles       170  77   brown         fair         hazel    
##  7 Palpatine            170  75   grey          pale         yellow   
##  8 Boba Fett            183  78.2 black         fair         brown    
##  9 Lando Calrissian     177  79   black         dark         brown    
## 10 Ackbar               180  83   none          brown mottle orange   
##    birth_year sex   gender    homeworld species      films     vehicles 
##         <dbl> <chr> <chr>     <chr>     <chr>        <list>    <list>   
##  1      112   none  masculine Tatooine  Droid        <chr [6]> <chr [0]>
##  2       24   male  masculine Tatooine  Human        <chr [1]> <chr [0]>
##  3       57   male  masculine Stewjon   Human        <chr [6]> <chr [1]>
##  4       29   male  masculine Corellia  Human        <chr [4]> <chr [0]>
##  5       44   male  masculine Rodia     Rodian       <chr [1]> <chr [0]>
##  6       21   male  masculine Corellia  Human        <chr [3]> <chr [1]>
##  7       82   male  masculine Naboo     Human        <chr [5]> <chr [0]>
##  8       31.5 male  masculine Kamino    Human        <chr [3]> <chr [0]>
##  9       31   male  masculine Socorro   Human        <chr [2]> <chr [0]>
## 10       41   male  masculine Mon Cala  Mon Calamari <chr [2]> <chr [0]>
##    starships
##    <list>   
##  1 <chr [0]>
##  2 <chr [1]>
##  3 <chr [5]>
##  4 <chr [2]>
##  5 <chr [0]>
##  6 <chr [1]>
##  7 <chr [0]>
##  8 <chr [1]>
##  9 <chr [1]>
## 10 <chr [0]>
## # … with 17 more rows

 

📝Ejercicio 4: añadido al filtro anterior, encuentra todos los personajes que además tengan menos de 100 años.

  • Solución:
# todas columnas
filtro <-
  starwars %>%
  filter(between(mass, 60, 90) & eye_color != "blue" &
           birth_year < 100)
print(filtro, width = Inf)
## # A tibble: 15 × 14
##    name              height  mass hair_color    skin_color   eye_color
##    <chr>              <int> <dbl> <chr>         <chr>        <chr>    
##  1 Biggs Darklighter    183  84   black         light        brown    
##  2 Obi-Wan Kenobi       182  77   auburn, white fair         blue-gray
##  3 Han Solo             180  80   brown         fair         brown    
##  4 Greedo               173  74   <NA>          green        black    
##  5 Wedge Antilles       170  77   brown         fair         hazel    
##  6 Palpatine            170  75   grey          pale         yellow   
##  7 Boba Fett            183  78.2 black         fair         brown    
##  8 Lando Calrissian     177  79   black         dark         brown    
##  9 Ackbar               180  83   none          brown mottle orange   
## 10 Jar Jar Binks        196  66   none          orange       orange   
## 11 Darth Maul           175  80   none          red          yellow   
## 12 Mace Windu           188  84   none          dark         brown    
## 13 Ki-Adi-Mundi         198  82   white         pale         yellow   
## 14 Plo Koon             188  80   none          orange       black    
## 15 Jango Fett           183  79   black         tan          brown    
##    birth_year sex   gender    homeworld    species      films     vehicles 
##         <dbl> <chr> <chr>     <chr>        <chr>        <list>    <list>   
##  1       24   male  masculine Tatooine     Human        <chr [1]> <chr [0]>
##  2       57   male  masculine Stewjon      Human        <chr [6]> <chr [1]>
##  3       29   male  masculine Corellia     Human        <chr [4]> <chr [0]>
##  4       44   male  masculine Rodia        Rodian       <chr [1]> <chr [0]>
##  5       21   male  masculine Corellia     Human        <chr [3]> <chr [1]>
##  6       82   male  masculine Naboo        Human        <chr [5]> <chr [0]>
##  7       31.5 male  masculine Kamino       Human        <chr [3]> <chr [0]>
##  8       31   male  masculine Socorro      Human        <chr [2]> <chr [0]>
##  9       41   male  masculine Mon Cala     Mon Calamari <chr [2]> <chr [0]>
## 10       52   male  masculine Naboo        Gungan       <chr [2]> <chr [0]>
## 11       54   male  masculine Dathomir     Zabrak       <chr [1]> <chr [1]>
## 12       72   male  masculine Haruun Kal   Human        <chr [3]> <chr [0]>
## 13       92   male  masculine Cerea        Cerean       <chr [3]> <chr [0]>
## 14       22   male  masculine Dorin        Kel Dor      <chr [3]> <chr [0]>
## 15       66   male  masculine Concord Dawn Human        <chr [1]> <chr [0]>
##    starships
##    <list>   
##  1 <chr [1]>
##  2 <chr [5]>
##  3 <chr [2]>
##  4 <chr [0]>
##  5 <chr [1]>
##  6 <chr [0]>
##  7 <chr [1]>
##  8 <chr [1]>
##  9 <chr [0]>
## 10 <chr [0]>
## 11 <chr [1]>
## 12 <chr [0]>
## 13 <chr [0]>
## 14 <chr [1]>
## 15 <chr [0]>

 

📝Ejercicio 5: añadido al filtro anterior, selecciona solo las columnas name, mass, eye_color, birth_year

  • Solución:
# Solo name, mass, eye_color, birth_year
filtro %>% select(c(name, mass, eye_color, birth_year))
## # A tibble: 15 × 4
##    name               mass eye_color birth_year
##    <chr>             <dbl> <chr>          <dbl>
##  1 Biggs Darklighter  84   brown           24  
##  2 Obi-Wan Kenobi     77   blue-gray       57  
##  3 Han Solo           80   brown           29  
##  4 Greedo             74   black           44  
##  5 Wedge Antilles     77   hazel           21  
##  6 Palpatine          75   yellow          82  
##  7 Boba Fett          78.2 brown           31.5
##  8 Lando Calrissian   79   brown           31  
##  9 Ackbar             83   orange          41  
## 10 Jar Jar Binks      66   orange          52  
## 11 Darth Maul         80   yellow          54  
## 12 Mace Windu         84   brown           72  
## 13 Ki-Adi-Mundi       82   yellow          92  
## 14 Plo Koon           80   black           22  
## 15 Jango Fett         79   brown           66

 

📝Ejercicio 5: selecciona solo las columnas que contengan variables numéricas.

  • Solución:
starwars %>% select(where(is.numeric))
## # A tibble: 87 × 3
##    height  mass birth_year
##     <int> <dbl>      <dbl>
##  1    172    77       19  
##  2    167    75      112  
##  3     96    32       33  
##  4    202   136       41.9
##  5    150    49       19  
##  6    178   120       52  
##  7    165    75       47  
##  8     97    32       NA  
##  9    183    84       24  
## 10    182    77       57  
## # … with 77 more rows

 

📝Ejercicio 6: añadido a la selección anterior, coloca los años de nacimiento como primera columna y cambia los nombres a castellano.

  • Solución:
starwars %>%
  select(where(is.numeric)) %>%
  relocate(height, mass, .after = birth_year) %>%
  rename(edad = birth_year, altura = height, peso = mass)
## # A tibble: 87 × 3
##     edad altura  peso
##    <dbl>  <int> <dbl>
##  1  19      172    77
##  2 112      167    75
##  3  33       96    32
##  4  41.9    202   136
##  5  19      150    49
##  6  52      178   120
##  7  47      165    75
##  8  NA       97    32
##  9  24      183    84
## 10  57      182    77
## # … with 77 more rows

 

📝Ejercicio 7: calcula una nueva columna que indique el número de naves que ha pilotado cada persona (escribe ? starwars en consola para ver documentación del fichero).

  • Solución:
starwars_numero_naves <- 
  starwars %>%
  mutate(n_naves = map_chr(starships, length))

# Imprimimos todas las columnas
print(starwars_numero_naves, width = Inf)
## # A tibble: 87 × 15
##    name               height  mass hair_color    skin_color  eye_color
##    <chr>               <int> <dbl> <chr>         <chr>       <chr>    
##  1 Luke Skywalker        172    77 blond         fair        blue     
##  2 C-3PO                 167    75 <NA>          gold        yellow   
##  3 R2-D2                  96    32 <NA>          white, blue red      
##  4 Darth Vader           202   136 none          white       yellow   
##  5 Leia Organa           150    49 brown         light       brown    
##  6 Owen Lars             178   120 brown, grey   light       blue     
##  7 Beru Whitesun lars    165    75 brown         light       blue     
##  8 R5-D4                  97    32 <NA>          white, red  red      
##  9 Biggs Darklighter     183    84 black         light       brown    
## 10 Obi-Wan Kenobi        182    77 auburn, white fair        blue-gray
##    birth_year sex    gender    homeworld species films     vehicles  starships
##         <dbl> <chr>  <chr>     <chr>     <chr>   <list>    <list>    <list>   
##  1       19   male   masculine Tatooine  Human   <chr [5]> <chr [2]> <chr [2]>
##  2      112   none   masculine Tatooine  Droid   <chr [6]> <chr [0]> <chr [0]>
##  3       33   none   masculine Naboo     Droid   <chr [7]> <chr [0]> <chr [0]>
##  4       41.9 male   masculine Tatooine  Human   <chr [4]> <chr [0]> <chr [1]>
##  5       19   female feminine  Alderaan  Human   <chr [5]> <chr [1]> <chr [0]>
##  6       52   male   masculine Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  7       47   female feminine  Tatooine  Human   <chr [3]> <chr [0]> <chr [0]>
##  8       NA   none   masculine Tatooine  Droid   <chr [1]> <chr [0]> <chr [0]>
##  9       24   male   masculine Tatooine  Human   <chr [1]> <chr [0]> <chr [1]>
## 10       57   male   masculine Stewjon   Human   <chr [6]> <chr [1]> <chr [5]>
##    n_naves
##    <chr>  
##  1 2      
##  2 0      
##  3 0      
##  4 1      
##  5 0      
##  6 0      
##  7 0      
##  8 0      
##  9 1      
## 10 5      
## # … with 77 more rows
# Solo la columna añadida
starwars %>%
  transmute(n_naves = map_chr(starships, length))
## # A tibble: 87 × 1
##    n_naves
##    <chr>  
##  1 2      
##  2 0      
##  3 0      
##  4 1      
##  5 0      
##  6 0      
##  7 0      
##  8 0      
##  9 1      
## 10 5      
## # … with 77 more rows

 

📝Ejercicio 8: con la columna anterior añadido, crea una nueva variable TRUE/FALSE que nos diga si ha conducido o no alguna nave, y filtra después solo aquellos personajes que han conducido alguna nave.

  • Solución:
# Nueva columna lógica
starwars_numero_naves %>%
  mutate(conducir_nave = n_naves > 0) %>%
  filter(conducir_nave)
## # A tibble: 20 × 16
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172  77   blond       fair       blue            19   male  mascu…
##  2 Darth …    202 136   none        white      yellow          41.9 male  mascu…
##  3 Biggs …    183  84   black       light      brown           24   male  mascu…
##  4 Obi-Wa…    182  77   auburn, wh… fair       blue-gray       57   male  mascu…
##  5 Anakin…    188  84   blond       fair       blue            41.9 male  mascu…
##  6 Chewba…    228 112   brown       unknown    blue           200   male  mascu…
##  7 Han So…    180  80   brown       fair       brown           29   male  mascu…
##  8 Wedge …    170  77   brown       fair       hazel           21   male  mascu…
##  9 Jek To…    180 110   brown       fair       blue            NA   male  mascu…
## 10 Boba F…    183  78.2 black       fair       brown           31.5 male  mascu…
## 11 Lando …    177  79   black       dark       brown           31   male  mascu…
## 12 Arvel …     NA  NA   brown       fair       brown           NA   male  mascu…
## 13 Nien N…    160  68   none        grey       black           NA   male  mascu…
## 14 Ric Ol…    183  NA   brown       fair       blue            NA   <NA>  <NA>  
## 15 Darth …    175  80   none        red        yellow          54   male  mascu…
## 16 Plo Ko…    188  80   none        orange     black           22   male  mascu…
## 17 Gregar…    185  85   black       dark       brown           NA   male  mascu…
## 18 Grievo…    216 159   none        brown, wh… green, y…       NA   male  mascu…
## 19 Poe Da…     NA  NA   brown       light      brown           NA   male  mascu…
## 20 Padmé …    165  45   brown       light      brown           46   fema… femin…
## # … with 7 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>, n_naves <chr>, conducir_nave <lgl>

 

📝Ejercicio 9: calcula el número de películas en las que han salido y ordena a los personajes de mayor a menor número de películas en las que ha aparecido

  • Solución:
starwars %>% # Calculamos primero el número de películas
  mutate(n_films = map_int(films, length)) %>%
  arrange(desc(n_films)) # Ordenamos de mayor a menor
## # A tibble: 87 × 15
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
##  4 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Chewba…    228   112 brown       unknown    blue           200   male  mascu…
##  7 Yoda        66    17 white       green      brown          896   male  mascu…
##  8 Palpat…    170    75 grey        pale       yellow          82   male  mascu…
##  9 Darth …    202   136 none        white      yellow          41.9 male  mascu…
## 10 Han So…    180    80 brown       fair       brown           29   male  mascu…
## # … with 77 more rows, and 6 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>, n_films <int>

 

📝Ejercicio 10: selecciona 7 personajes al azar.

  • Solución:
# Una extracción aleatoria
starwars %>% slice_sample(n = 7)
## # A tibble: 7 × 14
##   name     height  mass hair_color skin_color eye_color birth_year sex    gender
##   <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr>  <chr> 
## 1 Chewbac…    228 112   brown      unknown    blue           200   male   mascu…
## 2 Arvel C…     NA  NA   brown      fair       brown           NA   male   mascu…
## 3 Plo Koon    188  80   none       orange     black           22   male   mascu…
## 4 Luminar…    170  56.2 black      yellow     blue            58   female femin…
## 5 Boba Fe…    183  78.2 black      fair       brown           31.5 male   mascu…
## 6 Biggs D…    183  84   black      light      brown           24   male   mascu…
## 7 Rey          NA  NA   brown      light      hazel           NA   female femin…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>
# otra (que sale distinta, claro)
starwars %>% slice_sample(n = 7)
## # A tibble: 7 × 14
##   name     height  mass hair_color  skin_color eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
## 1 Cliegg …    183    NA brown       fair       blue              82 male  mascu…
## 2 Shaak Ti    178    57 none        red, blue… black             NA fema… femin…
## 3 Poe Dam…     NA    NA brown       light      brown             NA male  mascu…
## 4 Obi-Wan…    182    77 auburn, wh… fair       blue-gray         57 male  mascu…
## 5 Sebulba     112    40 none        grey, red  orange            NA male  mascu…
## 6 Bail Pr…    191    NA black       tan        brown             67 male  mascu…
## 7 Wat Tam…    193    48 none        green, gr… unknown           NA male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>

 

📝Ejercicio 11: selecciona los 5 personajes que en más películas han salido y los 5 que menos.

  • Solución:
# personajes que en más películas han salido (metiendo empates)
starwars  %>%
  mutate(n_films = map_int(films, length)) %>%
  slice_max(n_films, n = 5)
## # A tibble: 8 × 15
##   name     height  mass hair_color  skin_color eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
## 1 R2-D2        96    32 <NA>        white, bl… red               33 none  mascu…
## 2 C-3PO       167    75 <NA>        gold       yellow           112 none  mascu…
## 3 Obi-Wan…    182    77 auburn, wh… fair       blue-gray         57 male  mascu…
## 4 Luke Sk…    172    77 blond       fair       blue              19 male  mascu…
## 5 Leia Or…    150    49 brown       light      brown             19 fema… femin…
## 6 Chewbac…    228   112 brown       unknown    blue             200 male  mascu…
## 7 Yoda         66    17 white       green      brown            896 male  mascu…
## 8 Palpati…    170    75 grey        pale       yellow            82 male  mascu…
## # … with 6 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>, n_films <int>
# personajes que en más películas han salido (sin empates)
starwars %>%
  mutate(n_films = map_int(films, length)) %>%
  slice_max(n_films, n = 5, with_ties = FALSE)
## # A tibble: 5 × 15
##   name     height  mass hair_color  skin_color eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
## 1 R2-D2        96    32 <NA>        white, bl… red               33 none  mascu…
## 2 C-3PO       167    75 <NA>        gold       yellow           112 none  mascu…
## 3 Obi-Wan…    182    77 auburn, wh… fair       blue-gray         57 male  mascu…
## 4 Luke Sk…    172    77 blond       fair       blue              19 male  mascu…
## 5 Leia Or…    150    49 brown       light      brown             19 fema… femin…
## # … with 6 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>, n_films <int>
# personajes que en menos películas han salido (metiendo empates)
starwars %>%
  mutate(n_films = map_int(films, length)) %>%
  slice_min(n_films, n = 5)
## # A tibble: 46 × 15
##    name     height  mass hair_color skin_color eye_color birth_year sex   gender
##    <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
##  1 R5-D4        97    32 <NA>       white, red red               NA none  mascu…
##  2 Biggs D…    183    84 black      light      brown             24 male  mascu…
##  3 Greedo      173    74 <NA>       green      black             44 male  mascu…
##  4 Jek Ton…    180   110 brown      fair       blue              NA male  mascu…
##  5 IG-88       200   140 none       metal      red               15 none  mascu…
##  6 Bossk       190   113 none       green      red               53 male  mascu…
##  7 Lobot       175    79 none       light      blue              37 male  mascu…
##  8 Mon Mot…    150    NA auburn     fair       blue              48 fema… femin…
##  9 Arvel C…     NA    NA brown      fair       brown             NA male  mascu…
## 10 Wicket …     88    20 brown      brown      brown              8 male  mascu…
## # … with 36 more rows, and 6 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>, n_films <int>
# personajes que en menos películas han salido (sin empates)
starwars %>%
  mutate(n_films = map_int(films, length)) %>%
  slice_min(n_films, n = 5, with_ties = FALSE)
## # A tibble: 5 × 15
##   name      height  mass hair_color skin_color eye_color birth_year sex   gender
##   <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
## 1 R5-D4         97    32 <NA>       white, red red               NA none  mascu…
## 2 Biggs Da…    183    84 black      light      brown             24 male  mascu…
## 3 Greedo       173    74 <NA>       green      black             44 male  mascu…
## 4 Jek Tono…    180   110 brown      fair       blue              NA male  mascu…
## 5 IG-88        200   140 none       metal      red               15 none  mascu…
## # … with 6 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>, n_films <int>

 

📝Ejercicio 12: selecciona solo las columnas que se refieren a variables de color (ojos, piel, pelo) (con sufijo "color")

  • Solución:
# Contiene "color" independientemente de que sea sufijo o prefijo
starwars %>% select(contains("color"))
## # A tibble: 87 × 3
##    hair_color    skin_color  eye_color
##    <chr>         <chr>       <chr>    
##  1 blond         fair        blue     
##  2 <NA>          gold        yellow   
##  3 <NA>          white, blue red      
##  4 none          white       yellow   
##  5 brown         light       brown    
##  6 brown, grey   light       blue     
##  7 brown         light       blue     
##  8 <NA>          white, red  red      
##  9 black         light       brown    
## 10 auburn, white fair        blue-gray
## # … with 77 more rows
# Contiene "color" como sufijo
starwars %>% select(ends_with("color"))
## # A tibble: 87 × 3
##    hair_color    skin_color  eye_color
##    <chr>         <chr>       <chr>    
##  1 blond         fair        blue     
##  2 <NA>          gold        yellow   
##  3 <NA>          white, blue red      
##  4 none          white       yellow   
##  5 brown         light       brown    
##  6 brown, grey   light       blue     
##  7 brown         light       blue     
##  8 <NA>          white, red  red      
##  9 black         light       brown    
## 10 auburn, white fair        blue-gray
## # … with 77 more rows