2  Importación y exploración de datos

NoteResultados de aprendizaje
  • R2. Importar y manipular bases de datos en R.

En este capítulo damos el salto desde datos que escribimos a mano hacia datos reales que viven en archivos. Trabajaremos con el ecosistema tidyverse, una colección de paquetes diseñados para el análisis de datos.

# Instala una sola vez (si aún no lo tienes)
install.packages("tidyverse")
install.packages("naniar")   # para explorar datos faltantes
library(tidyverse)
library(naniar)

2.1 Importar un archivo CSV

La función read_csv() lee un archivo de valores separados por comas y lo guarda como un data frame (técnicamente, un tibble). En este ejemplo usamos un dataset de los animes mejor rankeados.

anime <- read_csv("data/anime.csv")
TipRutas relativas

Si trabajas dentro de un Proyecto de RStudio, las rutas se escriben relativas a la carpeta del proyecto. Así "data/..." funcionará en cualquier computador, sin necesidad de setwd().

2.2 Explorar la estructura de los datos

Antes de analizar, siempre conviene mirar los datos. Estas funciones son tus herramientas básicas de exploración:

str(anime)       # estructura: tipo de cada columna
spc_tbl_ [2,000 × 19] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ anime_id    : num [1:2000] 25833 1237 9202 30868 34784 ...
 $ name        : chr [1:2000] "Zephyr" "One Piece: Oounabara ni Hirake! Dekkai Dekkai Chichi no Yume!" "Seikon no Qwaser: Jotei no Shouzou" "Ajin Part 1: Shoudou" ...
 $ english_name: chr [1:2000] NA "One Piece: Open Upon the Great Sea! A Father's Huge, HUGE Dream!" "The Qwaser of Stigmata: Portrait of the Empress" "Ajin: Demi-Human Movie 1: Compel" ...
 $ score       : num [1:2000] 5.87 7.18 6.37 7.42 7.19 7.6 5.7 6.61 8.46 6.9 ...
 $ genres      : chr [1:2000] "Drama" "Action, Adventure, Fantasy" "Action, Ecchi" "Action, Horror, Mystery, Supernatural" ...
 $ themes      : chr [1:2000] "Music" NA "Gore, Harem, School, Super Power" "Gore" ...
 $ demographics: chr [1:2000] NA "Shounen" "Seinen" "Seinen" ...
 $ type        : chr [1:2000] "Special" "TV Special" "OVA" "Movie" ...
 $ episodes    : num [1:2000] 1 1 1 1 1 25 23 1 12 1 ...
 $ premiered   : chr [1:2000] NA NA NA NA ...
 $ studios     : chr [1:2000] "Satelight" "Toei Animation" "Hoods Entertainment" "Polygon Pictures" ...
 $ source      : chr [1:2000] "Unknown" "Manga" "Manga" "Manga" ...
 $ duration    : chr [1:2000] "26 min" "46 min" "23 min" "1 hr 44 min" ...
 $ rating      : chr [1:2000] "PG-13 - Teens 13 or older" "PG-13 - Teens 13 or older" "R+ - Mild Nudity" "R - 17+ (violence & profanity)" ...
 $ rank        : num [1:2000] 10577 3478 7847 2283 3402 ...
 $ popularity  : num [1:2000] 10969 4010 4352 3423 5422 ...
 $ favorites   : num [1:2000] 7 17 16 49 7 ...
 $ scored_by   : num [1:2000] 317 16732 11696 17295 6080 ...
 $ members     : num [1:2000] 2643 34805 29381 47229 17401 ...
 - attr(*, "spec")=
  .. cols(
  ..   anime_id = col_double(),
  ..   name = col_character(),
  ..   english_name = col_character(),
  ..   score = col_double(),
  ..   genres = col_character(),
  ..   themes = col_character(),
  ..   demographics = col_character(),
  ..   type = col_character(),
  ..   episodes = col_double(),
  ..   premiered = col_character(),
  ..   studios = col_character(),
  ..   source = col_character(),
  ..   duration = col_character(),
  ..   rating = col_character(),
  ..   rank = col_double(),
  ..   popularity = col_double(),
  ..   favorites = col_double(),
  ..   scored_by = col_double(),
  ..   members = col_double()
  .. )
 - attr(*, "problems")=<pointer: 0x74aff8340> 
