Paquete R tidyr y sus nuevas funciones pivot_longer y pivot_wider

Paquete ordenar incluido en el núcleo de una de las bibliotecas más populares en el lenguaje R: tidyverse.
El objetivo principal del paquete es llevar los datos de forma precisa.

Ya disponible en Habré publicación dedicado a este paquete, pero se remonta a 2015. Y quiero hablaros de los cambios más actuales, que fueron anunciados hace unos días por su autor, Hedley Wickham.

Paquete R tidyr y sus nuevas funciones pivot_longer y pivot_wider

SJK: ¿Recopilar() y difundir() quedarán obsoletos?

hadley wickham: Hasta cierto punto. Ya no recomendaremos el uso de estas funciones ni corregiremos errores en ellas, pero seguirán estando presentes en el paquete en su estado actual.

contenido

Si te interesa el análisis de datos, quizás te interese mi Telegram. и Youtube canales. La mayor parte del contenido está dedicado al lenguaje R.

Concepto de datos ordenados

objetivo ordenar — le ayudará a llevar los datos a la denominada forma ordenada. Los datos ordenados son datos donde:

  • Cada variable está en una columna.
  • Cada observación es una cadena.
  • Cada valor es una celda.

Es mucho más fácil y conveniente trabajar con datos que se presentan ordenados al realizar el análisis.

Funciones principales incluidas en el paquete tidyr

tidyr contiene un conjunto de funciones diseñadas para transformar tablas:

  • fill() — completar los valores faltantes en una columna con valores anteriores;
  • separate() — divide un campo en varios usando un separador;
  • unite() — realiza la operación de combinar varios campos en uno, la acción inversa de la función separate();
  • pivot_longer() — una función que convierte datos de formato ancho a formato largo;
  • pivot_wider() - una función que convierte datos de formato largo a formato ancho. La operación inversa a la que realiza la función. pivot_longer().
  • gather()obsoleto — una función que convierte datos de formato ancho a formato largo;
  • spread()obsoleto - una función que convierte datos de formato largo a formato ancho. La operación inversa a la que realiza la función. gather().

Nuevo concepto para convertir datos de formato ancho a largo y viceversa

Anteriormente, se utilizaban funciones para este tipo de transformación. gather() и spread(). A lo largo de los años de existencia de estas funciones, se hizo evidente que para la mayoría de los usuarios, incluido el autor del paquete, los nombres de estas funciones y sus argumentos no eran del todo obvios, y causaban dificultades para encontrarlas y comprender cuál de estas funciones convierte un marco de fecha de formato ancho a largo, y viceversa.

En este sentido, en ordenar Se han agregado dos funciones nuevas e importantes diseñadas para transformar marcos de fechas.

Nuevas características pivot_longer() и pivot_wider() se inspiraron en algunas de las características del paquete cdatos, creado por John Mount y Nina Zumel.

Instalación de la versión más actual de tidyr 0.8.3.9000

Para instalar la versión nueva y más actual del paquete ordenar 0.8.3.9000, donde haya nuevas funciones disponibles, utilice el siguiente código.

devtools::install_github("tidyverse/tidyr")

Al momento de escribir este artículo, estas funciones solo están disponibles en la versión de desarrollo del paquete en GitHub.

Transición a nuevas funciones

De hecho, no es difícil transferir scripts antiguos para trabajar con nuevas funciones; para una mejor comprensión, tomaré un ejemplo de la documentación de funciones antiguas y mostraré cómo se realizan las mismas operaciones usando otras nuevas. pivot_*() funciones

Convierta formato ancho a formato largo.

Código de ejemplo de la documentación de la función de recopilación

# example
library(dplyr)
stocks <- data.frame(
  time = as.Date('2009-01-01') + 0:9,
  X = rnorm(10, 0, 1),
  Y = rnorm(10, 0, 2),
  Z = rnorm(10, 0, 4)
)

# old
stocks_gather <- stocks %>% gather(key   = stock, 
                                   value = price, 
                                   -time)

# new
stocks_long   <- stocks %>% pivot_longer(cols      = -time, 
                                       names_to  = "stock", 
                                       values_to = "price")

Conversión de formato largo a formato ancho.

Código de ejemplo de la documentación de la función de extensión

# old
stocks_spread <- stocks_gather %>% spread(key = stock, 
                                          value = price) 

# new 
stock_wide    <- stocks_long %>% pivot_wider(names_from  = "stock",
                                            values_from = "price")

Porque en los ejemplos anteriores de trabajo con pivot_longer() и pivot_wider(), en la tabla original acciones no hay columnas enumeradas en los argumentos nombres_a и valores_a sus nombres deben estar entre comillas.

