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:
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.
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)
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 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.
library(readxl)
library(tidyverse)
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 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