10 tidyr

tidyr es un paquete que pertenece al tidyverse, a quienes también pertenecen ggplot2 y dplyr. Este paquete es muy útil para manipular datos, y más específicamente, crear datos “tidy”, es decir, datos bien ordenados.

Los datos bien ordenados, de acuerdo a tidyr.tidyverse.org, tiene tres características:

  • Cada una de las columnas de nuestros datos son una variable
  • Cada fila es una observación
  • Cada celda es un valor particular.

Las ventajas de emplear este paquete es que permite manipular los datos de una forma más comprensiva y eficiente.

10.1 Instalación de tidyr

Como se mencionaba anteriormente para ggplot2 y dyplr, puede instalarse tidyverse de la siguiente manera:

install.packages("tidyverse")

También puede instalarse solamente tidyr:

install.packages("tidyr")

10.2 Principales 5 funciones de tidyr

Existen 5 funciones de tidyr que hacen parte de las explicadas en tidyr.tidyverse.org: Pivotting, Rectangling, Nesting, Splitting and Combining y Missings

Pivotting

Las funciones pivot_longer() o pivot_wider() que pertencen a esta categoría, permiten convertir entre formas largas (long) y anchas (wide) las bases de datos. Veamos un ejemplo.

Para este fin utilizaremos la base de datos relig_income que viene en el paquete tidyr:

##  [1] "religion"           "<$10k"              "$10-20k"           
##  [4] "$20-30k"            "$30-40k"            "$40-50k"           
##  [7] "$50-75k"            "$75-100k"           "$100-150k"         
## [10] ">150k"              "Don't know/refused"
## # A tibble: 5 x 11
##   religion `<$10k` `$10-20k` `$20-30k` `$30-40k` `$40-50k` `$50-75k` `$75-100k`
##   <chr>      <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>      <dbl>
## 1 Agnostic      27        34        60        81        76       137        122
## 2 Atheist       12        27        37        52        35        70         73
## 3 Buddhist      27        21        30        34        33        58         62
## 4 Catholic     418       617       732       670       638      1116        949
## 5 Don’t k~      15        14        15        11        10        35         21
## # ... with 3 more variables: `$100-150k` <dbl>, `>150k` <dbl>, `Don't
## #   know/refused` <dbl>

Esta base de datos cuenta con las variables religion y rangos de ingreso, y los valores corresponden al número de personas que respondieron en cada rango.

Ahora se va a transformar a una base de datos larga (long), es decir, se incrementará el número de filas y disminuirá el número de clumnas. Para ello, utilizamos la función pivot_longer() para reorganizar los datos de la siguiente manera:

## # A tibble: 10 x 3
##    religion income             count
##    <chr>    <chr>              <dbl>
##  1 Agnostic <$10k                 27
##  2 Agnostic $10-20k               34
##  3 Agnostic $20-30k               60
##  4 Agnostic $30-40k               81
##  5 Agnostic $40-50k               76
##  6 Agnostic $50-75k              137
##  7 Agnostic $75-100k             122
##  8 Agnostic $100-150k            109
##  9 Agnostic >150k                 84
## 10 Agnostic Don't know/refused    96

Se observa que la nueva base de datos contiene sólo 3 columnas: religion, income y count y al pasar de ancha a larga el número de observaciones se ha incrementado y este incremento se debe a que en la nueva base de datos cada religión se repite el rango de ingresos que había en las columnas.

Intentemos entender los argumentos de la función pivot_longer(). Utilizamos, como en ejemplos pasados, el muy útil operador %>% para indicarle a R que la base de datos a manipular es relig_income. Por otro lado, el primer argumento de la función pivot_longer() indica que se van a manipular todas las columnas exceptuando religion (recordemos los operadores lógicos, especialmente !). El segundo argumento, le da nombre a la variable que se creó a partir de los datos guardados en columnas; y el tercero argumento, le da nombre a la variable que se creó a partir de los datos guardados en las celdas.

Para pasar la base de datos larga a ancha, se utliza la función pivot_wider de la siguiente forma:

## # A tibble: 10 x 11
##    religion `<$10k` `$10-20k` `$20-30k` `$30-40k` `$40-50k` `$50-75k` `$75-100k`
##    <chr>      <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>      <dbl>
##  1 Agnostic      27        34        60        81        76       137        122
##  2 Atheist       12        27        37        52        35        70         73
##  3 Buddhist      27        21        30        34        33        58         62
##  4 Catholic     418       617       732       670       638      1116        949
##  5 Don’t k~      15        14        15        11        10        35         21
##  6 Evangel~     575       869      1064       982       881      1486        949
##  7 Hindu          1         9         7         9        11        34         47
##  8 Histori~     228       244       236       238       197       223        131
##  9 Jehovah~      20        27        24        24        21        30         15
## 10 Jewish        19        19        25        25        30        95         69
## # ... with 3 more variables: `$100-150k` <dbl>, `>150k` <dbl>, `Don't
## #   know/refused` <dbl>

Rectangling

Rectangling tiene una serie de funciones que permite tomar bases de datos que son listas de elementos numéricas y no y crear una base de datos con filas y columnas.

Como ejemplo, utilizaremos la base de datos gh_users del paquete repurrrsive que contiene información sobre usuarios de GitHub y ha sido recuperda de la API de GitHub.4 Esta base de datos es una lista con 6 componentes y cada uno contiene información de 6 usuarios, tales como: username, GitHub id, y fecha de afiliación.

las funciones de este tipo, como unnest_longer() o unnest_wider(), convierten listas anidadas en “Tidy tibbles” más simples. Por ahora, podemos interpretar un “Tibble” como una especie de data frame. Veamos un ejemplo.

Para ver la información del primer usuario, el código sería:

install.packages("repurrrsive")
## [[1]]
## [[1]]$login
## [1] "gaborcsardi"
## 
## [[1]]$id
## [1] 660288
## 
## [[1]]$avatar_url
## [1] "https://avatars.githubusercontent.com/u/660288?v=3"
## 
## [[1]]$gravatar_id
## [1] ""
## 
## [[1]]$url
## [1] "https://api.github.com/users/gaborcsardi"
## 
## [[1]]$html_url
## [1] "https://github.com/gaborcsardi"
## 
## [[1]]$followers_url
## [1] "https://api.github.com/users/gaborcsardi/followers"
## 
## [[1]]$following_url
## [1] "https://api.github.com/users/gaborcsardi/following{/other_user}"
## 
## [[1]]$gists_url
## [1] "https://api.github.com/users/gaborcsardi/gists{/gist_id}"
## 
## [[1]]$starred_url
## [1] "https://api.github.com/users/gaborcsardi/starred{/owner}{/repo}"
## 
## [[1]]$subscriptions_url
## [1] "https://api.github.com/users/gaborcsardi/subscriptions"
## 
## [[1]]$organizations_url
## [1] "https://api.github.com/users/gaborcsardi/orgs"
## 
## [[1]]$repos_url
## [1] "https://api.github.com/users/gaborcsardi/repos"
## 
## [[1]]$events_url
## [1] "https://api.github.com/users/gaborcsardi/events{/privacy}"
## 
## [[1]]$received_events_url
## [1] "https://api.github.com/users/gaborcsardi/received_events"
## 
## [[1]]$type
## [1] "User"
## 
## [[1]]$site_admin
## [1] FALSE
## 
## [[1]]$name
## [1] "Gábor Csárdi"
## 
## [[1]]$company
## [1] "Mango Solutions, @MangoTheCat "
## 
## [[1]]$blog
## [1] "http://gaborcsardi.org"
## 
## [[1]]$location
## [1] "Chippenham, UK"
## 
## [[1]]$email
## [1] "csardi.gabor@gmail.com"
## 
## [[1]]$hireable
## NULL
## 
## [[1]]$bio
## NULL
## 
## [[1]]$public_repos
## [1] 52
## 
## [[1]]$public_gists
## [1] 6
## 
## [[1]]$followers
## [1] 303
## 
## [[1]]$following
## [1] 22
## 
## [[1]]$created_at
## [1] "2011-03-09T17:29:25Z"
## 
## [[1]]$updated_at
## [1] "2016-10-11T11:05:06Z"

Se observa que la lista tiene varios objetos, ahora lo que queremos es organizar las 6 listas en formato de base de datos estándar, esto es, que cada información del usuario sea una variable (columna) y tener esto para los 6 usuarios (filas). Lo primero que hacemos es utilizar la función tibble(), que nos permite poner cada una de las 6 listas en una fila. El código es el siguiente:

View(usuarios)

Al observar nuestra base de datos usuarios, nos damos cuenta que ya tiene un formato de data frame pero la información se encuentra anidada, es decir, en cada fila se encuentra toda la información para cada usuario. Miremos la información para la primera fila (usuario):