Una tabla que le ayudará a descubrir más fácilmente cómo pasar a trabajar con un nuevo concepto. ordenar.

Paquete R tidyr y sus nuevas funciones pivot_longer y pivot_wider

Nota del autor

Todo el texto a continuación es adaptable, incluso diría que es traducción gratuita. viñetas del sitio web oficial de la biblioteca tidyverse.

Un ejemplo sencillo de conversión de datos de formato ancho a largo

pivot_longer () — alarga los conjuntos de datos al reducir el número de columnas y aumentar el número de filas.

Paquete R tidyr y sus nuevas funciones pivot_longer y pivot_wider

Para ejecutar los ejemplos presentados en el artículo, primero debe conectar los paquetes necesarios:

library(tidyr)
library(dplyr)
library(readr)

Digamos que tenemos una tabla con los resultados de una encuesta que (entre otras cosas) preguntaba a la gente sobre su religión e ingresos anuales:

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

Esta tabla contiene datos religiosos de los encuestados en filas y los niveles de ingresos están dispersos en los nombres de las columnas. El número de encuestados de cada categoría se almacena en los valores de celda en la intersección de religión y nivel de ingresos. Para que la tabla tenga un formato ordenado y correcto, basta con utilizar pivot_longer():

pew %>% 
  pivot_longer(cols = -religion, names_to = "income", values_to = "count")

pew %>% 
  pivot_longer(cols = -religion, names_to = "income", values_to = "count")
#> # A tibble: 180 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
#> # … with 170 more rows

Argumentos de función pivot_longer()

  • Primer argumento cols, describe qué columnas deben fusionarse. En este caso, todas las columnas excepto equipo.
  • Argumento nombres_a da el nombre de la variable que se creará a partir de los nombres de las columnas que concatenamos.
  • valores_a da el nombre de una variable que se creará a partir de los datos almacenados en los valores de las celdas de las columnas fusionadas.

Especificaciones

Esta es una nueva funcionalidad del paquete. ordenar, que anteriormente no estaba disponible cuando se trabajaba con funciones heredadas.

Una especificación es un marco de datos, cada fila del cual corresponde a una columna en el nuevo marco de fechas de salida y dos columnas especiales que comienzan con:

  • . Nombre contiene el nombre de la columna original.
  • valor contiene el nombre de la columna que contendrá los valores de la celda.

Las columnas restantes de la especificación reflejan cómo la nueva columna mostrará el nombre de las columnas comprimidas de . Nombre.

La especificación describe los metadatos almacenados en un nombre de columna, con una fila para cada columna y una columna para cada variable, combinados con el nombre de la columna. Esta definición puede parecer confusa en este momento, pero después de ver algunos ejemplos se volverá mucho más confusa. más claro.

El objetivo de la especificación es que puede recuperar, modificar y definir nuevos metadatos para el marco de datos que se está convirtiendo.

Para trabajar con especificaciones al convertir una tabla de un formato ancho a un formato largo, use la función pivot_longer_spec().

El funcionamiento de esta función es que toma cualquier marco de fechas y genera sus metadatos de la manera descrita anteriormente.

Como ejemplo, tomemos el conjunto de datos who que se proporciona con el paquete. ordenar. Este conjunto de datos contiene información proporcionada por la organización internacional de la salud sobre la incidencia de la tuberculosis.

who
#> # A tibble: 7,240 x 60
#>    country iso2  iso3   year new_sp_m014 new_sp_m1524 new_sp_m2534
#>    <chr>   <chr> <chr> <int>       <int>        <int>        <int>
#>  1 Afghan… AF    AFG    1980          NA           NA           NA
#>  2 Afghan… AF    AFG    1981          NA           NA           NA
#>  3 Afghan… AF    AFG    1982          NA           NA           NA
#>  4 Afghan… AF    AFG    1983          NA           NA           NA
#>  5 Afghan… AF    AFG    1984          NA           NA           NA
#>  6 Afghan… AF    AFG    1985          NA           NA           NA
#>  7 Afghan… AF    AFG    1986          NA           NA           NA
#>  8 Afghan… AF    AFG    1987          NA           NA           NA
#>  9 Afghan… AF    AFG    1988          NA           NA           NA
#> 10 Afghan… AF    AFG    1989          NA           NA           NA
#> # … with 7,230 more rows, and 53 more variables

Construyamos su especificación.

spec <- who %>%
  pivot_longer_spec(new_sp_m014:newrel_f65, values_to = "count")

