R paket tidyr in njegovi novi funkciji pivot_longer in pivot_wider

Paket tidyr vključeno v jedro ene najbolj priljubljenih knjižnic v jeziku R - tidyverse.
Glavni namen paketa je spraviti podatke v natančno obliko.

Že na voljo na Habréju objave posvečen temu paketu, vendar sega v leto 2015. In želim vam povedati o najnovejših spremembah, ki jih je pred nekaj dnevi napovedal njen avtor Hedley Wickham.

R paket tidyr in njegovi novi funkciji pivot_longer in pivot_wider

S.J.K.: Bosta gather() in spread() opuščena?

Hadley Wickham: Do določene mere. Ne bomo več priporočali uporabe teh funkcij in odpravljali napak v njih, vendar bodo še naprej prisotne v paketu v trenutnem stanju.

Vsebina

Če vas zanima analiza podatkov, vas bo morda zanimal moj telegram и youtube kanalov. Večina vsebine je posvečena jeziku R.

Koncept TidyData

Cilj tidyr — pomaga pripeljati podatke v tako imenovano čisto obliko. Čisti podatki so podatki, kjer:

  • Vsaka spremenljivka je v stolpcu.
  • Vsako opazovanje je niz.
  • Vsaka vrednost je celica.

Pri analizi je veliko lažje in bolj priročno delati s podatki, ki so predstavljeni v urejenih podatkih.

Glavne funkcije, vključene v paket tidyr

tidyr vsebuje nabor funkcij za preoblikovanje tabel:

  • fill() — polnjenje manjkajočih vrednosti v stolpcu s prejšnjimi vrednostmi;
  • separate() — razdeli eno polje na več z uporabo ločila;
  • unite() — izvede operacijo združevanja več polj v eno, inverzno delovanje funkcije separate();
  • pivot_longer() — funkcija, ki pretvori podatke iz širokega formata v dolgi format;
  • pivot_wider() - funkcija, ki pretvori podatke iz dolgega formata v široki format. Delovanje, obratno od tistega, ki ga izvaja funkcija pivot_longer().
  • gather()zastarel — funkcija, ki pretvori podatke iz širokega formata v dolgi format;
  • spread()zastarel - funkcija, ki pretvori podatke iz dolgega formata v široki format. Delovanje, obratno od tistega, ki ga izvaja funkcija gather().

Nov koncept za pretvorbo podatkov iz širokega v dolgi format in obratno

Prej so se za tovrstno transformacijo uporabljale funkcije gather() и spread(). Z leti obstoja teh funkcij je postalo očitno, da za večino uporabnikov, vključno z avtorjem paketa, imena teh funkcij in njihovi argumenti niso bili povsem očitni, kar je povzročalo težave pri iskanju in razumevanju, katera od teh funkcij pretvori datumski okvir iz širokega v dolgi format in obratno.

V zvezi s tem v tidyr Dodani sta bili dve novi pomembni funkciji, ki sta zasnovani za preoblikovanje datumskih okvirjev.

Nove funkcije pivot_longer() и pivot_wider() navdihnile nekatere funkcije v paketu cdata, ki sta ga ustvarila John Mount in Nina Zumel.

Namestitev najnovejše različice tidyr 0.8.3.9000

Za namestitev nove, najnovejše različice paketa tidyr 0.8.3.9000, kjer so na voljo nove funkcije, uporabite naslednjo kodo.

devtools::install_github("tidyverse/tidyr")

V času pisanja so te funkcije na voljo samo v različici paketa za razvijalce na GitHubu.

Prehod na nove funkcije

Pravzaprav pretvorba starih skriptov za delo z novimi funkcijami ni težka, za boljše razumevanje bom vzel primer iz dokumentacije starih funkcij in pokazal, kako se iste operacije izvajajo z uporabo novih pivot_*() funkcije.

Pretvori širok format v dolg format.

Primer kode iz dokumentacije funkcije zbiranja

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

Pretvorba dolgega formata v široki format.

Primer kode iz dokumentacije funkcije širjenja

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

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

Ker v zgornjih primerih dela z pivot_longer() и pivot_wider(), v originalni tabeli Zaloge v argumentih ni navedenih stolpcev names_to и values_to njihova imena morajo biti v narekovajih.

