R-pakaĵo tidyr kaj ĝiaj novaj funkcioj pivot_longer kaj pivot_wider

Pako tidyr inkluzivita en la kerno de unu el la plej popularaj bibliotekoj en la R-lingvo - tidyverse.
La ĉefa celo de la pakaĵo estas alporti la datumojn en precizan formon.

Jam disponebla ĉe Habré publikigo dediĉita al ĉi tiu pako, sed ĝi devenas de 2015. Kaj mi volas rakonti al vi pri la plej aktualaj ŝanĝoj, kiuj estis anoncitaj antaŭ kelkaj tagoj de ĝia aŭtoro, Hedley Wickham.

R-pakaĵo tidyr kaj ĝiaj novaj funkcioj pivot_longer kaj pivot_wider

SJK: Ĉu gather() kaj spread() estos malrekomenditaj?

Hadley Wickham: Iagrade. Ni ne plu rekomendos la uzon de ĉi tiuj funkcioj kaj riparos cimojn en ili, sed ili daŭre ĉeestos en la pakaĵo en sia nuna stato.

Enhavo

Se vi interesiĝas pri datuma analizo, vi eble interesiĝos pri mia telegramo и youtube kanaloj. Plejparto de la enhavo estas dediĉita al la R-lingvo.

TidyData koncepto

Golo tidyr — helpi vin alporti la datumojn al tiel nomata neta formo. Neta datumo estas datumoj kie:

  • Ĉiu variablo estas en kolumno.
  • Ĉiu observo estas ŝnuro.
  • Ĉiu valoro estas ĉelo.

Estas multe pli facile kaj pli oportune labori kun datumoj, kiuj estas prezentitaj en ordaj datumoj, kiam oni faras analizon.

Ĉefaj funkcioj inkluzivitaj en la tidyr-pakaĵo

tidyr enhavas aron de funkcioj dizajnitaj por transformi tabelojn:

  • fill() — plenigi mankantajn valorojn en kolumno kun antaŭaj valoroj;
  • separate() — dividas unu kampon en plurajn uzante apartigilon;
  • unite() — faras la operacion kunigi plurajn kampojn en unu, la inversan agon de la funkcio separate();
  • pivot_longer() — funkcio, kiu konvertas datumojn de larĝa formato al longa formato;
  • pivot_wider() - funkcio, kiu konvertas datumojn de longa formato al larĝa formato. La inversa operacio de tiu farita de la funkcio pivot_longer().
  • gather()malmoderna — funkcio, kiu konvertas datumojn de larĝa formato al longa formato;
  • spread()malmoderna - funkcio, kiu konvertas datumojn de longa formato al larĝa formato. La inversa operacio de tiu farita de la funkcio gather().

Nova koncepto por konverti datumojn de larĝa al longa formato kaj inverse

Antaŭe, funkcioj estis uzitaj por ĉi tiu speco de transformo gather() и spread(). Dum la jaroj de ekzisto de ĉi tiuj funkcioj, iĝis evidente, ke por la plej multaj uzantoj, inkluzive de la aŭtoro de la pakaĵo, la nomoj de ĉi tiuj funkcioj kaj iliaj argumentoj ne estis tute evidentaj, kaj kaŭzis malfacilaĵojn trovi ilin kaj kompreni, kiun el tiuj funkcioj konvertas. datkadro de larĝa ĝis longa formato, kaj inverse.

Ĉi-rilate, en tidyr Du novaj, gravaj funkcioj estis aldonitaj, kiuj estas dezajnitaj por transformi datkadrojn.

Novaj funkcioj pivot_longer() и pivot_wider() estis inspiritaj de kelkaj el la funkcioj en la pakaĵo cdata, kreita fare de John Mount kaj Nina Zumel.

Instalante la plej aktualan version de tidyr 0.8.3.9000

Por instali la novan, plej aktualan version de la pakaĵo tidyr 0.8.3.9000, kie novaj funkcioj estas disponeblaj, uzu la sekvan kodon.

devtools::install_github("tidyverse/tidyr")

Dum la skribado, ĉi tiuj funkcioj estas disponeblaj nur en la dev-versio de la pakaĵo en GitHub.

Transiro al novaj funkcioj

Fakte, ne estas malfacile translokigi malnovajn skriptojn por labori kun novaj funkcioj; por pli bona kompreno, mi prenos ekzemplon el la dokumentado de malnovaj funkcioj kaj montros kiel la samaj operacioj estas faritaj uzante novajn. pivot_*() funkcioj.

Konverti larĝan formaton al longa formato.

Ekzempla kodo el la kolekta funkciodokumentado

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

Konverti longan formaton al larĝa formato.

Ekzempla kodo de disvastiga funkciodokumentado

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

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