#> # A tibble: 56 x 3
#>    .name        .value name        
#>    <chr>        <chr>  <chr>       
#>  1 new_sp_m014  count  new_sp_m014 
#>  2 new_sp_m1524 count  new_sp_m1524
#>  3 new_sp_m2534 count  new_sp_m2534
#>  4 new_sp_m3544 count  new_sp_m3544
#>  5 new_sp_m4554 count  new_sp_m4554
#>  6 new_sp_m5564 count  new_sp_m5564
#>  7 new_sp_m65   count  new_sp_m65  
#>  8 new_sp_f014  count  new_sp_f014 
#>  9 new_sp_f1524 count  new_sp_f1524
#> 10 new_sp_f2534 count  new_sp_f2534
#> # … with 46 more rows

campos país, iso2, iso3 ya son variables. Nuestra tarea es voltear las columnas con nuevo_sp_m014 en newrel_f65.

Los nombres de estas columnas almacenan la siguiente información:

  • Prefijo new_ indica que la columna contiene datos sobre nuevos casos de tuberculosis, el marco de fechas actual contiene información solo sobre nuevas enfermedades, por lo que este prefijo en el contexto actual no tiene ningún significado.
  • sp/rel/sp/ep describe un método para diagnosticar una enfermedad.
  • m/f género del paciente.
  • 014/1524/2535/3544/4554/65 rango de edad del paciente.

Podemos dividir estas columnas usando la función extract()usando expresiones regulares.

spec <- spec %>%
        extract(name, c("diagnosis", "gender", "age"), "new_?(.*)_(.)(.*)")

#> # A tibble: 56 x 5
#>    .name        .value diagnosis gender age  
#>    <chr>        <chr>  <chr>     <chr>  <chr>
#>  1 new_sp_m014  count  sp        m      014  
#>  2 new_sp_m1524 count  sp        m      1524 
#>  3 new_sp_m2534 count  sp        m      2534 
#>  4 new_sp_m3544 count  sp        m      3544 
#>  5 new_sp_m4554 count  sp        m      4554 
#>  6 new_sp_m5564 count  sp        m      5564 
#>  7 new_sp_m65   count  sp        m      65   
#>  8 new_sp_f014  count  sp        f      014  
#>  9 new_sp_f1524 count  sp        f      1524 
#> 10 new_sp_f2534 count  sp        f      2534 
#> # … with 46 more rows

Tenga en cuenta la columna . Nombre debe permanecer sin cambios ya que este es nuestro índice en los nombres de las columnas del conjunto de datos original.

Género y edad (columnas género и edad ) tienen valores fijos y conocidos, por lo que se recomienda convertir estas columnas a factores:

spec <-  spec %>%
            mutate(
              gender = factor(gender, levels = c("f", "m")),
              age = factor(age, levels = unique(age), ordered = TRUE)
            ) 

Finalmente, para aplicar la especificación que creamos al marco de fechas original (entornos urbanos), liderados por sus Ayuntamientos, que son responsables de validar e integrar las herramientas en su propio contexto aportando sus necesidades y retos. necesitamos usar un argumento especulación en función pivot_longer().

who %>% pivot_longer(spec = spec)

#> # A tibble: 405,440 x 8
#>    country     iso2  iso3   year diagnosis gender age   count
#>    <chr>       <chr> <chr> <int> <chr>     <fct>  <ord> <int>
#>  1 Afghanistan AF    AFG    1980 sp        m      014      NA
#>  2 Afghanistan AF    AFG    1980 sp        m      1524     NA
#>  3 Afghanistan AF    AFG    1980 sp        m      2534     NA
#>  4 Afghanistan AF    AFG    1980 sp        m      3544     NA
#>  5 Afghanistan AF    AFG    1980 sp        m      4554     NA
#>  6 Afghanistan AF    AFG    1980 sp        m      5564     NA
#>  7 Afghanistan AF    AFG    1980 sp        m      65       NA
#>  8 Afghanistan AF    AFG    1980 sp        f      014      NA
#>  9 Afghanistan AF    AFG    1980 sp        f      1524     NA
#> 10 Afghanistan AF    AFG    1980 sp        f      2534     NA
#> # … with 405,430 more rows

Todo lo que acabamos de hacer se puede representar esquemáticamente de la siguiente manera:

Paquete R tidyr y sus nuevas funciones pivot_longer y pivot_wider

Especificación utilizando múltiples valores (.value)

En el ejemplo anterior, la columna de especificación valor contenía solo un valor, en la mayoría de los casos este es el caso.

