R-pakken tidyr og dens nye funksjoner pivot_longer og pivot_wider

pakke ryddig inkludert i kjernen av et av de mest populære bibliotekene i R-språket - ryddig.
Hovedformålet med pakken er å bringe dataene inn i en nøyaktig form.

Allerede tilgjengelig på Habré utgivelse dedikert til denne pakken, men den dateres tilbake til 2015. Og jeg vil fortelle deg om de mest aktuelle endringene, som ble annonsert for noen dager siden av forfatteren, Hedley Wickham.

R-pakken tidyr og dens nye funksjoner pivot_longer og pivot_wider

SJK: Vil gather() og spread() bli avskrevet?

Hadley Wickham: Til en viss grad. Vi vil ikke lenger anbefale bruk av disse funksjonene og fikse feil i dem, men de vil fortsette å være tilstede i pakken i den nåværende tilstanden.

Innhold

Hvis du er interessert i dataanalyse, kan du være interessert i min telegram и youtube kanaler. Det meste av innholdet er dedikert til R-språket.

TidyData konsept

target ryddig — hjelpe deg med å bringe dataene til en såkalt ryddig form. Ryddige data er data der:

  • Hver variabel er i en kolonne.
  • Hver observasjon er en streng.
  • Hver verdi er en celle.

Det er mye enklere og mer praktisk å jobbe med data som presenteres i ryddige data når man utfører analyse.

Hovedfunksjoner inkludert i tidyr-pakken

tidyr inneholder et sett med funksjoner designet for å transformere tabeller:

  • fill() — fylle manglende verdier i en kolonne med tidligere verdier;
  • separate() — deler ett felt i flere ved hjelp av en skilletegn;
  • unite() — utfører operasjonen med å kombinere flere felt til ett, den inverse handlingen til funksjonen separate();
  • pivot_longer() — en funksjon som konverterer data fra bredformat til langt format;
  • pivot_wider() - en funksjon som konverterer data fra langt format til bredt format. Omvendt operasjon av den som utføres av funksjonen pivot_longer().
  • gather()Utdatert — en funksjon som konverterer data fra bredformat til langt format;
  • spread()Utdatert - en funksjon som konverterer data fra langt format til bredt format. Omvendt operasjon av den som utføres av funksjonen gather().

Nytt konsept for konvertering av data fra bredt til langt format og omvendt

Tidligere ble funksjoner brukt for denne typen transformasjon gather() и spread(). I løpet av årene med eksistensen av disse funksjonene ble det åpenbart at for de fleste brukere, inkludert forfatteren av pakken, var navnene på disse funksjonene og deres argumenter ikke helt åpenbare, og forårsaket vanskeligheter med å finne dem og forstå hvilke av disse funksjonene som konverterer en datoramme fra bredt til langt format, og omvendt.

I denne forbindelse, i ryddig To nye, viktige funksjoner er lagt til som er designet for å transformere datorammer.

Nye funksjoner pivot_longer() и pivot_wider() ble inspirert av noen av funksjonene i pakken cdata, laget av John Mount og Nina Zumel.

Installerer den nyeste versjonen av tidyr 0.8.3.9000

For å installere den nye, nyeste versjonen av pakken ryddig 0.8.3.9000, der nye funksjoner er tilgjengelige, bruk følgende kode.

devtools::install_github("tidyverse/tidyr")

I skrivende stund er disse funksjonene kun tilgjengelige i dev-versjonen av pakken på GitHub.

Overgang til nye funksjoner

Faktisk er det ikke vanskelig å overføre gamle skript for å jobbe med nye funksjoner; for bedre forståelse vil jeg ta et eksempel fra dokumentasjonen av gamle funksjoner og vise hvordan de samme operasjonene utføres med nye. pivot_*() funksjoner.

Konverter bredformat til langt format.

Eksempelkode fra dokumentasjonen for samlefunksjonen

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

Konvertering av langt format til bredformat.

Eksempelkode fra spredningsfunksjonsdokumentasjon

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

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

