Puedes representar los mismos datos subyacentes de múltiples formas. Cada set de datos muestra los mismos valores de cuatro variables country, year, cases, population, pero cada uno organiza los valores de forma distinta.

Las anteriores son representaciones de los mismos datos subyacentes, pero no todas son igualmente fáciles de usar. Un tipo de conjunto de datos, el conjunto de datos tidy (ordenado) será mucho más fácil de trabajar dentro del tidyverse.

Tabla 1 – consistente

Reglas de tidy data Existen tres reglas interrelacionadas que hacen que un conjunto de datos sea ordenado:

  1. Cada variable debe tener su propia columna.
  2. Cada observación debe tener su propia fila.
  3. Cada valor debe tener su propia celda.

Estas reglas están interrelacionadas ya que es imposible cumplir solo dos de las tres. Esta interrelación lleva a un conjunto práctico de instrucciones más simple aún:

Coloca cada conjunto de datos en un tibble. Coloca cada variable en una columna. En este ejemplo, solo la tabla1 es tidy. Es la única representación en que cada columna es una variable.

TIDY DIVERSE: CADA COLUMNA ES UNA VARIABLE!

¿Por qué asegurarse de que los datos estén ordenados? Existen dos ventajas principales:

Existe una ventaja general al elegir una forma consistente de almacenar datos. Si tienes una estructura de datos consistente, es más fácil aprender las herramientas que funcionan con ella ya que tienen una uniformidad subyacente.

Existe una ventaja específica al situar las variables en las columnas, ya que permite que la naturaleza vectorizada de R brille. Muchas de las funciones que vienen con R trabajan con vectores de valores. Esto hace que transformar datos ordenados se perciba como algo casi natural.

dplyr, ggplot2 y el resto de los paquetes del tidyverse están diseñados para trabajar con datos ordenados.

Pivotar

El primer paso es entender siempre cuáles son las variables y las observaciones. ¿Qué es lo que está midiendo el set de datos? Esto a veces es fácil; otras veces deberás consultar con quienes crearon el set de datos.

El segundo paso es resolver uno de los siguientes problemas frecuentes:

Una variable se extiende por varias columnas Una observación está dispersa entre múltiples filas. Típicamente, un set de datos tiene uno de estos problemas. Si contiene ambos ¡significa que tienes muy mala suerte!

Para solucionar estos problemas necesitarás las dos funciones más importantes de tidyr:

pivot_longer() (pivotar a lo largo), y pivot_wider() (pivotar a lo ancho).

Datos “largos” Un problema común es cuando en un dataset los nombres de las columnas no representan nombres de variables, sino que representan los valores de una variable.

Con estos parámetros podemos utilizar la función pivot_longer() (pivotar a lo largo):

library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.3     ✓ purrr   0.3.4
## ✓ tibble  3.1.1     ✓ dplyr   1.0.6
## ✓ tidyr   1.1.3     ✓ stringr 1.4.0
## ✓ readr   1.4.0     ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
library(tidyr)

Cargar datos

library(readxl)
table4a %>% 
  pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "cases")
## # A tibble: 6 x 3
##   country     year   cases
##   <chr>       <chr>  <int>
## 1 Afghanistan 1999     745
## 2 Afghanistan 2000    2666
## 3 Brazil      1999   37737
## 4 Brazil      2000   80488
## 5 China       1999  212258
## 6 China       2000  213766

Podemos usar pivot_longer() para ordenar tabla4b de modo similar. La única diferencia es la variable almacenada en los valores de las celdas.

table4b %>% 
  pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "population")
## # A tibble: 6 x 3
##   country     year  population
##   <chr>       <chr>      <int>
## 1 Afghanistan 1999    19987071
## 2 Afghanistan 2000    20595360
## 3 Brazil      1999   172006362
## 4 Brazil      2000   174504898
## 5 China       1999  1272915272
## 6 China       2000  1280428583

Para combinar las versiones ordenadas de tabla4a y tabla4b en un único tibble, necesitamos usar dplyr::left_join(), función necesaria para trabajar con datos relacionales.