glimpse(anime)   # versión más ordenada de str() (tidyverse)
Rows: 2,000
Columns: 19
$ anime_id     <dbl> 25833, 1237, 9202, 30868, 34784, 10278, 7808, 32338, 4451…
$ name         <chr> "Zephyr", "One Piece: Oounabara ni Hirake! Dekkai Dekkai …
$ english_name <chr> NA, "One Piece: Open Upon the Great Sea! A Father's Huge,…
$ score        <dbl> 5.87, 7.18, 6.37, 7.42, 7.19, 7.60, 5.70, 6.61, 8.46, 6.9…
$ genres       <chr> "Drama", "Action, Adventure, Fantasy", "Action, Ecchi", "…
$ themes       <chr> "Music", NA, "Gore, Harem, School, Super Power", "Gore", …
$ demographics <chr> NA, "Shounen", "Seinen", "Seinen", "Shounen", NA, NA, NA,…
$ type         <chr> "Special", "TV Special", "OVA", "Movie", "Special", "TV",…
$ episodes     <dbl> 1, 1, 1, 1, 1, 25, 23, 1, 12, 1, 13, 2, 4, 52, NA, 1, 1, …
$ premiered    <chr> NA, NA, NA, NA, NA, "summer 2011", "spring 1980", NA, "fa…
$ studios      <chr> "Satelight", "Toei Animation", "Hoods Entertainment", "Po…
$ source       <chr> "Unknown", "Manga", "Manga", "Manga", "Manga", "Game", "N…
$ duration     <chr> "26 min", "46 min", "23 min", "1 hr 44 min", "15 min", "2…
$ rating       <chr> "PG-13 - Teens 13 or older", "PG-13 - Teens 13 or older",…
$ rank         <dbl> 10577, 3478, 7847, 2283, 3402, 1580, 11396, NA, 160, NA, …
$ popularity   <dbl> 10969, 4010, 4352, 3423, 5422, 1592, 13587, 9487, 62, 138…
$ favorites    <dbl> 7, 17, 16, 49, 7, 1690, 3, 7, 49992, 1, 18088, 2, 28, 0, …
$ scored_by    <dbl> 317, 16732, 11696, 17295, 6080, 55143, 340, 1831, 958426,…
$ members      <dbl> 2643, 34805, 29381, 47229, 17401, 156465, 1199, 4159, 169…
summary(anime)   # resumen estadístico
    anime_id            name         english_name      score      
 Min.   :    5   Length   :2000   Length   :2000   Min.   :5.580  
 1st Qu.: 5762   N.unique :2000   N.unique :1114   1st Qu.:6.130  
 Median :28330   N.blank  :   0   N.blank  :   0   Median :6.565  
 Mean   :25745   Min.nchar:   2   Min.nchar:   3   Mean   :6.680  
 3rd Qu.:40843   Max.nchar: 105   Max.nchar: 116   3rd Qu.:7.170  
 Max.   :60811                    NAs      : 883   Max.   :9.060  
                                                                  
       genres           themes        demographics         type     
 Length   :2000   Length   :2000   Length   :2000   Length   :2000  
 N.unique : 325   N.unique : 331   N.unique :   6   N.unique :   9  
 N.blank  :   0   N.blank  :   0   N.blank  :   0   N.blank  :   0  
 Min.nchar:   5   Min.nchar:   4   Min.nchar:   4   Min.nchar:   2  
 Max.nchar:  63   Max.nchar:  58   Max.nchar:  13   Max.nchar:  10  
 NAs      : 204   NAs      : 661   NAs      :1387                   
                                                                    
    episodes          premiered         studios           source    
 Min.   :   1.0   Length   :2000   Length   :2000   Length   :2000  
 1st Qu.:   1.0   N.unique : 182   N.unique : 504   N.unique :  16  
 Median :   3.0   N.blank  :   0   N.blank  :   0   N.blank  :   0  
 Mean   :  13.6   Min.nchar:   9   Min.nchar:   3   Min.nchar:   4  
 3rd Qu.:  13.0   Max.nchar:  11   Max.nchar:  54   Max.nchar:  12  
 Max.   :1274.0   NAs      :1345   NAs      : 292                   
 NAs    :15                                                         
      duration          rating          rank         popularity   
 Length   :2000   Length   :2000   Min.   :    4   Min.   :    6  
 N.unique : 193   N.unique :   6   1st Qu.: 3027   1st Qu.: 3648  
 N.blank  :   0   N.blank  :   0   Median : 6104   Median : 7691  
 Min.nchar:   4   Min.nchar:  11   Mean   : 5960   Mean   : 8276  
 Max.nchar:  18   Max.nchar:  30   3rd Qu.: 8951   3rd Qu.:12497  
                  NAs      :   8   Max.   :11861   Max.   :21551  
                                   NAs    :376                    
   favorites          scored_by            members       
 Min.   :     0.0   Min.   :    103.0   Min.   :    200  
 1st Qu.:     2.0   1st Qu.:    556.5   1st Qu.:   1649  
 Median :    12.0   Median :   2675.0   Median :   7245  
 Mean   :   795.9   Mean   :  37890.2   Mean   :  74734  
 3rd Qu.:   106.0   3rd Qu.:  17303.5   3rd Qu.:  42306  
 Max.   :110803.0   Max.   :2206556.0   Max.   :3175177  
                                                         