Fordi i eksemplene ovenfor på arbeid med pivot_longer() и pivot_wider(), i den opprinnelige tabellen bestandene ingen kolonner oppført i argumenter navn_til и verdier_til deres navn må stå i anførselstegn.

En tabell som hjelper deg lettest å finne ut hvordan du bytter til å jobbe med et nytt konsept ryddig.

R-pakken tidyr og dens nye funksjoner pivot_longer og pivot_wider

Merknad fra forfatteren

All teksten nedenfor er adaptiv, jeg vil til og med si fri oversettelse vignetter fra det offisielle nettstedet til tidyverse-biblioteket.

Et enkelt eksempel på konvertering av data fra bredt til langt format

pivot_longer () — gjør datasett lengre ved å redusere antall kolonner og øke antall rader.

R-pakken tidyr og dens nye funksjoner pivot_longer og pivot_wider

For å kjøre eksemplene presentert i artikkelen, må du først koble til de nødvendige pakkene:

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

La oss si at vi har en tabell med resultatene av en undersøkelse som (blant annet) spurte folk om deres religion og årsinntekt:

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

Denne tabellen inneholder respondentenes religionsdata i rader, og inntektsnivåene er spredt over kolonnenavn. Antall respondenter fra hver kategori er lagret i celleverdiene i skjæringspunktet mellom religion og inntektsnivå. For å bringe bordet i et pent, riktig format, er det nok å bruke 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

Funksjonsargumenter pivot_longer()

  • Første argument krage, beskriver hvilke kolonner som må slås sammen. I dette tilfellet er alle kolonner unntatt tid.
  • argument navn_til gir navnet på variabelen som vil bli opprettet fra navnene på kolonnene vi sammenkoblet.
  • verdier_til gir navnet på en variabel som vil bli opprettet fra dataene som er lagret i verdiene til cellene i de sammenslåtte kolonnene.

Spesifikasjoner

Dette er en ny funksjonalitet i pakken ryddig, som tidligere ikke var tilgjengelig når du arbeidet med eldre funksjoner.

En spesifikasjon er en dataramme, der hver rad tilsvarer én kolonne i den nye utdatarammen, og to spesielle kolonner som begynner med:

  • . Navn inneholder det opprinnelige kolonnenavnet.
  • .verdi inneholder navnet på kolonnen som skal inneholde celleverdiene.

De resterende kolonnene i spesifikasjonen gjenspeiler hvordan den nye kolonnen vil vise navnet på de komprimerte kolonnene fra . Navn.

Spesifikasjonen beskriver metadata som er lagret i et kolonnenavn, med en rad for hver kolonne og en kolonne for hver variabel, kombinert med kolonnenavnet, denne definisjonen kan virke forvirrende for øyeblikket, men etter å ha sett på noen få eksempler vil det bli mye klarere.

Poenget med spesifikasjonen er at du kan hente, modifisere og definere nye metadata for datarammen som konverteres.

For å jobbe med spesifikasjoner når du konverterer en tabell fra et bredt format til et langt format, bruk funksjonen pivot_longer_spec().

Hvordan denne funksjonen fungerer er at den tar en hvilken som helst datoramme og genererer metadataene på den måten som er beskrevet ovenfor.

Som et eksempel, la oss ta who-datasettet som følger med pakken ryddig. Dette datasettet inneholder informasjon gitt av den internasjonale helseorganisasjonen om forekomsten av tuberkulose.

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

La oss bygge spesifikasjonen.

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

felt land, iso2, iso3 er allerede variabler. Vår oppgave er å vende kolonnene med new_sp_m014newrel_f65.

Navnene på disse kolonnene lagrer følgende informasjon:

  • prefiks new_ indikerer at kolonnen inneholder data om nye tilfeller av tuberkulose, gjeldende datoramme inneholder kun informasjon om nye sykdommer, så dette prefikset i den aktuelle konteksten har ingen betydning.
  • sp/rel/sp/ep beskriver en metode for å diagnostisere en sykdom.
  • m/f pasientens kjønn.
  • 014/1524/2535/3544/4554/65 pasientens aldersgruppe.