##  [1] "login"               "id"                  "avatar_url"         
##  [4] "gravatar_id"         "url"                 "html_url"           
##  [7] "followers_url"       "following_url"       "gists_url"          
## [10] "starred_url"         "subscriptions_url"   "organizations_url"  
## [13] "repos_url"           "events_url"          "received_events_url"
## [16] "type"                "site_admin"          "name"               
## [19] "company"             "blog"                "location"           
## [22] "email"               "hireable"            "bio"                
## [25] "public_repos"        "public_gists"        "followers"          
## [28] "following"           "created_at"          "updated_at"

Podemos ver ahora cada uno de los componentes, lo que nos gustaría transformar a variables (columnas). Para ello podemos utilizar la función unnest_wider() de la siguiente forma:

View(data)

La función anterior, en efecto, nos permitió convertir cada componente en una columna, sin embargo, puede que no necesitemos todos esos componentes. Para ello, utilizamos la función hoist() que permite seleccionar y renombrar las columnas con las que nos queremos quedar en la base de datos final. El código es el siguiente:

View(data)

Como se puede observar, ya tenemos una base de datos con un formato estádar.

Nesting

Las funciones Nesting son opuestas a las Rectangling y permiten convertir un grupo de componentes en una sola fila anidada.

Miremos el siguiente ejemplo. Se genera un data frame con la función tibble(), la cual permite crear columnas como listas. El código es el siguiente:

View(df_anidado)

Hemos creado un data frame de tipo tibble anidado. En data, en cada fila es posible visualizar las variables y los valores. El código para visualizar cada elemento es:

View(df_anidado[[2]][[1]])
View(df_anidado[[2]][[2]])
View(df_anidado[[2]][[3]])

Para desanidar cada conjunto de datos se utiliza la función unnest() de la siguiente forma:

View(data)

Le hemos indicado a la función unnest() que desanidara todos los componentes anidados del data frame principal. Ahora podemos observar cada uno de esos elementos.

Splitting and Combining

Las funciones de este tipo nos permiten convertir una sola columnaen varias, o lo contrario, convertir varias columnas en una sola. A este tipo pertenecen las funciones separate(), extract() o unite().

Veamos un ejemplo para la función separate.

View(df)

View(data)

En el primer paso creamos un data frame con la función data.frame() que contaba con una sola columna, y lo separamos en otro data frame con dos columnas. Observemos otro ejemplo simple.

View(df1)

## Warning: Expected 4 pieces. Missing pieces filled with `NA` in 2 rows [2, 3].
View(data)

La función separate() toma los elementos de la columna dada en el primer argumento, en este caso ‘x’ y los separa en columnas de acuerdo al vector del segundo argumento. En df1 tenemos tres elementos de caracteres. En la columna “A” van los primeros elementos de la cadena, en la “B” los segundos y así sucesivamente. En el caso de que la cadena de caracteres no tenga los suficientes componentes R llenará el espacio con ‘NA’; y ¿si quisieramos tomar solo el segundo elemento? Se podría hacer lo siguiente:

View(df4)

Missings

Estas funciones nos permiten manipular los datos tipo NA. A este tipo pertenecen las funciones drop_na(), complete() y fill().

En el caso de drop_na() tenemos el siguiente ejemplo:

## Warning: Expected 2 pieces. Missing pieces filled with `NA` in 1 rows [1].
View(df1)

View(df2)

Se observa que ha borrado todas las filas donde habían valores missing

Respecto a complete() tenemos la siguiente base de datos simulada y construida utilizando la función separate(). La primera columna representa un identificador (ID) y la idea es poner los valores :

## Warning: Expected 3 pieces. Missing pieces filled with `NA` in 1 rows [2].
View(df1)

Ahora, a los valores missing en A y B se pondran los valores 3 y 0, respectivamente. El código es el siguiente utilizando la función complete():

View(df2)

El anterior código se puede resumir en:

## Warning: Expected 3 pieces. Missing pieces filled with `NA` in 1 rows [2].
## # A tibble: 4 x 3
##   ID    A     B    
##   <chr> <chr> <chr>
## 1 1     a     b    
## 2 2     3     0    
## 3 3     a     d    
## 4 4     b     c

10.3 Información adicional

El paquete tidyr cuenta con muchas funciones más allá de las que se presentaron brevemente en este capítulo. Por otro lado, las funciones aquí presentadas pueden seguir instrucciones aún más específicas. Por esto, resulta bastante interesante consultar la documentación al respecto en el siguiente link: https://tidyr.tidyverse.org/