R-paketet tidyr och dess nya funktioner pivot_longer och pivot_wider

paket tidyr ingår i kärnan av ett av de mest populära biblioteken i R-språket - tidyverse.
Huvudsyftet med paketet är att få informationen till en korrekt form.

Finns redan på Habré publikation tillägnad detta paket, men det går tillbaka till 2015. Och jag vill berätta om de senaste förändringarna, som tillkännagavs för några dagar sedan av dess författare, Hedley Wickham.

R-paketet tidyr och dess nya funktioner pivot_longer och pivot_wider

SJK: Kommer gather() och spread() att fasas ut?

Hadley Wickham: I viss utsträckning. Vi kommer inte längre att rekommendera användningen av dessa funktioner och fixa buggar i dem, men de kommer att fortsätta att finnas i paketet i deras nuvarande tillstånd.

Innehåll

Om du är intresserad av dataanalys kan du vara intresserad av min telegram и Youtube kanaler. Det mesta av innehållet är tillägnat R-språket.

TidyData koncept

Mål tidyr — hjälpa dig att få uppgifterna till en så kallad snygg form. Snygga data är data där:

  • Varje variabel finns i en kolumn.
  • Varje observation är en sträng.
  • Varje värde är en cell.

Det är mycket enklare och bekvämare att arbeta med data som presenteras i snygg data när man gör analys.

Huvudfunktioner ingår i tidyr-paketet

tidyr innehåller en uppsättning funktioner utformade för att transformera tabeller:

  • fill() — fylla i saknade värden i en kolumn med tidigare värden;
  • separate() — delar upp ett fält i flera med hjälp av en separator;
  • unite() — utför operationen att kombinera flera fält till ett, den omvända verkan av funktionen separate();
  • pivot_longer() — en funktion som konverterar data från bredformat till långformat;
  • pivot_wider() - en funktion som konverterar data från långformat till bredformat. Den omvända operationen av den som utförs av funktionen pivot_longer().
  • gather()föråldrad — en funktion som konverterar data från bredformat till långformat;
  • spread()föråldrad - en funktion som konverterar data från långformat till bredformat. Den omvända operationen av den som utförs av funktionen gather().

Nytt koncept för att konvertera data från brett till långt format och vice versa

Tidigare användes funktioner för denna typ av transformation gather() и spread(). Under åren av existensen av dessa funktioner blev det uppenbart att för de flesta användare, inklusive författaren till paketet, var namnen på dessa funktioner och deras argument inte helt uppenbara och orsakade svårigheter att hitta dem och förstå vilka av dessa funktioner som konverterar en datumram från brett till långt format, och vice versa.

I detta avseende, i tidyr Två nya, viktiga funktioner har lagts till som är designade för att transformera datumramar.

Nya funktioner pivot_longer() и pivot_wider() inspirerades av några av funktionerna i paketet cdata, skapad av John Mount och Nina Zumel.

Installerar den senaste versionen av tidyr 0.8.3.9000

För att installera den nya, senaste versionen av paketet tidyr 0.8.3.9000, där nya funktioner är tillgängliga, använd följande kod.

devtools::install_github("tidyverse/tidyr")

I skrivande stund är dessa funktioner endast tillgängliga i dev-versionen av paketet på GitHub.

Övergång till nya funktioner

Faktum är att det inte är svårt att överföra gamla skript för att arbeta med nya funktioner; för bättre förståelse kommer jag att ta ett exempel från dokumentationen av gamla funktioner och visa hur samma operationer utförs med nya. pivot_*() funktioner.

Konvertera bredformat till långt format.

Exempelkod från samlingsfunktionsdokumentationen

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

Konvertera långformat till bredformat.

Exempelkod från spridningsfunktionsdokumentation

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

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

Därför att i ovanstående exempel på att arbeta med pivot_longer() и pivot_wider(), i den ursprungliga tabellen lagren inga kolumner listade i argument namn_till и värden_till deras namn måste stå inom citattecken.

En tabell som hjälper dig enklast att ta reda på hur du går över till att arbeta med ett nytt koncept tidyr.

R-paketet tidyr och dess nya funktioner pivot_longer och pivot_wider

Anteckning från författaren

All text nedan är adaptiv, jag skulle till och med säga fri översättning vinjetter från den officiella tidyverse-bibliotekets webbplats.

Ett enkelt exempel på att konvertera data från brett till långt format

pivot_longer () — gör datamängder längre genom att minska antalet kolumner och öka antalet rader.

R-paketet tidyr och dess nya funktioner pivot_longer och pivot_wider

För att köra exemplen som presenteras i artikeln måste du först ansluta de nödvändiga paketen:

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

Låt oss säga att vi har en tabell med resultaten från en undersökning som (bland annat) frågade människor om deras religion och årsinkomst:

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

Den här tabellen innehåller respondenternas religionsdata i rader, och inkomstnivåerna är utspridda över kolumnnamn. Antalet svarande från varje kategori lagras i cellvärdena i skärningspunkten mellan religion och inkomstnivå. För att få bordet i ett snyggt, korrekt format räcker det att använda 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