Vi kan dele disse kolonnene ved å bruke funksjonen extract()ved hjelp av regulære uttrykk.

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

Legg merke til kolonnen . Navn bør forbli uendret siden dette er vår indeks i kolonnenavnene til det originale datasettet.

Kjønn og alder (kolonner kjønn и alder) har faste og kjente verdier, så det anbefales å konvertere disse kolonnene til faktorer:

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

Til slutt, for å bruke spesifikasjonen vi opprettet på den originale daterammen som vi må bruke et argument spec i funksjon 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

Alt vi nettopp gjorde kan skjematisk avbildes som følger:

R-pakken tidyr og dens nye funksjoner pivot_longer og pivot_wider

Spesifikasjon som bruker flere verdier (.value)

I eksemplet ovenfor, spesifikasjonskolonnen .verdi inneholdt bare én verdi, i de fleste tilfeller er dette tilfellet.

Men av og til kan det oppstå en situasjon når du trenger å samle inn data fra kolonner med ulike datatyper i verdier. Bruker en eldre funksjon spread() dette ville være ganske vanskelig å gjøre.

Eksemplet nedenfor er hentet fra vignetter til pakken data bord.

La oss lage en treningsdataramme.

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

Den opprettede datorammen inneholder data om barn i én familie i hver rad. Familier kan ha ett eller to barn. For hvert barn er det gitt data om fødselsdato og kjønn, og dataene for hvert barn er i separate kolonner; vår oppgave er å bringe disse dataene til riktig format for analyse.

Vær oppmerksom på at vi har to variabler med informasjon om hvert barn: deres kjønn og fødselsdato (kolonner med prefikset dåp inneholder fødselsdato, kolonner med prefiks kjønn inneholde barnets kjønn). Det forventede resultatet er at de skal vises i separate kolonner. Vi kan gjøre dette ved å generere en spesifikasjon der kolonnen .value vil ha to forskjellige betydninger.

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

Så la oss ta en trinnvis titt på handlingene utført av koden ovenfor.

  • pivot_longer_spec(-family) — lag en spesifikasjon som komprimerer alle eksisterende kolonner bortsett fra familiekolonnen.
  • separate(col = name, into = c(".value", "child")) - del kolonnen . Navn, som inneholder navnene på kildefeltene, ved å bruke understreken og skrive inn de resulterende verdiene i kolonnene .verdi и barn.
  • mutate(child = parse_number(child)) — transformere feltverdiene barn fra tekst til numerisk datatype.

Nå kan vi bruke den resulterende spesifikasjonen på den originale datarammen og bringe tabellen til ønsket form.

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

Vi bruker argument na.rm = TRUE, fordi den nåværende formen for dataene tvinger opprettelsen av ekstra rader for ikke-eksisterende observasjoner. Fordi familie 2 har bare ett barn, na.rm = TRUE garanterer at familie 2 vil ha én rad i utgangen.

Konvertering av datorammer fra langt til bredt format

pivot_wider() - er den inverse transformasjonen, og vice versa øker antall kolonner i datorammen ved å redusere antall rader.

R-pakken tidyr og dens nye funksjoner pivot_longer og pivot_wider

Denne typen transformasjon brukes ekstremt sjelden for å bringe data til en nøyaktig form, men denne teknikken kan være nyttig for å lage pivottabeller som brukes i presentasjoner, eller for integrasjon med noen andre verktøy.

Egentlig funksjonene pivot_longer() и pivot_wider() er symmetriske, og produserer handlinger som er omvendt til hverandre, dvs.: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) vil returnere den originale df.

Det enkleste eksemplet på å konvertere en tabell til et bredt format

For å demonstrere hvordan funksjonen fungerer pivot_wider() vi vil bruke datasettet fiske_møter, som lagrer informasjon om hvordan ulike stasjoner registrerer bevegelsen av fisk langs elva.

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

I de fleste tilfeller vil denne tabellen være mer informativ og enklere å bruke hvis du presenterer informasjon for hver stasjon i en egen kolonne.

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>