Pero ocasionalmente puede surgir una situación en la que necesite recopilar datos de columnas con diferentes tipos de datos en valores. Usando una función heredada spread() esto sería bastante difícil de hacer.

El siguiente ejemplo está tomado de viñetas al paquete tabla de datos.

Creemos un marco de datos de entrenamiento.

family <- tibble::tribble(
  ~family,  ~dob_child1,  ~dob_child2, ~gender_child1, ~gender_child2,
       1L, "1998-11-26", "2000-01-29",             1L,             2L,
       2L, "1996-06-22",           NA,             2L,             NA,
       3L, "2002-07-11", "2004-04-05",             2L,             2L,
       4L, "2004-10-10", "2009-08-27",             1L,             1L,
       5L, "2000-12-05", "2005-02-28",             2L,             1L,
)
family <- family %>% mutate_at(vars(starts_with("dob")), parse_date)

#> # A tibble: 5 x 5
#>   family dob_child1 dob_child2 gender_child1 gender_child2
#>    <int> <date>     <date>             <int>         <int>
#> 1      1 1998-11-26 2000-01-29             1             2
#> 2      2 1996-06-22 NA                     2            NA
#> 3      3 2002-07-11 2004-04-05             2             2
#> 4      4 2004-10-10 2009-08-27             1             1
#> 5      5 2000-12-05 2005-02-28             2             1

El marco de fechas creado contiene datos sobre los hijos de una familia en cada fila. Las familias pueden tener uno o dos hijos. Para cada niño, se proporcionan datos sobre la fecha de nacimiento y el sexo, y los datos de cada niño están en columnas separadas; nuestra tarea es llevar estos datos al formato correcto para su análisis.

Tenga en cuenta que tenemos dos variables con información de cada niño: su sexo y fecha de nacimiento (columnas con el prefijo bautismo contiene fecha de nacimiento, columnas con prefijo género contener el sexo del niño). El resultado esperado es que aparezcan en columnas separadas. Podemos hacer esto generando una especificación en la que la columna .value tendrá dos significados diferentes.

spec <- family %>%
  pivot_longer_spec(-family) %>%
  separate(col = name, into = c(".value", "child"))%>%
  mutate(child = parse_number(child))

#> # A tibble: 4 x 3
#>   .name         .value child
#>   <chr>         <chr>  <dbl>
#> 1 dob_child1    dob        1
#> 2 dob_child2    dob        2
#> 3 gender_child1 gender     1
#> 4 gender_child2 gender     2

Entonces, echemos un vistazo paso a paso a las acciones realizadas por el código anterior.

  • pivot_longer_spec(-family) — cree una especificación que comprima todas las columnas existentes excepto la columna de familia.
  • separate(col = name, into = c(".value", "child")) - dividir la columna . Nombre, que contiene los nombres de los campos de origen, usando el guión bajo e ingresando los valores resultantes en las columnas valor и sus hijos.
  • mutate(child = parse_number(child)) — transformar los valores del campo sus hijos de texto a tipo de datos numérico.

Ahora podemos aplicar la especificación resultante al marco de datos original y llevar la tabla al formato deseado.

family %>% 
    pivot_longer(spec = spec, na.rm = T)

#> # A tibble: 9 x 4
#>   family child dob        gender
#>    <int> <dbl> <date>      <int>
#> 1      1     1 1998-11-26      1
#> 2      1     2 2000-01-29      2
#> 3      2     1 1996-06-22      2
#> 4      3     1 2002-07-11      2
#> 5      3     2 2004-04-05      2
#> 6      4     1 2004-10-10      1
#> 7      4     2 2009-08-27      1
#> 8      5     1 2000-12-05      2
#> 9      5     2 2005-02-28      1

Usamos argumento na.rm = TRUE, porque la forma actual de los datos obliga a la creación de filas adicionales para observaciones inexistentes. Porque la familia 2 tiene un solo hijo, na.rm = TRUE garantiza que la familia 2 tendrá una fila en la salida.

Conversión de marcos de fecha de formato largo a ancho

pivot_wider() - es la transformación inversa, y viceversa aumenta el número de columnas del marco de fecha al reducir el número de filas.

Paquete R tidyr y sus nuevas funciones pivot_longer y pivot_wider

Este tipo de transformación rara vez se utiliza para dar un formato preciso a los datos; sin embargo, esta técnica puede ser útil para crear tablas dinámicas utilizadas en presentaciones o para la integración con otras herramientas.

En realidad las funciones pivot_longer() и pivot_wider() son simétricos y producen acciones inversas entre sí, es decir: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) devolverá el df original.