Ĉar en la supraj ekzemploj pri laboro kun pivot_longer() и pivot_wider(), en la origina tabelo akcioj neniuj kolumnoj listigitaj en argumentoj nomoj_al и valoroj_al iliaj nomoj devas esti inter citiloj.

Tabelo, kiu helpos vin plej facile eltrovi kiel ŝanĝi al laboro kun nova koncepto tidyr.

R-pakaĵo tidyr kaj ĝiaj novaj funkcioj pivot_longer kaj pivot_wider

Noto de la aŭtoro

La tuta suba teksto estas adapta, mi eĉ dirus liberan tradukon vinjetoj de la oficiala retejo de la biblioteko de tidyverse.

Simpla ekzemplo de konvertado de datumoj de larĝa al longa formato

pivot_longer () — plilongigas datumojn reduktante la nombron da kolumnoj kaj pliigante la nombron da vicoj.

R-pakaĵo tidyr kaj ĝiaj novaj funkcioj pivot_longer kaj pivot_wider

Por ruli la ekzemplojn prezentitajn en la artikolo, vi unue devas konekti la necesajn pakaĵojn:

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

Ni diru, ke ni havas tabelon kun la rezultoj de enketo, kiu (interalie) demandis homojn pri ilia religio kaj jara enspezo:

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

Ĉi tiu tabelo enhavas religiajn datumojn de respondantoj en vicoj, kaj enspezniveloj estas disigitaj tra kolonnomoj. La nombro da respondantoj de ĉiu kategorio estas konservita en la ĉelvaloroj ĉe la intersekco de religio kaj enspeznivelo. Por alporti la tablon en bonordan, ĝustan formaton, sufiĉas uzi 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

Funkciaj Argumentoj pivot_longer()

  • Unua argumento kolumoj, priskribas kiuj kolumnoj devas esti kunfanditaj. En ĉi tiu kazo, ĉiuj kolumnoj krom tempo.
  • argumenton nomoj_al donas la nomon de la variablo kiu estos kreita el la nomoj de la kolumnoj, kiujn ni kunligis.
  • valoroj_al donas la nomon de variablo, kiu estos kreita el la datumoj stokitaj en la valoroj de la ĉeloj de la kunfanditaj kolumnoj.

Specifoj (redakti)

Ĉi tio estas nova funkcio de la pako tidyr, kiu antaŭe estis neatingebla kiam oni laboris kun heredaj funkcioj.

Specifo estas datumkadro, ĉiu vico de kiu egalrilatas al unu kolumno en la nova produktaĵdatkadro, kaj du specialaj kolumnoj kiuj komenciĝas per:

  • .nomo enhavas la originan kolonnomon.
  • .valoro enhavas la nomon de la kolumno, kiu enhavos la ĉelvalorojn.

La ceteraj kolumnoj de la specifo reflektas kiel la nova kolumno montros la nomon de la kunpremitaj kolumnoj de .nomo.

La specifo priskribas la metadatenojn konservitajn en kolumnonomo, kun unu vico por ĉiu kolumno kaj unu kolumno por ĉiu variablo, kombinita kun la kolumnonomo, ĉi tiu difino povas ŝajni konfuza nuntempe, sed post rigardado de kelkaj ekzemploj ĝi fariĝos multe. pli klara.

La punkto de la specifo estas, ke vi povas preni, modifi kaj difini novajn metadatenojn por la konverta datuma kadro.

Por labori kun specifoj konvertante tabelon de larĝa formato al longa formato, uzu la funkcion pivot_longer_spec().

Kiel ĉi tiu funkcio funkcias estas ke ĝi prenas ajnan datkadron kaj generas siajn metadatenojn en la maniero priskribita supre.

Kiel ekzemplo, ni prenu la who-datumaron, kiu estas provizita kun la pakaĵo tidyr. Ĉi tiu datumaro enhavas informojn provizitajn de la internacia sanorganizo pri la incidenco de tuberkulozo.

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

Ni konstruu ĝian specifon.

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

kampoj lando, isoxnumx, isoxnumx estas jam variabloj. Nia tasko estas renversi la kolumnojn kun nova_sp_m014 sur newrel_f65.

La nomoj de ĉi tiuj kolumnoj konservas la sekvajn informojn:

  • Prefikso new_ indikas, ke la kolumno enhavas datumojn pri novaj kazoj de tuberkulozo, la nuna datkadro enhavas informojn nur pri novaj malsanoj, do ĉi tiu prefikso en la nuna kunteksto ne havas ajnan signifon.
  • sp/rel/sp/ep priskribas metodon por diagnozi malsanon.
  • m/f sekso de paciento.
  • 014/1524/2535/3544/4554/65 pacienca aĝo gamo.