Tabela, ki vam bo pomagala najlažje ugotoviti, kako preklopiti na delo z novim konceptom tidyr.

R paket tidyr in njegovi novi funkciji pivot_longer in pivot_wider

Opomba avtorja

Vse spodnje besedilo je prilagodljivo, rekel bi celo brezplačen prevod vinjete z uradne spletne strani knjižnice tidyverse.

Preprost primer pretvorbe podatkov iz širokega v dolgi format

pivot_longer () — podaljša nize podatkov z zmanjšanjem števila stolpcev in povečanjem števila vrstic.

R paket tidyr in njegovi novi funkciji pivot_longer in pivot_wider

Za zagon primerov, predstavljenih v članku, morate najprej povezati potrebne pakete:

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

Recimo, da imamo tabelo z rezultati ankete, ki je (med drugim) ljudi spraševala o njihovi veri in letnem dohodku:

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

Ta tabela vsebuje podatke o veri anketirancev v vrsticah, ravni dohodka pa so razpršene po imenih stolpcev. Število anketirancev iz vsake kategorije je shranjeno v vrednostih celic na presečišču vere in ravni dohodka. Če želite tabelo prenesti v urejeno, pravilno obliko, je dovolj, da uporabite 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

Argumenti funkcije pivot_longer()

  • Prvi argument cols, opisuje, katere stolpce je treba združiti. V tem primeru so vsi stolpci razen čas.
  • Prepir names_to poda ime spremenljivke, ki bo ustvarjena iz imen stolpcev, ki smo jih združili.
  • values_to podaja ime spremenljivke, ki bo ustvarjena iz podatkov, shranjenih v vrednostih celic združenih stolpcev.

Tehnični podatki

To je nova funkcionalnost paketa tidyr, ki prej ni bil na voljo pri delu s podedovanimi funkcijami.

Specifikacija je podatkovni okvir, katerega vsaka vrstica ustreza enemu stolpcu v novem izhodnem datumskem okviru, in dva posebna stolpca, ki se začneta z:

  • . Ime vsebuje izvirno ime stolpca.
  • .vrednost vsebuje ime stolpca, ki bo vseboval vrednosti celic.

Preostali stolpci specifikacije odražajo, kako bo novi stolpec prikazal ime stisnjenih stolpcev iz . Ime.

Specifikacija opisuje metapodatke, shranjene v imenu stolpca, z eno vrstico za vsak stolpec in enim stolpcem za vsako spremenljivko, skupaj z imenom stolpca, ta definicija se morda trenutno zdi zmedena, a po ogledu nekaj primerov bo postala zelo jasneje.

Bistvo specifikacije je, da lahko pridobite, spremenite in definirate nove metapodatke za podatkovni okvir, ki se pretvarja.

Če želite delati s specifikacijami pri pretvorbi tabele iz širokega formata v dolgi format, uporabite funkcijo pivot_longer_spec().

Ta funkcija deluje tako, da vzame poljuben datumski okvir in ustvari njegove metapodatke na zgoraj opisan način.

Za primer vzemimo nabor podatkov who, ki je priložen paketu tidyr. Ta niz podatkov vsebuje informacije, ki jih je zagotovila mednarodna zdravstvena organizacija o incidenci tuberkuloze.

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

Zgradimo njegovo specifikacijo.

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

polja država, iso2, iso3 so že spremenljivke. Naša naloga je, da obrnemo stolpce z novo_sp_m014 o newrel_f65.

Imena teh stolpcev hranijo naslednje informacije:

  • Predpona new_ označuje, da stolpec vsebuje podatke o novih primerih tuberkuloze, trenutni datumski okvir vsebuje samo podatke o novih boleznih, zato ta predpona v trenutnem kontekstu nima nobenega pomena.
  • sp/rel/sp/ep opisuje metodo za diagnosticiranje bolezni.
  • m/f pacientov spol.
  • 014/1524/2535/3544/4554/65 starostni razpon bolnika.

Te stolpce lahko razdelimo s funkcijo extract()z uporabo regularnega izraza.

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

Upoštevajte stolpec . Ime mora ostati nespremenjen, saj je to naš indeks v imenih stolpcev izvirnega niza podatkov.

