El paquet R tidyr i les seves noves funcions pivot_longer i pivot_wider

Paquet ordenar inclòs al nucli d'una de les biblioteques més populars en llenguatge R: endreçat.
L'objectiu principal del paquet és portar les dades en una forma precisa.

Ja disponible a Habré publicació dedicat a aquest paquet, però es remunta al 2015. I us vull parlar dels canvis més actuals, que va anunciar fa uns dies la seva autora, Hedley Wickham.

El paquet R tidyr i les seves noves funcions pivot_longer i pivot_wider

SJK: Gather() i spread() quedaran obsolets?

Hadley Wickham: Fins a cert punt. Ja no recomanarem l'ús d'aquestes funcions i solucionarem errors, però continuaran presents al paquet en el seu estat actual.

Contingut

Si esteu interessats en l'anàlisi de dades, potser us interessa el meu telegram и youtube canals. La major part del contingut està dedicat al llenguatge R.

Concepte TidyData

Objectiu ordenar — ajudar-vos a portar les dades a una forma anomenada ordenada. Les dades netes són dades on:

  • Cada variable es troba en una columna.
  • Cada observació és una cadena.
  • Cada valor és una cel·la.

És molt més fàcil i còmode treballar amb dades que es presenten en dades ordenades quan es realitza l'anàlisi.

Funcions principals incloses al paquet tidyr

tidyr conté un conjunt de funcions dissenyades per transformar taules:

  • fill() — omplir els valors que falten en una columna amb valors anteriors;
  • separate() — divideix un camp en diversos mitjançant un separador;
  • unite() — realitza l'operació de combinar diversos camps en un, l'acció inversa de la funció separate();
  • pivot_longer() — una funció que converteix dades de format ample a format llarg;
  • pivot_wider() - una funció que converteix dades de format llarg a format ample. Operació inversa a la realitzada per la funció pivot_longer().
  • gather()obsolet — una funció que converteix dades de format ample a format llarg;
  • spread()obsolet - una funció que converteix dades de format llarg a format ample. Operació inversa a la realitzada per la funció gather().

Nou concepte per convertir dades de format ample a format llarg i viceversa

Anteriorment, s'utilitzaven funcions per a aquest tipus de transformació gather() и spread(). Al llarg dels anys d'existència d'aquestes funcions, es va fer evident que per a la majoria d'usuaris, inclòs l'autor del paquet, els noms d'aquestes funcions i els seus arguments no eren del tot evidents, i van causar dificultats per trobar-les i entendre quina d'aquestes funcions convertia. un marc de data de format ample a format llarg, i viceversa.

En aquest sentit, en ordenar S'han afegit dues funcions noves i importants que estan dissenyades per transformar els marcs de data.

Noves característiques pivot_longer() и pivot_wider() es van inspirar en algunes de les característiques del paquet cdata, creat per John Mount i Nina Zumel.

Instal·lant la versió més actual de tidyr 0.8.3.9000

Per instal·lar la versió nova i més actual del paquet ordenar 0.8.3.9000, quan hi hagi noves funcions disponibles, utilitzeu el codi següent.

devtools::install_github("tidyverse/tidyr")

En el moment d'escriure, aquestes funcions només estan disponibles a la versió de desenvolupament del paquet a GitHub.

Transició a noves funcions

De fet, convertir scripts antics per treballar amb noves funcions no és difícil, per a una major comprensió, agafaré un exemple de la documentació de les funcions antigues i mostraré com es realitzen les mateixes operacions utilitzant les noves. pivot_*() funcions.

Converteix format ample a format llarg.

Exemple de codi de la documentació de la funció de recopilació

# 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ó de format llarg a format ample.

Exemple de codi de la documentació de la funció de difusió

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

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

Perquè en els exemples anteriors de treball amb pivot_longer() и pivot_wider(), a la taula original estocs no hi ha columnes enumerades als arguments noms_a и valors_a els seus noms han d'anar entre cometes.

Una taula que us ajudarà a esbrinar més fàcilment com canviar a treballar amb un concepte nou ordenar.

El paquet R tidyr i les seves noves funcions pivot_longer i pivot_wider

Nota de l'autor

Tot el text següent és adaptatiu, fins i tot diria traducció gratuïta vinyetes del lloc web oficial de la biblioteca tidyverse.

Un exemple senzill de conversió de dades de format ample a format llarg

pivot_longer () — fa que els conjunts de dades siguin més llargs reduint el nombre de columnes i augmentant el nombre de files.

El paquet R tidyr i les seves noves funcions pivot_longer i pivot_wider

Per executar els exemples presentats a l'article, primer heu de connectar els paquets necessaris:

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

Suposem que tenim una taula amb els resultats d'una enquesta que (entre altres coses) preguntava a la gent sobre la seva religió i els ingressos anuals:

#> # 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>

Aquesta taula conté les dades religioses dels enquestats en files i els nivells d'ingressos es distribueixen entre els noms de les columnes. El nombre d'enquestats de cada categoria s'emmagatzema als valors de la cel·la a la intersecció de la religió i el nivell d'ingressos. Per portar la taula en un format net i correcte, n'hi ha prou amb utilitzar 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