El ejemplo más sencillo de convertir una tabla a formato ancho.

Para demostrar cómo funciona la función. pivot_wider() usaremos el conjunto de datos encuentros_con-peces, que almacena información sobre cómo las diferentes estaciones registran el movimiento de los peces a lo largo del río.

#> # A tibble: 114 x 3
#>    fish  station  seen
#>    <fct> <fct>   <int>
#>  1 4842  Release     1
#>  2 4842  I80_1       1
#>  3 4842  Lisbon      1
#>  4 4842  Rstr        1
#>  5 4842  Base_TD     1
#>  6 4842  BCE         1
#>  7 4842  BCW         1
#>  8 4842  BCE2        1
#>  9 4842  BCW2        1
#> 10 4842  MAE         1
#> # … with 104 more rows

En la mayoría de los casos, esta tabla será más informativa y más fácil de usar si presenta la información de cada estación en una columna separada.

fish_encounters %>% pivot_wider(names_from = station, values_from = seen)

fish_encounters %>% pivot_wider(names_from = station, values_from = seen)
#> # A tibble: 19 x 12
#>    fish  Release I80_1 Lisbon  Rstr Base_TD   BCE   BCW  BCE2  BCW2   MAE
#>    <fct>   <int> <int>  <int> <int>   <int> <int> <int> <int> <int> <int>
#>  1 4842        1     1      1     1       1     1     1     1     1     1
#>  2 4843        1     1      1     1       1     1     1     1     1     1
#>  3 4844        1     1      1     1       1     1     1     1     1     1
#>  4 4845        1     1      1     1       1    NA    NA    NA    NA    NA
#>  5 4847        1     1      1    NA      NA    NA    NA    NA    NA    NA
#>  6 4848        1     1      1     1      NA    NA    NA    NA    NA    NA
#>  7 4849        1     1     NA    NA      NA    NA    NA    NA    NA    NA
#>  8 4850        1     1     NA     1       1     1     1    NA    NA    NA
#>  9 4851        1     1     NA    NA      NA    NA    NA    NA    NA    NA
#> 10 4854        1     1     NA    NA      NA    NA    NA    NA    NA    NA
#> # … with 9 more rows, and 1 more variable: MAW <int>

Este conjunto de datos solo registra información cuando la estación ha detectado peces, es decir, Si alguna estación no registró algún pez, estos datos no estarán en la tabla. Esto significa que la salida se llenará con NA.

Sin embargo, en este caso sabemos que la ausencia de un registro significa que el pez no fue visto, por lo que podemos usar el argumento valores_relleno en función pivot_wider() y llena estos valores faltantes con ceros:

fish_encounters %>% pivot_wider(
  names_from = station, 
  values_from = seen,
  values_fill = list(seen = 0)
)

#> # A tibble: 19 x 12
#>    fish  Release I80_1 Lisbon  Rstr Base_TD   BCE   BCW  BCE2  BCW2   MAE
#>    <fct>   <int> <int>  <int> <int>   <int> <int> <int> <int> <int> <int>
#>  1 4842        1     1      1     1       1     1     1     1     1     1
#>  2 4843        1     1      1     1       1     1     1     1     1     1
#>  3 4844        1     1      1     1       1     1     1     1     1     1
#>  4 4845        1     1      1     1       1     0     0     0     0     0
#>  5 4847        1     1      1     0       0     0     0     0     0     0
#>  6 4848        1     1      1     1       0     0     0     0     0     0
#>  7 4849        1     1      0     0       0     0     0     0     0     0
#>  8 4850        1     1      0     1       1     1     1     0     0     0
#>  9 4851        1     1      0     0       0     0     0     0     0     0
#> 10 4854        1     1      0     0       0     0     0     0     0     0
#> # … with 9 more rows, and 1 more variable: MAW <int>

Generar un nombre de columna a partir de múltiples variables fuente

Imaginemos que tenemos una tabla que contiene una combinación de producto, país y año. Para generar un marco de fecha de prueba, puede ejecutar el siguiente código:

df <- expand_grid(
  product = c("A", "B"), 
  country = c("AI", "EI"), 
  year = 2000:2014
) %>%
  filter((product == "A" & country == "AI") | product == "B") %>% 
  mutate(value = rnorm(nrow(.)))

#> # A tibble: 45 x 4
#>    product country  year    value
#>    <chr>   <chr>   <int>    <dbl>
#>  1 A       AI       2000 -2.05   
#>  2 A       AI       2001 -0.676  
#>  3 A       AI       2002  1.60   
#>  4 A       AI       2003 -0.353  
#>  5 A       AI       2004 -0.00530
#>  6 A       AI       2005  0.442  
#>  7 A       AI       2006 -0.610  
#>  8 A       AI       2007 -2.77   
#>  9 A       AI       2008  0.899  
#> 10 A       AI       2009 -0.106  
#> # … with 35 more rows