Spol in starost (stolpci spol и starost) imajo fiksne in znane vrednosti, zato je priporočljivo pretvoriti te stolpce v faktorje:

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

Končno, da uporabimo specifikacijo, ki smo jo ustvarili, na prvotni datumski okvir kdo moramo uporabiti argument spec v funkciji 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

Vse, kar smo pravkar naredili, lahko shematično prikažemo na naslednji način:

R paket tidyr in njegovi novi funkciji pivot_longer in pivot_wider

Specifikacija z uporabo več vrednosti (.value)

V zgornjem primeru stolpec specifikacij .vrednost vseboval samo eno vrednost, v večini primerov je tako.

Toda občasno lahko pride do situacije, ko morate zbrati podatke iz stolpcev z različnimi vrstami podatkov v vrednosti. Uporaba podedovane funkcije spread() to bi bilo precej težko narediti.

Spodnji primer je vzet iz vinjete na paket podatki.tabela.

Ustvarimo podatkovni okvir za usposabljanje.

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

Ustvarjeni datumski okvir vsebuje podatke o otrocih ene družine v vsaki vrstici. Družine imajo lahko enega ali dva otroka. Za vsakega otroka so navedeni podatki o datumu rojstva in spolu, podatki za vsakega otroka pa so v ločenih stolpcih, naša naloga je, da te podatke spravimo v pravilno obliko za analizo.

Upoštevajte, da imamo dve spremenljivki s podatki o vsakem otroku: njihov spol in datum rojstva (stolpci s predpono Krst vsebujejo datum rojstva, stolpce s predpono spol vsebujejo spol otroka). Pričakovani rezultat je, da se prikažejo v ločenih stolpcih. To lahko naredimo tako, da ustvarimo specifikacijo, v kateri je stolpec .value bo imel dva različna pomena.

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

Torej, poglejmo korak za korakom dejanja, ki jih izvaja zgornja koda.

  • pivot_longer_spec(-family) — ustvarite specifikacijo, ki stisne vse obstoječe stolpce razen stolpca družine.
  • separate(col = name, into = c(".value", "child")) - razdelite stolpec . Ime, ki vsebuje imena izvornih polj, z uporabo podčrtaja in vnosom nastalih vrednosti v stolpce .vrednost и otrok.
  • mutate(child = parse_number(child)) — transformirajte vrednosti polja otrok iz besedilnega v številski podatkovni tip.

Zdaj lahko dobljeno specifikacijo uporabimo na izvirnem podatkovnem okvirju in tabelo pripeljemo v želeno obliko.

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

Uporabljamo argumente na.rm = TRUE, ker trenutna oblika podatkov zahteva ustvarjanje dodatnih vrstic za neobstoječa opazovanja. Ker družina 2 ima samo enega otroka, na.rm = TRUE zagotavlja, da bo družina 2 imela eno vrstico v izhodu.

Pretvorba datumskih okvirjev iz dolgega v široki format

pivot_wider() - je inverzna transformacija in obratno poveča število stolpcev datumskega okvira z zmanjšanjem števila vrstic.

R paket tidyr in njegovi novi funkciji pivot_longer in pivot_wider

Tovrstna transformacija se izredno redko uporablja za pripravo podatkov v natančno obliko, vendar je ta tehnika lahko uporabna za ustvarjanje vrtilnih tabel, ki se uporabljajo v predstavitvah, ali za integracijo z nekaterimi drugimi orodji.

Pravzaprav funkcije pivot_longer() и pivot_wider() so simetrični in ustvarjajo dejanja, inverzna drug drugemu, tj. df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) bo vrnil originalni df.

Najenostavnejši primer pretvorbe tabele v širok format

Za prikaz delovanja funkcije pivot_wider() uporabili bomo nabor podatkov fish_encounters, ki hrani podatke o tem, kako različne postaje beležijo gibanje rib vzdolž reke.

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

V večini primerov bo ta tabela bolj informativna in enostavnejša za uporabo, če podate podatke za vsako postajo v ločenem stolpcu.

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>

Ta niz podatkov beleži samo informacije, ko postaja zazna ribe, tj. če kakšne ribe katera postaja ni zabeležila, tega podatka ne bo v tabeli. To pomeni, da bo izhod zapolnjen z NA.