Arguments de funció pivot_longer()

  • Primer argument collars, descriu quines columnes s'han de combinar. En aquest cas, totes les columnes excepte temps.
  • Argument noms_a dóna el nom de la variable que es crearà a partir dels noms de les columnes que hem concatenat.
  • valors_a dóna el nom d'una variable que es crearà a partir de les dades emmagatzemades en els valors de les cel·les de les columnes combinades.

Спецификации

Aquesta és una nova funcionalitat del paquet ordenar, que abans no estava disponible quan es treballava amb funcions heretades.

Una especificació és un marc de dades, cada fila del qual correspon a una columna del nou marc de data de sortida i dues columnes especials que comencen per:

  • . Nom conté el nom de la columna original.
  • .valor conté el nom de la columna que contindrà els valors de la cel·la.

Les columnes restants de l'especificació reflecteixen com la nova columna mostrarà el nom de les columnes comprimides . Nom.

L'especificació descriu les metadades emmagatzemades en un nom de columna, amb una fila per a cada columna i una columna per a cada variable, combinades amb el nom de la columna, aquesta definició pot semblar confusa de moment, però després de mirar alguns exemples es convertirà en molt. més clar.

El punt de l'especificació és que podeu recuperar, modificar i definir metadades noves per al marc de dades que s'està convertint.

Per treballar amb especificacions en convertir una taula d'un format ampli a un format llarg, utilitzeu la funció pivot_longer_spec().

El funcionament d'aquesta funció és que pren qualsevol marc de data i genera les seves metadades de la manera descrita anteriorment.

Com a exemple, prenem el conjunt de dades who que es proporciona amb el paquet ordenar. Aquest conjunt de dades conté informació proporcionada per l'organització internacional de la salut sobre la incidència de la tuberculosi.

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

Construïm la seva especificació.

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

camps país, isoxnumx, isoxnumx ja són variables. La nostra tasca és capgirar les columnes amb nou_sp_m014 en newrel_f65.

Els noms d'aquestes columnes emmagatzemen la informació següent:

  • Prefix new_ indica que la columna conté dades sobre nous casos de tuberculosi, el marc de data actual només conté informació sobre noves malalties, de manera que aquest prefix en el context actual no té cap significat.
  • sp/rel/sp/ep descriu un mètode per diagnosticar una malaltia.
  • m/f gènere del pacient.
  • 014/1524/2535/3544/4554/65 interval d'edat del pacient.

Podem dividir aquestes columnes mitjançant la funció extract()utilitzant expressió regular.

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

Si us plau, tingueu en compte la columna . Nom hauria de romandre sense canvis, ja que aquest és el nostre índex als noms de columnes del conjunt de dades original.

Gènere i edat (columnes gènere и edat) tenen valors fixos i coneguts, per la qual cosa es recomana convertir aquestes columnes en factors:

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

Finalment, per aplicar l'especificació que hem creat al marc de data original que hem de fer servir un argument spec en funció 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

Tot el que acabem de fer es pot representar esquemàticament de la següent manera:

El paquet R tidyr i les seves noves funcions pivot_longer i pivot_wider

Especificació utilitzant diversos valors (.value)

A l'exemple anterior, la columna d'especificacions .valor contenia només un valor, en la majoria dels casos aquest és el cas.

Però de vegades pot sorgir una situació quan necessiteu recopilar dades de columnes amb diferents tipus de dades en valors. Ús d'una funció heretada spread() això seria bastant difícil de fer.

L'exemple següent està extret de vinyetes al paquet dades.taula.

Creem un marc de dades d'entrenament.

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 marc de data creat conté dades sobre els fills d'una família a cada fila. Les famílies poden tenir un o dos fills. Per a cada nen, es proporcionen dades sobre la data de naixement i el gènere, i les dades de cada nen es troben en columnes separades; la nostra tasca és portar aquestes dades al format correcte per analitzar-les.

Tingueu en compte que tenim dues variables amb informació sobre cada nen: el seu gènere i la data de naixement (columnes amb el prefix DOP conté data de naixement, columnes amb prefix gènere continguin el sexe del nen). El resultat esperat és que apareguin en columnes separades. Podem fer-ho generant una especificació en què la columna .value tindrà dos significats diferents.

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

Per tant, fem una ullada pas a pas a les accions realitzades pel codi anterior.

  • pivot_longer_spec(-family) — Creeu una especificació que comprimiu totes les columnes existents excepte la columna de la família.
  • separate(col = name, into = c(".value", "child")) - dividir la columna . Nom, que conté els noms dels camps d'origen, utilitzant el guió baix i introduint els valors resultants a les columnes .valor и nen.
  • mutate(child = parse_number(child)) — transformar els valors del camp nen del text al tipus de dades numèriques.

Ara podem aplicar l'especificació resultant al marc de dades original i portar la taula a la forma desitjada.

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