nrow(anime)      # número de filas (observaciones)
[1] 2000
ncol(anime)      # número de columnas (variables)
[1] 19
names(anime)     # nombres de las columnas
 [1] "anime_id"     "name"         "english_name" "score"        "genres"      
 [6] "themes"       "demographics" "type"         "episodes"     "premiered"   
[11] "studios"      "source"       "duration"     "rating"       "rank"        
[16] "popularity"   "favorites"    "scored_by"    "members"     

Para revisar el tipo de una columna específica usamos class():

class(anime$episodes)
[1] "numeric"
class(anime$rank)
[1] "numeric"

2.3 Detectar datos faltantes (NA)

Los valores faltantes se representan con NA. Ignorarlos puede arruinar un análisis, así que el primer paso es cuantificarlos:

n_miss(anime)            # total de NA (paquete naniar)
[1] 5171
sum(is.na(anime))        # forma equivalente en R base
[1] 5171
vis_miss(anime)          # visualiza el patrón de NA por columna

sum(is.na(anime$episodes))  # NA en una columna específica
[1] 15

Para quedarnos solo con las filas sin NA en una columna, filtramos con la negación lógica !:

anime_sin_na <- anime %>%
  filter(!is.na(episodes))

2.4 El operador pipe %>%

El símbolo %>% (pipe) encadena operaciones: toma el resultado de la izquierda y lo entrega como primer argumento de la función de la derecha. Se lee como “y luego”. Es la columna vertebral del tidyverse.

Note

También existe el pipe nativo de R |>, que funciona de forma muy similar. Verás ambos a lo largo del curso.

2.5 Conteos y frecuencias

count() cuenta cuántas observaciones hay por categoría; con sort = TRUE las ordena de mayor a menor:

anime %>% count(type, sort = TRUE)     # ¿cuántos hay de cada tipo?
anime %>% count(source, sort = TRUE)   # ¿de qué fuente provienen?
table(anime$rating)                    # alternativa en R base

                  G - All Ages                  PG - Children 
                           415                            111 
     PG-13 - Teens 13 or older R - 17+ (violence & profanity) 
                           996                            190 
              R+ - Mild Nudity                    Rx - Hentai 
                           131                            149 
# Cantidad de valores únicos en una columna
anime %>% distinct(source) %>% nrow()
[1] 16

2.6 Ordenar y seleccionar

arrange() ordena filas; desc() invierte el orden (de mayor a menor). select() elige columnas y head(n) muestra las primeras n filas:

# Top 10 animes por puntaje
anime %>%
  arrange(desc(score)) %>%
  head(10) %>%
  select(name, score, type, episodes)

filter() permite combinar varias condiciones separándolas con comas (que funcionan como un “Y” lógico):

# Los 5 animes de tipo "Movie" con más miembros
anime %>%
  filter(type == "Movie") %>%
  arrange(desc(members)) %>%
  head(5) %>%
  select(name, members, score)

Ejercicios

ImportantEjercicio 2.1 — Exploración guiada

Usando el dataset anime:

  1. Usa tres funciones distintas para explorar su estructura.
  2. ¿Cuántas filas y columnas tiene?
  3. Obtén los nombres de todas las columnas.
  4. ¿Cuántos valores faltantes hay en total? ¿Y en la columna episodes?
  5. Crea anime_sin_na conservando solo filas donde episodes no sea NA.
  6. ¿Cuál es la fuente (source) más frecuente?
  7. Encuentra los 10 animes con más favoritos (favorites). ¿Coinciden con los mejor puntuados?