Vendar v tem primeru vemo, da odsotnost zapisa pomeni, da riba ni bila videna, zato lahko uporabimo argument vrednosti_polnila v funkciji pivot_wider() in izpolnite te manjkajoče vrednosti z ničlami:

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>

Ustvarjanje imena stolpca iz več izvornih spremenljivk

Predstavljajte si, da imamo tabelo, ki vsebuje kombinacijo izdelka, države in leta. Če želite ustvariti preskusni datumski okvir, lahko zaženete naslednjo kodo:

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

Naša naloga je razširiti podatkovni okvir tako, da en stolpec vsebuje podatke za vsako kombinacijo izdelka in države. Če želite to narediti, samo vnesite argument imena_iz vektor, ki vsebuje imena polj, ki jih želite združiti.

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

Za funkcijo lahko uporabite tudi specifikacije pivot_wider(). Ko pa se predloži pivot_wider() specifikacija naredi nasprotno pretvorbo pivot_longer(): Stolpci, navedeni v . Ime, z uporabo vrednosti iz .vrednost in druge stolpce.

Za ta nabor podatkov lahko ustvarite specifikacijo po meri, če želite, da ima vsaka možna kombinacija držav in izdelkov svoj stolpec, ne samo tisti, ki so prisotni v podatkih:

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

Več naprednih primerov dela z novim konceptom tidyr

Čiščenje podatkov z uporabo nabora podatkov o dohodku in najemnini popisa v ZDA kot primer.

Nabor podatkov us_rent_income vsebuje informacije o medianem dohodku in najemnini za vsako državo v ZDA za leto 2017 (nabor podatkov je na voljo v paketu 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

V obliki, v kateri so podatki shranjeni v naboru podatkov us_rent_income delo z njimi je izjemno neprijetno, zato bi radi ustvarili nabor podatkov s stolpci: najem, rent_moe, kako, dohodek_moe. Obstaja veliko načinov za ustvarjanje te specifikacije, vendar je glavna točka, da moramo ustvariti vsako kombinacijo vrednosti spremenljivk in ocena/moein nato ustvarite ime stolpca.

  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

Zagotavljanje te specifikacije pivot_wider() nam daje rezultat, ki ga iščemo:

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

Svetovna banka

Včasih je za prenos nabora podatkov v želeno obliko potrebno več korakov.
Nabor podatkov svetovna_banka_pop vsebuje podatke Svetovne banke o številu prebivalcev vsake države med letoma 2000 in 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>

Naš cilj je ustvariti čist nabor podatkov z vsako spremenljivko v svojem stolpcu. Ni jasno, kateri koraki so potrebni, vendar bomo začeli z najbolj očitno težavo: leto je razporejeno v več stolpcih.

Če želite to popraviti, morate uporabiti funkcijo 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

Naslednji korak je ogled spremenljivke indikatorja.
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

Kjer je SP.POP.GROW rast prebivalstva, SP.POP.TOTL skupno prebivalstvo in SP.URB. * isto, vendar samo za urbana območja. Razdelimo te vrednosti na dve spremenljivki: površina - površina (celotna ali urbana) in spremenljivka, ki vsebuje dejanske podatke (prebivalstvo ali rast):

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

Zdaj moramo samo spremenljivko razdeliti v dva stolpca:

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

Seznam kontaktov

Še zadnji primer, predstavljajte si, da imate seznam stikov, ki ste ga kopirali in prilepili s spletnega mesta:

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

Tabela tega seznama je precej težavna, ker ni spremenljivke, ki bi identificirala, kateri podatki pripadajo kateremu stiku. To lahko popravimo tako, da opazimo, da se podatki za vsak nov stik začnejo z "ime", tako da lahko ustvarimo edinstven identifikator in ga povečamo za eno vsakič, ko stolpec polja vsebuje vrednost "ime":

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

Zdaj, ko imamo edinstven ID za vsak stik, lahko spremenimo polje in vrednost v stolpce:

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>

Zaključek

Moje osebno mnenje je, da je nov koncept tidyr resnično bolj intuitiven in bistveno boljši v funkcionalnosti od podedovanih funkcij spread() и gather(). Upam, da vam je ta članek pomagal pri soočanju pivot_longer() и pivot_wider().

Vir: www.habr.com

Dodaj komentar