tidy4a <- table4a %>% 
  pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "cases")
tidy4b <- table4b %>% 
  pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "population")

left_join(tidy4a, tidy4b)
## Joining, by = c("country", "year")
## # A tibble: 6 x 4
##   country     year   cases population
##   <chr>       <chr>  <int>      <int>
## 1 Afghanistan 1999     745   19987071
## 2 Afghanistan 2000    2666   20595360
## 3 Brazil      1999   37737  172006362
## 4 Brazil      2000   80488  174504898
## 5 China       1999  212258 1272915272
## 6 China       2000  213766 1280428583

Datos “anchos” pivot_wider() (pivotar a lo ancho) es lo opuesto de pivot_longer(). Se usa cuando una observación aparece en múltiples filas. Por ejemplo, considera la tabla2: una observación es un país en un año, pero cada observación aparece en dos filas.

Para ordenar esto, primero analizamos la representación de un modo similar a cómo se haría con pivot_longer(). Esta vez, sin embargo, necesitamos únicamente dos parámetros:

La columna desde la que obtener los nombres de las variables. En este caso corresponde a type. La columna desde la que obtener los valores. En este caso corresponde a count. Una vez resuelto esto, podemos usar pivot_wider(), como se muestra debajo.

table2 %>%
    pivot_wider(names_from = type, values_from = count)
## # A tibble: 6 x 4
##   country      year  cases population
##   <chr>       <int>  <int>      <int>
## 1 Afghanistan  1999    745   19987071
## 2 Afghanistan  2000   2666   20595360
## 3 Brazil       1999  37737  172006362
## 4 Brazil       2000  80488  174504898
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583

Como te habrás dado cuenta a partir de sus nombres, las funciones pivot_longer() y pivot_wider() son complementarias. pivot_longer() genera tablas angostas y largas, pivot_wider() genera tablas anchas y cortas (+ columnas, - filas).

gapminder <- readxl::read_xlsx("gapminder.xlsx")
gapminder %>% 
  pivot_longer(c(lifeExp,pop, gdpPercap), names_to = "variable", values_to = "valor") %>% 
  pivot_wider(names_from = variable, values_from = valor)
## # A tibble: 1,704 x 6
##    country     continent  year lifeExp      pop gdpPercap
##    <chr>       <chr>     <dbl>   <dbl>    <dbl>     <dbl>
##  1 Afghanistan Asia       1952    28.8  8425333      779.
##  2 Afghanistan Asia       1957    30.3  9240934      821.
##  3 Afghanistan Asia       1962    32.0 10267083      853.
##  4 Afghanistan Asia       1967    34.0 11537966      836.
##  5 Afghanistan Asia       1972    36.1 13079460      740.
##  6 Afghanistan Asia       1977    38.4 14880372      786.
##  7 Afghanistan Asia       1982    39.9 12881816      978.
##  8 Afghanistan Asia       1987    40.8 13867957      852.
##  9 Afghanistan Asia       1992    41.7 16317921      649.
## 10 Afghanistan Asia       1997    41.8 22227415      635.
## # … with 1,694 more rows

Separar y unir

Separar separate() desarma una columna en varias columnas, dividiendo de acuerdo a la posición de un carácter separador. Tomemos la tabla3:

La columna rate contiene tanto los casos como la población, por lo que necesitamos dividirla en dos variables. La función separate()toma el nombre de la columna a separar y el nombre de las columnas a donde irá el resultado, tal como se muestra en el código a continuación.

table3 %>% 
  separate(rate, into = c("cases", "population"))
## # A tibble: 6 x 4
##   country      year cases  population
##   <chr>       <int> <chr>  <chr>     
## 1 Afghanistan  1999 745    19987071  
## 2 Afghanistan  2000 2666   20595360  
## 3 Brazil       1999 37737  172006362 
## 4 Brazil       2000 80488  174504898 
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583