Funktionsargument pivot_longer()

  • Första argumentet cols, beskriver vilka kolumner som behöver slås samman. I det här fallet, alla kolumner utom tid.
  • argument namn_till ger namnet på variabeln som kommer att skapas från namnen på kolumnerna vi sammanfogade.
  • värden_till ger namnet på en variabel som kommer att skapas från data som lagras i värdena i cellerna i de sammanslagna kolumnerna.

Specifikationer

Detta är en ny funktion i paketet tidyr, som tidigare inte var tillgängligt när man arbetade med äldre funktioner.

En specifikation är en dataram där varje rad motsvarar en kolumn i den nya utdataramen och två specialkolumner som börjar med:

  • . Namn innehåller det ursprungliga kolumnnamnet.
  • .värde innehåller namnet på kolumnen som ska innehålla cellvärdena.

De återstående kolumnerna i specifikationen visar hur den nya kolumnen kommer att visa namnet på de komprimerade kolumnerna från . Namn.

Specifikationen beskriver metadata som lagras i ett kolumnnamn, med en rad för varje kolumn och en kolumn för varje variabel, kombinerat med kolumnnamnet, denna definition kan verka förvirrande för tillfället, men efter att ha tittat på några exempel kommer det att bli mycket klarare.

Poängen med specifikationen är att du kan hämta, modifiera och definiera ny metadata för den dataram som konverteras.

För att arbeta med specifikationer när du konverterar en tabell från ett brett format till ett långt format, använd funktionen pivot_longer_spec().

Hur den här funktionen fungerar är att den tar vilken datumram som helst och genererar sin metadata på det sätt som beskrivs ovan.

Som ett exempel, låt oss ta who-datasetet som tillhandahålls med paketet tidyr. Denna datauppsättning innehåller information från den internationella hälsoorganisationen om förekomsten av tuberkulos.

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

Låt oss bygga dess specifikation.

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

fält land, isoxnumx, isoxnumx är redan variabler. Vår uppgift är att vända kolumnerna med new_sp_m014newrel_f65.

Namnen på dessa kolumner lagrar följande information:

  • prefixet new_ indikerar att kolumnen innehåller uppgifter om nya fall av tuberkulos, den aktuella datumramen innehåller endast information om nya sjukdomar, så detta prefix i det aktuella sammanhanget har ingen betydelse.
  • sp/rel/sp/ep beskriver en metod för att diagnostisera en sjukdom.
  • m/f patientens kön.
  • 014/1524/2535/3544/4554/65 patientens åldersintervall.

Vi kan dela upp dessa kolumner med funktionen extract()använda reguljära uttryck.

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

Observera kolumnen . Namn bör förbli oförändrad eftersom detta är vårt index i kolumnnamnen för den ursprungliga datamängden.

Kön och ålder (kolumner kön и ålder) har fasta och kända värden, så det rekommenderas att konvertera dessa kolumner till faktorer:

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

Slutligen, för att tillämpa specifikationen som vi skapade på den ursprungliga datumramen som vi måste använda ett argument spec i funktion 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

Allt vi just gjorde kan schematiskt avbildas enligt följande:

R-paketet tidyr och dess nya funktioner pivot_longer och pivot_wider

Specifikation med flera värden (.value)

I exemplet ovan, specifikationskolumnen .värde innehöll endast ett värde, i de flesta fall är detta fallet.

Men ibland kan en situation uppstå när du behöver samla in data från kolumner med olika datatyper i värden. Använder en äldre funktion spread() detta skulle vara ganska svårt att göra.

Exemplet nedan är hämtat från vinjetter till paketet datatabell.

Låt oss skapa en träningsdataram.

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 skapade datumramen innehåller data om barn i en familj på varje rad. Familjer kan ha ett eller två barn. För varje barn ges uppgifter om födelsedatum och kön, och uppgifterna för varje barn finns i separata kolumner, vår uppgift är att föra dessa data till rätt format för analys.

Observera att vi har två variabler med information om varje barn: deras kön och födelsedatum (kolumner med prefixet propp innehåller födelsedatum, kolumner med prefix kön innehålla barnets kön). Det förväntade resultatet är att de ska visas i separata kolumner. Vi kan göra detta genom att skapa en specifikation där kolumnen .value kommer att ha två olika betydelser.

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å låt oss ta en steg-för-steg titt på de åtgärder som utförs av ovanstående kod.

  • pivot_longer_spec(-family) — skapa en specifikation som komprimerar alla befintliga kolumner utom familjekolumnen.
  • separate(col = name, into = c(".value", "child")) - dela kolumnen . Namn, som innehåller namnen på källfälten, använder understrecket och anger de resulterande värdena i kolumnerna .värde и barn.
  • mutate(child = parse_number(child)) — omvandla fältvärdena barn från text till numerisk datatyp.

Nu kan vi tillämpa den resulterande specifikationen på den ursprungliga dataramen och föra tabellen till önskad 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 använder argument na.rm = TRUE, eftersom den aktuella formen av data tvingar fram skapandet av extra rader för obefintliga observationer. Därför att familj 2 har bara ett barn, na.rm = TRUE garanterar att familj 2 kommer att ha en rad i utgången.