Nuestra tarea es expandir el marco de datos para que una columna contenga datos para cada combinación de producto y país. Para hacer esto, simplemente pase el argumento. nombres_de un vector que contiene los nombres de los campos que se van a fusionar.

df %>% pivot_wider(names_from = c(product, country),
                 values_from = "value")

#> # A tibble: 15 x 4
#>     year     A_AI    B_AI    B_EI
#>    <int>    <dbl>   <dbl>   <dbl>
#>  1  2000 -2.05     0.607   1.20  
#>  2  2001 -0.676    1.65   -0.114 
#>  3  2002  1.60    -0.0245  0.501 
#>  4  2003 -0.353    1.30   -0.459 
#>  5  2004 -0.00530  0.921  -0.0589
#>  6  2005  0.442   -1.55    0.594 
#>  7  2006 -0.610    0.380  -1.28  
#>  8  2007 -2.77     0.830   0.637 
#>  9  2008  0.899    0.0175 -1.30  
#> 10  2009 -0.106   -0.195   1.03  
#> # … with 5 more rows

También puede aplicar especificaciones a una función. pivot_wider(). Pero cuando se presenta a pivot_wider() la especificación hace la conversión opuesta pivot_longer(): Las columnas especificadas en . Nombre, usando valores de valor y otras columnas.

Para este conjunto de datos, puede generar una especificación personalizada si desea que cada combinación posible de países y productos tenga su propia columna, no solo las presentes en los datos:

spec <- df %>% 
  expand(product, country, .value = "value") %>% 
  unite(".name", product, country, remove = FALSE)

#> # A tibble: 4 x 4
#>   .name product country .value
#>   <chr> <chr>   <chr>   <chr> 
#> 1 A_AI  A       AI      value 
#> 2 A_EI  A       EI      value 
#> 3 B_AI  B       AI      value 
#> 4 B_EI  B       EI      value

df %>% pivot_wider(spec = spec) %>% head()

#> # A tibble: 6 x 5
#>    year     A_AI  A_EI    B_AI    B_EI
#>   <int>    <dbl> <dbl>   <dbl>   <dbl>
#> 1  2000 -2.05       NA  0.607   1.20  
#> 2  2001 -0.676      NA  1.65   -0.114 
#> 3  2002  1.60       NA -0.0245  0.501 
#> 4  2003 -0.353      NA  1.30   -0.459 
#> 5  2004 -0.00530    NA  0.921  -0.0589
#> 6  2005  0.442      NA -1.55    0.594

Varios ejemplos avanzados de cómo trabajar con el nuevo concepto tidyr.

Limpieza de datos utilizando como ejemplo el conjunto de datos de ingresos y alquileres del censo de EE. UU.

Conjunto de datos nosotros_alquiler_ingresos contiene información sobre ingresos medios y alquileres para cada estado de EE. UU. para 2017 (conjunto de datos disponible en el paquete tidycenso).

us_rent_income
#> # A tibble: 104 x 5
#>    GEOID NAME       variable estimate   moe
#>    <chr> <chr>      <chr>       <dbl> <dbl>
#>  1 01    Alabama    income      24476   136
#>  2 01    Alabama    rent          747     3
#>  3 02    Alaska     income      32940   508
#>  4 02    Alaska     rent         1200    13
#>  5 04    Arizona    income      27517   148
#>  6 04    Arizona    rent          972     4
#>  7 05    Arkansas   income      23789   165
#>  8 05    Arkansas   rent          709     5
#>  9 06    California income      29454   109
#> 10 06    California rent         1358     3
#> # … with 94 more rows

En la forma en que se almacenan los datos en el conjunto de datos. nosotros_alquiler_ingresos trabajar con ellos es extremadamente inconveniente, por lo que nos gustaría crear un conjunto de datos con columnas: alquiler, alquiler_moe, cómo, ingresos_moe. Hay muchas formas de crear esta especificación, pero el punto principal es que necesitamos generar cada combinación de valores variables y estimación/mesy luego generar el nombre de la columna.

  spec <- us_rent_income %>% 
    expand(variable, .value = c("estimate", "moe")) %>% 
    mutate(
      .name = paste0(variable, ifelse(.value == "moe", "_moe", ""))
    )