Por defecto, separate() dividirá una columna donde encuentre un carácter no alfanumérico (esto es, un carácter que no es un número o letra). Por ejemplo, en el siguiente código, separate() divide los valores de rate donde aparece una barra (/). Si deseas usar un carácter específico para separar una columna, puedes especificarlo en el argumento sep de separate(). Siempre es mejor trabajar con un separador definido.

Por ejemplo, el código anterior se puede re-escribir del siguiente modo:

table3 %>% 
  separate(rate, into = c("cases", "population"), sep = "/")
## # A tibble: 6 x 4
##   country      year cases  population
##   <chr>       <int> <chr>  <chr>     
## 1 Afghanistan  1999 745    19987071  
## 2 Afghanistan  2000 2666   20595360  
## 3 Brazil       1999 37737  172006362 
## 4 Brazil       2000 80488  174504898 
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583

Mira atentamente los tipos de columna: notarás que cases y population son columnas de tipo carácter. Este es el comportamiento por defecto en separate(): preserva el tipo de columna. Aquí, sin embargo, no es muy útil, ya que se trata de números. Podemos pedir a separate() que intente convertir a un tipo más adecuado usando convert = TRUE: Recomendación: usar mutate con coerción explícita

table3 %>% 
  separate(rate, into = c("cases", "population"), convert = TRUE)
## # A tibble: 6 x 4
##   country      year  cases population
##   <chr>       <int>  <int>      <int>
## 1 Afghanistan  1999    745   19987071
## 2 Afghanistan  2000   2666   20595360
## 3 Brazil       1999  37737  172006362
## 4 Brazil       2000  80488  174504898
## 5 China        1999 212258 1272915272
## 6 China        2000 213766 1280428583

Unir unite() es el inverso de separate(): combina múltiples columnas en una única columna. Necesitarás esta función con mucha menos frecuencia que separate(), pero aún así es una buena herramienta para conocer.

Podemos usar unite() para volver a unir las columnas que acabamos de separar.

Usamos col = rate para indicar que la nueva columna debe llamarse rate.

EJERCICIOS

Cargando paquete

library(readxl)
library(tidyverse)

Cargando data

mujeres_sida <-read_csv("sida_mujeres_peru.csv", col_types = "dddddd")
## Warning: 2 parsing failures.
## row              col expected actual                    file
##   2 De 50 a 59 años  a double      - 'sida_mujeres_peru.csv'
##   2 De 60 años y más a double      - 'sida_mujeres_peru.csv'

Usando el set de datos “sida_mujeres_peru.csv”

mujeres_sida 
## # A tibble: 29 x 6
##      año `De 0 a 14 años` `De 15 a 24 años` `De 25 a 49 años` `De 50 a 59 años`
##    <dbl>            <dbl>             <dbl>             <dbl>             <dbl>
##  1  1990                2                 9                19                 1
##  2  1991                3                13                31                NA
##  3  1992                4                26                57                 7
##  4  1993               15                32                51                 5
##  5  1994               14                44                84                 4
##  6  1995               16                49               143                16
##  7  1996               22                69               176                16
##  8  1997               19                60               175                15
##  9  1998               18                95               207                 6
## 10  1999               17                78               205                20
## # … with 19 more rows, and 1 more variable: De 60 años y más <dbl>

Convertir el dataset a un formato tidy pivot_longer – si los datos están repartidos en varias columnas pivot_wider – si los datos están repartidos en varias filar

mujeres_sida %>% 
   pivot_longer(c(`De 0 a 14 años`, `De 15 a 24 años`, `De 25 a 49 años`,`De 50 a 59 años`, `De 60 años y más`), names_to = "grupo etario", values_to = "cases")
## # A tibble: 145 x 3
##      año `grupo etario`   cases
##    <dbl> <chr>            <dbl>
##  1  1990 De 0 a 14 años       2
##  2  1990 De 15 a 24 años      9
##  3  1990 De 25 a 49 años     19
##  4  1990 De 50 a 59 años      1
##  5  1990 De 60 años y más     1
##  6  1991 De 0 a 14 años       3
##  7  1991 De 15 a 24 años     13
##  8  1991 De 25 a 49 años     31
##  9  1991 De 50 a 59 años     NA
## 10  1991 De 60 años y más    NA
## # … with 135 more rows