Konvertera datumramar från långt till brett format

pivot_wider() - är den omvända transformationen, och vice versa ökar antalet kolumner i datumramen genom att minska antalet rader.

R-paketet tidyr och dess nya funktioner pivot_longer och pivot_wider

Denna typ av transformation används extremt sällan för att få data till en korrekt form, men den här tekniken kan vara användbar för att skapa pivottabeller som används i presentationer, eller för integration med några andra verktyg.

Egentligen funktionerna pivot_longer() и pivot_wider() är symmetriska och producerar handlingar omvända till varandra, dvs. df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) kommer att returnera originalet df.

Det enklaste exemplet på att konvertera en tabell till ett brett format

För att visa hur funktionen fungerar pivot_wider() vi kommer att använda datamängden fisk_möten, som lagrar information om hur olika stationer registrerar fiskens rörelse längs floden.

#> # 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 flesta fall blir denna tabell mer informativ och enklare att använda om du presenterar information för varje station i en separat kolumn.

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>

Denna datamängd registrerar endast information när fisk har upptäckts av stationen, d.v.s. om någon fisk inte registrerades av någon station, kommer dessa data inte att finnas i tabellen. Detta betyder att utgången kommer att fyllas med NA.

Men i det här fallet vet vi att frånvaron av ett register betyder att fisken inte sågs, så vi kan använda argumentet värden_fyll i funktion pivot_wider() och fyll dessa saknade värden med nollor:

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>

Generera ett kolumnnamn från flera källvariabler

Föreställ dig att vi har en tabell som innehåller en kombination av produkt, land och år. För att generera en testdatumram kan du köra följande kod:

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 uppgift är att utöka dataramen så att en kolumn innehåller data för varje kombination av produkt och land. För att göra detta, skicka bara in argumentet namn_från en vektor som innehåller namnen på de fält som ska slås samman.

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 också tillämpa specifikationer på en funktion pivot_wider(). Men när de lämnas till pivot_wider() specifikationen gör den motsatta konverteringen pivot_longer(): Kolumnerna som anges i . Namn, med hjälp av värden från .värde och andra kolumner.

För denna datauppsättning kan du generera en anpassad specifikation om du vill att alla möjliga länder och produktkombinationer ska ha sin egen kolumn, inte bara de som finns i data:

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

Flera avancerade exempel på att arbeta med det nya tidyr-konceptet

Rensa upp data med hjälp av US Census Income and Rent dataset som exempel.

Datauppsättning us_rent_income innehåller medianinkomst och hyresinformation för varje delstat i USA för 2017 (datauppsättning tillgänglig i paketet 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 form som data lagras i datamängden us_rent_income att arbeta med dem är extremt obekvämt, så vi skulle vilja skapa en datamängd med kolumner: hyra, rent_moe, komma, inkomst_moe. Det finns många sätt att skapa denna specifikation, men huvudpoängen är att vi måste generera varje kombination av variabelvärden och uppskattning/moeoch generera sedan kolumnnamnet.

  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

Tillhandahåller denna specifikation pivot_wider() ger oss resultatet vi letar efter:

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

Världsbanken

Ibland krävs flera steg för att föra en datamängd till önskad form.
Datauppsättning world_bank_pop innehåller Världsbankens uppgifter om befolkningen i varje land mellan 2000 och 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>

Vårt mål är att skapa en snygg datamängd med varje variabel i sin egen kolumn. Det är oklart exakt vilka steg som behövs, men vi börjar med det mest uppenbara problemet: året är spritt över flera kolumner.

För att fixa detta måste du använda funktionen 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

Nästa steg är att titta på indikatorvariabeln.
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

Där SP.POP.GROW är befolkningstillväxt, SP.POP.TOTL är total befolkning och SP.URB. * samma sak, men bara för tätorter. Låt oss dela upp dessa värden i två variabler: area - area (totalt eller urban) och en variabel som innehåller faktiska data (befolkning eller tillväxt):

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

Nu behöver vi bara dela upp variabeln i två kolumner:

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

Список

Ett sista exempel, tänk dig att du har en kontaktlista som du kopierade och klistrade in från en webbplats:

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

Att tabellera denna lista är ganska svårt eftersom det inte finns någon variabel som identifierar vilken data som hör till vilken kontakt. Vi kan fixa detta genom att notera att varje ny kontakts data börjar med "namn", så vi kan skapa en unik identifierare och öka den med en varje gång fältkolumnen innehåller värdet "namn":

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

Nu när vi har ett unikt ID för varje kontakt kan vi göra om fältet och värdet till kolumner:

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>

Slutsats

Min personliga åsikt är att det nya konceptet tidyr verkligen mer intuitivt och betydligt överlägsen i funktionalitet jämfört med äldre funktioner spread() и gather(). Jag hoppas att den här artikeln hjälpte dig att hantera pivot_longer() и pivot_wider().

Källa: will.com

Lägg en kommentar