#> # A tibble: 4 x 3
#>   variable .value   .name     
#>   <chr>    <chr>    <chr>     
#> 1 income   estimate income    
#> 2 income   moe      income_moe
#> 3 rent     estimate rent      
#> 4 rent     moe      rent_moe

Proporcionar esta especificación pivot_wider() nos da el resultado que buscamos:

us_rent_income %>% pivot_wider(spec = spec)

#> # A tibble: 52 x 6
#>    GEOID NAME                 income income_moe  rent rent_moe
#>    <chr> <chr>                 <dbl>      <dbl> <dbl>    <dbl>
#>  1 01    Alabama               24476        136   747        3
#>  2 02    Alaska                32940        508  1200       13
#>  3 04    Arizona               27517        148   972        4
#>  4 05    Arkansas              23789        165   709        5
#>  5 06    California            29454        109  1358        3
#>  6 08    Colorado              32401        109  1125        5
#>  7 09    Connecticut           35326        195  1123        5
#>  8 10    Delaware              31560        247  1076       10
#>  9 11    District of Columbia  43198        681  1424       17
#> 10 12    Florida               25952         70  1077        3
#> # … with 42 more rows

El Banco Mundial

A veces, llevar un conjunto de datos al formato deseado requiere varios pasos.
Conjunto de datos banco_mundial_pop Contiene datos del Banco Mundial sobre la población de cada país entre 2000 y 2018.

#> # A tibble: 1,056 x 20
#>    country indicator `2000` `2001` `2002` `2003`  `2004`  `2005`   `2006`
#>    <chr>   <chr>      <dbl>  <dbl>  <dbl>  <dbl>   <dbl>   <dbl>    <dbl>
#>  1 ABW     SP.URB.T… 4.24e4 4.30e4 4.37e4 4.42e4 4.47e+4 4.49e+4  4.49e+4
#>  2 ABW     SP.URB.G… 1.18e0 1.41e0 1.43e0 1.31e0 9.51e-1 4.91e-1 -1.78e-2
#>  3 ABW     SP.POP.T… 9.09e4 9.29e4 9.50e4 9.70e4 9.87e+4 1.00e+5  1.01e+5
#>  4 ABW     SP.POP.G… 2.06e0 2.23e0 2.23e0 2.11e0 1.76e+0 1.30e+0  7.98e-1
#>  5 AFG     SP.URB.T… 4.44e6 4.65e6 4.89e6 5.16e6 5.43e+6 5.69e+6  5.93e+6
#>  6 AFG     SP.URB.G… 3.91e0 4.66e0 5.13e0 5.23e0 5.12e+0 4.77e+0  4.12e+0
#>  7 AFG     SP.POP.T… 2.01e7 2.10e7 2.20e7 2.31e7 2.41e+7 2.51e+7  2.59e+7
#>  8 AFG     SP.POP.G… 3.49e0 4.25e0 4.72e0 4.82e0 4.47e+0 3.87e+0  3.23e+0
#>  9 AGO     SP.URB.T… 8.23e6 8.71e6 9.22e6 9.77e6 1.03e+7 1.09e+7  1.15e+7
#> 10 AGO     SP.URB.G… 5.44e0 5.59e0 5.70e0 5.76e0 5.75e+0 5.69e+0  4.92e+0
#> # … with 1,046 more rows, and 11 more variables: `2007` <dbl>,
#> #   `2008` <dbl>, `2009` <dbl>, `2010` <dbl>, `2011` <dbl>, `2012` <dbl>,
#> #   `2013` <dbl>, `2014` <dbl>, `2015` <dbl>, `2016` <dbl>, `2017` <dbl>

Nuestro objetivo es crear un conjunto de datos ordenado con cada variable en su propia columna. No está claro exactamente qué pasos se necesitan, pero comenzaremos con el problema más obvio: el año se distribuye en varias columnas.

Para solucionar este problema es necesario utilizar la función pivot_longer().

pop2 <- world_bank_pop %>% 
  pivot_longer(`2000`:`2017`, names_to = "year")

#> # A tibble: 19,008 x 4
#>    country indicator   year  value
#>    <chr>   <chr>       <chr> <dbl>
#>  1 ABW     SP.URB.TOTL 2000  42444
#>  2 ABW     SP.URB.TOTL 2001  43048
#>  3 ABW     SP.URB.TOTL 2002  43670
#>  4 ABW     SP.URB.TOTL 2003  44246
#>  5 ABW     SP.URB.TOTL 2004  44669
#>  6 ABW     SP.URB.TOTL 2005  44889
#>  7 ABW     SP.URB.TOTL 2006  44881
#>  8 ABW     SP.URB.TOTL 2007  44686
#>  9 ABW     SP.URB.TOTL 2008  44375
#> 10 ABW     SP.URB.TOTL 2009  44052
#> # … with 18,998 more rows