Usando el set de datos “fallecidos_covid.csv”

fallecidos_covid <-read_csv("fallecidos_covid.csv")
## 
## ── Column specification ────────────────────────────────────────────────────────
## cols(
##   FECHA_CORTE = col_character(),
##   UUID = col_character(),
##   FECHA_FALLECIMIENTO = col_character(),
##   EDAD_DECLARADA = col_double(),
##   SEXO = col_character(),
##   FECHA_NAC = col_character(),
##   DEPARTAMENTO = col_character(),
##   PROVINCIA = col_character(),
##   DISTRITO = col_character()
## )
fallecidos_covid 
## # A tibble: 41,181 x 9
##    FECHA_CORTE UUID            FECHA_FALLECIMIE… EDAD_DECLARADA SEXO   FECHA_NAC
##    <chr>       <chr>           <chr>                      <dbl> <chr>  <chr>    
##  1 2021.01.31  7320cabdc1aaca… 2020.03.19                    78 MASCU… 1941.10.…
##  2 2021.01.31  e81602051997ac… 2020.03.19                    69 MASCU… 1951.03.…
##  3 2021.01.31  cecdbf10074dbc… 2020.03.21                    83 MASCU… 1939.08.…
##  4 2021.01.31  71ecb6bccb248b… 2020.03.24                    65 FEMEN… 1954.01.…
##  5 2021.01.31  566af4276cbe93… 2020.03.24                    76 MASCU… <NA>     
##  6 2021.01.31  027561e9d126e7… 2020.03.24                    94 MASCU… 1925.12.…
##  7 2021.01.31  f016889b9ba5bd… 2020.03.26                    53 MASCU… 1966.05.…
##  8 2021.01.31  971f8e12955837… 2020.03.26                    65 MASCU… 1955.02.…
##  9 2021.01.31  bc45b71b005a96… 2020.03.26                    43 MASCU… 1977.01.…
## 10 2021.01.31  0e2a1928ddd07d… 2020.03.26                    66 MASCU… 1953.12.…
## # … with 41,171 more rows, and 3 more variables: DEPARTAMENTO <chr>,
## #   PROVINCIA <chr>, DISTRITO <chr>

para filas – filtrar y columnas - select

fallecidos_covid  %>% 
   separate(FECHA_NAC, into = c("año", "mes","dia"), sep = "\\.")
## # A tibble: 41,181 x 11
##    FECHA_CORTE UUID      FECHA_FALLECIMI… EDAD_DECLARADA SEXO  año   mes   dia  
##    <chr>       <chr>     <chr>                     <dbl> <chr> <chr> <chr> <chr>
##  1 2021.01.31  7320cabd… 2020.03.19                   78 MASC… 1941  10    13   
##  2 2021.01.31  e8160205… 2020.03.19                   69 MASC… 1951  03    05   
##  3 2021.01.31  cecdbf10… 2020.03.21                   83 MASC… 1939  08    17   
##  4 2021.01.31  71ecb6bc… 2020.03.24                   65 FEME… 1954  01    04   
##  5 2021.01.31  566af427… 2020.03.24                   76 MASC… <NA>  <NA>  <NA> 
##  6 2021.01.31  027561e9… 2020.03.24                   94 MASC… 1925  12    06   
##  7 2021.01.31  f016889b… 2020.03.26                   53 MASC… 1966  05    27   
##  8 2021.01.31  971f8e12… 2020.03.26                   65 MASC… 1955  02    17   
##  9 2021.01.31  bc45b71b… 2020.03.26                   43 MASC… 1977  01    29   
## 10 2021.01.31  0e2a1928… 2020.03.26                   66 MASC… 1953  12    18   
## # … with 41,171 more rows, and 3 more variables: DEPARTAMENTO <chr>,
## #   PROVINCIA <chr>, DISTRITO <chr>
fallecidos_covid  %>% 
  separate(FECHA_NAC, into = c("año", "mes","dia"), sep = "\\.") %>% 
  filter(año == "1960") %>% 
  summarise(numero_fallecidos = n())