Ni povas dividi ĉi tiujn kolumnojn uzante la funkcion extract()uzante regulan esprimon.

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

Bonvolu noti la kolumnon .nomo devus resti senŝanĝa ĉar ĉi tiu estas nia indekso en la kolonnomoj de la origina datumaro.

Sekso kaj aĝo (kolumnoj sekso и aĝo) havas fiksajn kaj konatajn valorojn, do rekomendas konverti ĉi tiujn kolumnojn al faktoroj:

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

Fine, por apliki la specifon, kiun ni kreis al la origina datkadro kiu ni bezonas uzi argumenton spec en funkcio 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

Ĉio, kion ni ĵus faris, povas esti skeme prezentita jene:

R-pakaĵo tidyr kaj ĝiaj novaj funkcioj pivot_longer kaj pivot_wider

Specifo uzante multoblajn valorojn (.value)

En la supra ekzemplo, la specifkolumno .valoro enhavis nur unu valoron, en la plej multaj kazoj tio estas la kazo.

Sed foje povas aperi situacio kiam vi bezonas kolekti datumojn de kolumnoj kun malsamaj datumtipoj en valoroj. Uzante heredan funkcion spread() ĉi tio estus sufiĉe malfacile fari.

La malsupra ekzemplo estas prenita de vinjetoj al la pako datumo.tablo.

Ni kreu trejnan datumkadron.

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

La kreita datkadro enhavas datumojn pri infanoj de unu familio en ĉiu vico. Familioj povas havi unu aŭ du infanojn. Por ĉiu infano, datumoj estas provizitaj pri la dato de naskiĝo kaj sekso, kaj la datumoj por ĉiu infano estas en apartaj kolumnoj; nia tasko estas alporti ĉi tiujn datumojn al la ĝusta formato por analizo.

Bonvolu noti, ke ni havas du variablojn kun informoj pri ĉiu infano: ilia sekso kaj naskiĝdato (kolumnoj kun la prefikso dop enhavas naskiĝdaton, kolumnojn kun prefikso sekso enhavas la sekson de la infano). La atendata rezulto estas, ke ili aperu en apartaj kolumnoj. Ni povas fari ĉi tion generante specifon en kiu la kolumno .value havos du malsamajn signifojn.

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

Do, ni rigardu paŝon post paŝo al la agoj faritaj de la supra kodo.

  • pivot_longer_spec(-family) — kreu specifon, kiu kunpremas ĉiujn ekzistantajn kolumnojn krom la familia kolumno.
  • separate(col = name, into = c(".value", "child")) - dividi la kolonon .nomo, kiu enhavas la nomojn de la fontaj kampoj, uzante la substrekon kaj enigante la rezultajn valorojn en la kolumnojn .valoro и infano.
  • mutate(child = parse_number(child)) — transformi la kampajn valorojn infano de teksto ĝis nombra datumtipo.

Nun ni povas apliki la rezultan specifon al la origina datuma kadro kaj alporti la tabelon al la dezirata formo.

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

Ni uzas argumenton na.rm = TRUE, ĉar la nuna formo de la datenoj devigas la kreadon de ekstraj vicoj por neekzistantaj observaĵoj. Ĉar familio 2 havas nur unu infanon, na.rm = TRUE garantias ke familio 2 havos unu vicon en la eligo.

Konvertado de dataj kadroj de longa al larĝa formato

pivot_wider() - estas la inversa transformo, kaj inverse pliigas la nombron da kolumnoj de la datokadro reduktante la nombron da vicoj.

R-pakaĵo tidyr kaj ĝiaj novaj funkcioj pivot_longer kaj pivot_wider

Ĉi tiu speco de transformo estas ekstreme malofte uzata por alporti datumojn en precizan formon, tamen ĉi tiu tekniko povas esti utila por krei pivotajn tabelojn uzatajn en prezentoj aŭ por integriĝo kun iuj aliaj iloj.

Fakte la funkcioj pivot_longer() и pivot_wider() estas simetriaj, kaj produktas agojn inversajn unu al la alia, t.e.: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) redonos la originalan df.

La plej simpla ekzemplo de konverti tabelo al larĝa formato

Por montri kiel funkcias la funkcio pivot_wider() ni uzos la datumaron fish_encounters, kiu stokas informojn pri kiel malsamaj stacioj registras la movadon de fiŝoj laŭ la rivero.

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

Plejofte, ĉi tiu tabelo estos pli informa kaj pli facile uzebla se vi prezentas informojn por ĉiu stacio en aparta kolumno.

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>