Dette datasettet registrerer kun informasjon når det er oppdaget fisk av stasjonen, dvs. hvis noen fisk ikke ble registrert av en stasjon, vil ikke disse dataene være i tabellen. Dette betyr at utgangen vil bli fylt med NA.

Men i dette tilfellet vet vi at fraværet av en registrering betyr at fisken ikke ble sett, så vi kan bruke argumentet verdier_fyll i funksjon pivot_wider() og fyll disse manglende verdiene med nuller:

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>

Generering av et kolonnenavn fra flere kildevariabler

Tenk deg at vi har en tabell som inneholder en kombinasjon av produkt, land og år. For å generere en testdatoramme kan du kjøre følgende kode:

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

Vår oppgave er å utvide datarammen slik at én kolonne inneholder data for hver kombinasjon av produkt og land. For å gjøre dette, bare gi argumentet navn_fra en vektor som inneholder navnene på feltene som skal slås sammen.

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

Du kan også bruke spesifikasjoner for en funksjon pivot_wider(). Men når det sendes til pivot_wider() spesifikasjonen gjør den motsatte konverteringen pivot_longer(): Kolonnene spesifisert i . Navn, ved å bruke verdier fra .verdi og andre kolonner.

For dette datasettet kan du generere en egendefinert spesifikasjon hvis du vil at alle mulige land og produktkombinasjoner skal ha sin egen kolonne, ikke bare de som finnes i dataene:

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

Flere avanserte eksempler på arbeid med det nye tidyr-konseptet

Rydd opp i data ved å bruke US Census Income and Rent datasettet som eksempel.

Datasett us_leieinntekt inneholder medianinntekt og leieinformasjon for hver stat i USA for 2017 (datasett tilgjengelig i pakken 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

I den formen dataene er lagret i datasettet us_leieinntekt å jobbe med dem er ekstremt upraktisk, så vi ønsker å lage et datasett med kolonner: leie, leie_moe, Kom, inntekt_moe. Det er mange måter å lage denne spesifikasjonen på, men hovedpoenget er at vi må generere hver kombinasjon av variabelverdier og anslag/moeog generer deretter kolonnenavnet.

  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

Leverer denne spesifikasjonen pivot_wider() gir oss resultatet vi ser etter:

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

Verdensbanken

Noen ganger krever det flere trinn å bringe et datasett til ønsket form.
Datasett verdensbankpop inneholder data fra Verdensbanken om befolkningen i hvert land mellom 2000 og 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>

Målet vårt er å lage et ryddig datasett med hver variabel i sin egen kolonne. Det er uklart nøyaktig hvilke trinn som trengs, men vi starter med det mest åpenbare problemet: året er spredt over flere kolonner.

For å fikse dette må du bruke funksjonen 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

Neste trinn er å se på indikatorvariabelen.
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

Der SP.POP.GROW er befolkningsvekst, SP.POP.TOTL er total befolkning, og SP.URB. * det samme, men bare for urbane områder. La oss dele disse verdiene inn i to variabler: område - område (totalt eller urbant) og en variabel som inneholder faktiske data (befolkning eller vekst):

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

Alt vi trenger å gjøre er å dele variabelen i to kolonner:

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

Kontaktliste

Et siste eksempel, forestill deg at du har en kontaktliste som du kopierte og limte inn fra et nettsted:

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

Tabellering av denne listen er ganske vanskelig fordi det ikke er noen variabel som identifiserer hvilke data som tilhører hvilken kontakt. Vi kan fikse dette ved å merke oss at hver ny kontakts data starter med "navn", så vi kan lage en unik identifikator og øke den med én hver gang feltkolonnen inneholder verdien "navn":

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

Nå som vi har en unik ID for hver kontakt, kan vi gjøre om feltet og verdien til kolonner:

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>

Konklusjon

Min personlige mening er at det nye konseptet ryddig virkelig mer intuitiv, og betydelig overlegen i funksjonalitet i forhold til eldre funksjoner spread() и gather(). Jeg håper denne artikkelen hjalp deg med å håndtere pivot_longer() и pivot_wider().

Kilde: www.habr.com

Legg til en kommentar