Utilitzem arguments na.rm = TRUE, perquè la forma actual de les dades obliga a crear files addicionals per a observacions inexistents. Perquè la família 2 només té un fill, na.rm = TRUE garanteix que la família 2 tindrà una fila a la sortida.

Conversió de marcs de data de format llarg a format ample

pivot_wider() - és la transformació inversa, i viceversa augmenta el nombre de columnes del marc de data reduint el nombre de files.

El paquet R tidyr i les seves noves funcions pivot_longer i pivot_wider

Aquest tipus de transformació s'utilitza molt poques vegades per portar les dades en una forma precisa, però aquesta tècnica pot ser útil per crear taules dinàmiques utilitzades en presentacions o per a la integració amb altres eines.

De fet, les funcions pivot_longer() и pivot_wider() són simètrics i produeixen accions inverses entre si, és a dir: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) retornarà el df original.

L'exemple més senzill de convertir una taula a un format ampli

Per demostrar com funciona la funció pivot_wider() utilitzarem el conjunt de dades trobades_peixos, que emmagatzema informació sobre com les diferents estacions registren el moviment dels peixos al llarg del riu.

#> # 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 majoria dels casos, aquesta taula serà més informativa i més fàcil d'utilitzar si presenteu la informació de cada estació en una columna independent.

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>

Aquest conjunt de dades només registra informació quan l'estació ha detectat peixos, és a dir. si alguna estació no va registrar cap peix, aquestes dades no apareixeran a la taula. Això significa que la sortida s'omplirà amb NA.

No obstant això, en aquest cas sabem que l'absència d'un registre significa que el peix no es va veure, per tant podem utilitzar l'argument omplir_valors en funció pivot_wider() i ompliu aquests valors que falten amb zeros:

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>

Generació d'un nom de columna a partir de diverses variables d'origen

Imagineu que tenim una taula que conté una combinació de producte, país i any. Per generar un marc de data de prova, podeu executar el codi següent:

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

La nostra tasca és ampliar el marc de dades de manera que una columna contingui dades per a cada combinació de producte i país. Per fer-ho, només cal passar l'argument noms_de un vector que conté els noms dels camps a combinar.

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

També podeu aplicar especificacions a una funció pivot_wider(). Però quan es sotmet a pivot_wider() l'especificació fa la conversió oposada pivot_longer(): les columnes especificades a . Nom, utilitzant valors de .valor i altres columnes.

Per a aquest conjunt de dades, podeu generar una especificació personalitzada si voleu que cada possible combinació de país i producte tingui la seva pròpia columna, no només les que hi ha a les dades:

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

Diversos exemples avançats de treball amb el nou concepte tidyr

Neteja de dades utilitzant el conjunt de dades d'ingressos i lloguers del cens dels EUA com a exemple.

Conjunt de dades us_rent_ingressos conté informació sobre els ingressos i lloguers mitjans per a cada estat dels EUA per al 2017 (conjunt de dades disponible al paquet cens ordenat).

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 què s'emmagatzemen les dades al conjunt de dades us_rent_ingressos treballar amb ells és extremadament inconvenient, així que ens agradaria crear un conjunt de dades amb columnes: llogar, rent_moe, Venir, income_moe. Hi ha moltes maneres de crear aquesta especificació, però el punt principal és que hem de generar totes les combinacions de valors variables i estimació/moei després genereu el nom 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

Proporcionant aquesta especificació pivot_wider() ens dóna el resultat que busquem:

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 Banc Mundial

De vegades, portar un conjunt de dades a la forma desitjada requereix diversos passos.
Conjunt de dades world_bank_pop conté dades del Banc Mundial sobre la població de cada país entre el 2000 i el 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>

El nostre objectiu és crear un conjunt de dades net amb cada variable a la seva pròpia columna. No està clar quins passos calen exactament, però començarem pel problema més evident: l'any es distribueix en diverses columnes.

Per solucionar-ho, heu d'utilitzar la funció 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 següent pas és mirar 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

On SP.POP.GROW és el creixement de la població, SP.POP.TOTL és la població total i SP.URB. * el mateix, però només per a zones urbanes. Dividim aquests valors en dues variables: àrea - àrea (total o urbana) i una variable que conté dades reals (població o creixement):

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

Ara tot el que hem de fer és dividir la variable en dues columnes:

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

Llista de contactes

Un últim exemple, imagineu que teniu una llista de contactes que heu copiat i enganxat d'un lloc web:

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

La tabulació d'aquesta llista és bastant difícil perquè no hi ha cap variable que identifiqui quines dades pertanyen a quin contacte. Podem solucionar-ho observant que les dades de cada contacte nou comencen amb "nom", de manera que podem crear un identificador únic i augmentar-lo en un cada vegada que la columna del camp contingui el valor "nom":

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

Ara que tenim un identificador únic per a cada contacte, podem convertir el camp i el valor en columnes:

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ó

La meva opinió personal és que el nou concepte ordenar realment més intuïtiu i significativament superior en funcionalitat a les funcions heretades spread() и gather(). Espero que aquest article t'hagi ajudat a tractar-ho pivot_longer() и pivot_wider().

Font: www.habr.com

Afegeix comentari