Ĉi tiu datumaro nur registras informojn kiam fiŝoj estis detektitaj de la stacio, t.e. se iu fiŝo ne estis registrita de iu stacio, tiam ĉi tiuj datumoj ne estos en la tabelo. Ĉi tio signifas, ke la eligo estos plenigita per NA.

Tamen, en ĉi tiu kazo ni scias ke la foresto de registro signifas ke la fiŝo ne estis vidita, do ni povas uzi la argumenton valoroj_plenigi en funkcio pivot_wider() kaj plenigu ĉi tiujn mankantajn valorojn per nuloj:

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>

Generante kolumnan nomon de multoblaj fontvariabloj

Imagu, ke ni havas tabelon enhavantan kombinaĵon de produkto, lando kaj jaro. Por generi testan datkadron, vi povas ruli la sekvan kodon:

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

Nia tasko estas vastigi la datumkadron tiel ke unu kolumno enhavas datumojn por ĉiu kombinaĵo de produkto kaj lando. Por fari tion, simple enigu la argumenton nomoj_de vektoro enhavanta la nomojn de la kampoj kunfandataj.

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

Vi ankaŭ povas apliki specifojn al funkcio pivot_wider(). Sed kiam submetita al pivot_wider() la specifo faras la kontraŭan konvertiĝon pivot_longer(): La kolumnoj specifitaj en .nomo, uzante valorojn de .valoro kaj aliaj kolumnoj.

Por ĉi tiu datumaro, vi povas generi kutiman specifon, se vi volas, ke ĉiu ebla lando kaj produkta kombinaĵo havu sian propran kolumnon, ne nur tiujn ĉeestantajn en la datumoj:

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

Pluraj altnivelaj ekzemploj de laborado kun la nova tidyr koncepto

Purigado de datumoj uzante la usonan Censan Enspezon kaj Rento-datumojn kiel ekzemplon.

Datenaro us_rent_enspezo enhavas mezajn enspezojn kaj luo-informojn por ĉiu ŝtato en Usono por 2017 (datenoj disponeblaj en pakaĵo tidycensus).

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 formo en kiu la datumoj estas konservitaj en la datumaro us_rent_enspezo labori kun ili estas ege maloportuna, do ni ŝatus krei datuman aron kun kolumnoj: renton, lui_moe, venu, income_moe. Estas multaj manieroj krei ĉi tiun specifon, sed la ĉefa punkto estas, ke ni devas generi ĉiun kombinaĵon de variaj valoroj kaj takso/moekaj poste generi la kolumnan nomon.

  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

Provizante ĉi tiun specifon pivot_wider() donas al ni la rezulton, kiun ni serĉas:

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

La Monda Banko

Foje alporti datuman aron en la deziratan formon postulas plurajn paŝojn.
Datenaro monda_banko_pop enhavas datumojn de la Monda Banko pri la loĝantaro de ĉiu lando inter 2000 kaj 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>

Nia celo estas krei bonordan datuman aron kun ĉiu variablo en sia propra kolumno. Estas neklare precize kiaj paŝoj necesas, sed ni komencos per la plej evidenta problemo: la jaro estas disvastigita tra pluraj kolumnoj.

Por ripari tion vi devas uzi la funkcion 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

La sekva paŝo estas rigardi la indikilon variablo.
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

Kie SP.POP.GROW estas loĝantarkresko, SP.POP.TOTL estas totala populacio, kaj SP.URB. * la sama afero, sed nur por urbaj areoj. Ni dividu ĉi tiujn valorojn en du variablojn: areo - areo (totala aŭ urba) kaj variablo enhavanta realajn datumojn (loĝantaro aŭ kresko):

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

Nun ĉio, kion ni devas fari, estas dividi la variablon en du kolumnojn:

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

Kontaktlisto

Unu lasta ekzemplo, imagu, ke vi havas kontaktliston, kiun vi kopiis kaj algluis de retejo:

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

Tabeligi ĉi tiun liston estas sufiĉe malfacila ĉar ne ekzistas variablo, kiu identigas, kiuj datumoj apartenas al kiu kontakto. Ni povas ripari ĉi tion rimarkante, ke la datumoj de ĉiu nova kontakto komenciĝas per "nomo", do ni povas krei unikan identigilon kaj pligrandigi ĝin je unu ĉiufoje kiam la kampa kolumno enhavas la valoron "nomo":

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

Nun kiam ni havas unikan identigilon por ĉiu kontakto, ni povas turni la kampon kaj valoron en kolumnojn:

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>

konkludo

Mia persona opinio estas ke la nova koncepto tidyr vere pli intuicia, kaj signife supera en funkcieco al heredaj funkcioj spread() и gather(). Mi esperas, ke ĉi tiu artikolo helpis vin trakti pivot_longer() и pivot_wider().

fonto: www.habr.com

Aldoni komenton