## # A tibble: 1 x 1
##   numero_fallecidos
##               <int>
## 1               765

Obtener el recuento de fallecidos que nacieron en 1960.

El número de fallecidos que nacieron en 1960 son 765 personas.

Ejercicio práctico en conjunto

Ejercicio 1 # Cargando data

mujeres_sida <-read_csv("sida_mujeres_peru.csv", col_types = "dddddd")
## Warning: 2 parsing failures.
## row              col expected actual                    file
##   2 De 50 a 59 años  a double      - 'sida_mujeres_peru.csv'
##   2 De 60 años y más a double      - 'sida_mujeres_peru.csv'
mujeres_sida %>% 
   pivot_longer(-año, names_to = "grupo etario", values_to = "cases")
## # A tibble: 145 x 3
##      año `grupo etario`   cases
##    <dbl> <chr>            <dbl>
##  1  1990 De 0 a 14 años       2
##  2  1990 De 15 a 24 años      9
##  3  1990 De 25 a 49 años     19
##  4  1990 De 50 a 59 años      1
##  5  1990 De 60 años y más     1
##  6  1991 De 0 a 14 años       3
##  7  1991 De 15 a 24 años     13
##  8  1991 De 25 a 49 años     31
##  9  1991 De 50 a 59 años     NA
## 10  1991 De 60 años y más    NA
## # … with 135 more rows

Ejercicio 2

fallecidos_covid <-read_csv("fallecidos_covid.csv")
## 
## ── Column specification ────────────────────────────────────────────────────────
## cols(
##   FECHA_CORTE = col_character(),
##   UUID = col_character(),
##   FECHA_FALLECIMIENTO = col_character(),
##   EDAD_DECLARADA = col_double(),
##   SEXO = col_character(),
##   FECHA_NAC = col_character(),
##   DEPARTAMENTO = col_character(),
##   PROVINCIA = col_character(),
##   DISTRITO = col_character()
## )
fallecidos_covid 
## # A tibble: 41,181 x 9
##    FECHA_CORTE UUID            FECHA_FALLECIMIE… EDAD_DECLARADA SEXO   FECHA_NAC
##    <chr>       <chr>           <chr>                      <dbl> <chr>  <chr>    
##  1 2021.01.31  7320cabdc1aaca… 2020.03.19                    78 MASCU… 1941.10.…
##  2 2021.01.31  e81602051997ac… 2020.03.19                    69 MASCU… 1951.03.…
##  3 2021.01.31  cecdbf10074dbc… 2020.03.21                    83 MASCU… 1939.08.…
##  4 2021.01.31  71ecb6bccb248b… 2020.03.24                    65 FEMEN… 1954.01.…
##  5 2021.01.31  566af4276cbe93… 2020.03.24                    76 MASCU… <NA>     
##  6 2021.01.31  027561e9d126e7… 2020.03.24                    94 MASCU… 1925.12.…
##  7 2021.01.31  f016889b9ba5bd… 2020.03.26                    53 MASCU… 1966.05.…
##  8 2021.01.31  971f8e12955837… 2020.03.26                    65 MASCU… 1955.02.…
##  9 2021.01.31  bc45b71b005a96… 2020.03.26                    43 MASCU… 1977.01.…
## 10 2021.01.31  0e2a1928ddd07d… 2020.03.26                    66 MASCU… 1953.12.…
## # … with 41,171 more rows, and 3 more variables: DEPARTAMENTO <chr>,
## #   PROVINCIA <chr>, DISTRITO <chr>
fallecidos_covid  %>% 
  separate(FECHA_NAC, into = c("año", "mes","dia"), sep = "\\.") %>% 
  filter(año == "1960") %>% 
  summarise(n = n())
## # A tibble: 1 x 1
##       n
##   <int>
## 1   765