El siguiente paso es observar la variable indicadora.
pop2 %>% count(indicator)

#> # A tibble: 4 x 2
#>   indicator       n
#>   <chr>       <int>
#> 1 SP.POP.GROW  4752
#> 2 SP.POP.TOTL  4752
#> 3 SP.URB.GROW  4752
#> 4 SP.URB.TOTL  4752

Donde SP.POP.GROW es el crecimiento de la población, SP.POP.TOTL es la población total y SP.URB. *lo mismo, pero sólo para zonas urbanas. Dividamos estos valores en dos variables: área - área (total o urbana) y una variable que contiene datos reales (población o crecimiento):

pop3 <- pop2 %>% 
  separate(indicator, c(NA, "area", "variable"))

#> # A tibble: 19,008 x 5
#>    country area  variable year  value
#>    <chr>   <chr> <chr>    <chr> <dbl>
#>  1 ABW     URB   TOTL     2000  42444
#>  2 ABW     URB   TOTL     2001  43048
#>  3 ABW     URB   TOTL     2002  43670
#>  4 ABW     URB   TOTL     2003  44246
#>  5 ABW     URB   TOTL     2004  44669
#>  6 ABW     URB   TOTL     2005  44889
#>  7 ABW     URB   TOTL     2006  44881
#>  8 ABW     URB   TOTL     2007  44686
#>  9 ABW     URB   TOTL     2008  44375
#> 10 ABW     URB   TOTL     2009  44052
#> # … with 18,998 more rows

Ahora todo lo que tenemos que hacer es dividir la variable en dos columnas:

pop3 %>% 
  pivot_wider(names_from = variable, values_from = value)

#> # A tibble: 9,504 x 5
#>    country area  year   TOTL    GROW
#>    <chr>   <chr> <chr> <dbl>   <dbl>
#>  1 ABW     URB   2000  42444  1.18  
#>  2 ABW     URB   2001  43048  1.41  
#>  3 ABW     URB   2002  43670  1.43  
#>  4 ABW     URB   2003  44246  1.31  
#>  5 ABW     URB   2004  44669  0.951 
#>  6 ABW     URB   2005  44889  0.491 
#>  7 ABW     URB   2006  44881 -0.0178
#>  8 ABW     URB   2007  44686 -0.435 
#>  9 ABW     URB   2008  44375 -0.698 
#> 10 ABW     URB   2009  44052 -0.731 
#> # … with 9,494 more rows

Lista de contactos

Un último ejemplo, imagina que tienes una lista de contactos que copiaste y pegaste de un sitio web:

contacts <- tribble(
  ~field, ~value,
  "name", "Jiena McLellan",
  "company", "Toyota", 
  "name", "John Smith", 
  "company", "google", 
  "email", "[email protected]",
  "name", "Huxley Ratcliffe"
)

Tabular esta lista es bastante difícil porque no existe una variable que identifique qué datos pertenecen a qué contacto. Podemos solucionar este problema observando que los datos de cada nuevo contacto comienzan con "nombre", por lo que podemos crear un identificador único e incrementarlo en uno cada vez que la columna del campo contenga el valor "nombre":

contacts <- contacts %>% 
  mutate(
    person_id = cumsum(field == "name")
  )
contacts

#> # A tibble: 6 x 3
#>   field   value            person_id
#>   <chr>   <chr>                <int>
#> 1 name    Jiena McLellan           1
#> 2 company Toyota                   1
#> 3 name    John Smith               2
#> 4 company google                   2
#> 5 email   [email protected]          2
#> 6 name    Huxley Ratcliffe         3

Ahora que tenemos una identificación única para cada contacto, podemos convertir el campo y el valor en columnas:

contacts %>% 
  pivot_wider(names_from = field, values_from = value)

#> # A tibble: 3 x 4
#>   person_id name             company email          
#>       <int> <chr>            <chr>   <chr>          
#> 1         1 Jiena McLellan   Toyota  <NA>           
#> 2         2 John Smith       google  [email protected]
#> 3         3 Huxley Ratcliffe <NA>    <NA>

Conclusión

Mi opinión personal es que el nuevo concepto ordenar verdaderamente más intuitivo y significativamente superior en funcionalidad a las funciones heredadas spread() и gather(). Espero que este artículo te haya ayudado a lidiar con pivot_longer() и pivot_wider().

Fuente: habr.com

Añadir un comentario