Pacchetto R tidyr e le sue nuove funzioni pivot_longer e pivot_wider

Pacchetto ordinato incluso nel nucleo di una delle librerie più popolari nel linguaggio R - tidyverse.
Lo scopo principale del pacchetto è riportare i dati in una forma accurata.

Già disponibile su Habré pubblicazione dedicato a questo pacchetto, ma risale al 2015. E voglio parlarvi delle novità più attuali, annunciate pochi giorni fa dal suo autore, Hedley Wickham.

Pacchetto R tidyr e le sue nuove funzioni pivot_longer e pivot_wider

SJK: Gather() e Spread() saranno deprecati?

Hadley Wickham: In una certa misura. Non consiglieremo più l'uso di queste funzioni né correggeremo i bug in esse contenuti, ma continueranno a essere presenti nel pacchetto nel loro stato attuale.

contenuto

Se sei interessato all'analisi dei dati, potresti essere interessato al mio Telegram и youtube canali. La maggior parte del contenuto è dedicata al linguaggio R.

Il concetto di TidyData

bersaglio ordinato - aiutarti a portare i dati in una cosiddetta forma ordinata. I dati ordinati sono dati in cui:

  • Ogni variabile è in una colonna.
  • Ogni osservazione è una stringa.
  • Ogni valore è una cella.

È molto più semplice e conveniente lavorare con dati presentati in modo ordinato durante l'esecuzione dell'analisi.

Principali funzioni incluse nel pacchetto tidyr

tidyr contiene una serie di funzioni progettate per trasformare le tabelle:

  • fill() — riempimento dei valori mancanti in una colonna con valori precedenti;
  • separate() — divide un campo in più campi utilizzando un separatore;
  • unite() — esegue l'operazione di combinazione di più campi in uno solo, l'azione inversa della funzione separate();
  • pivot_longer() — una funzione che converte i dati dal formato grande al formato lungo;
  • pivot_wider() - una funzione che converte i dati dal formato lungo al formato largo. L'operazione inversa di quella eseguita dalla funzione pivot_longer().
  • gather()obsoleto — una funzione che converte i dati dal formato grande al formato lungo;
  • spread()obsoleto - una funzione che converte i dati dal formato lungo al formato largo. L'operazione inversa di quella eseguita dalla funzione gather().

Nuovo concetto per la conversione dei dati dal formato largo a quello lungo e viceversa

In precedenza, per questo tipo di trasformazione venivano utilizzate le funzioni gather() и spread(). Nel corso degli anni di esistenza di queste funzioni, è diventato ovvio che per la maggior parte degli utenti, incluso l'autore del pacchetto, i nomi di queste funzioni e i loro argomenti non erano del tutto ovvi e causavano difficoltà nel trovarli e nel capire quale di queste funzioni converte una cornice di data dal formato largo a quello lungo e viceversa.

A questo proposito, nell ordinato Sono state aggiunte due nuove importanti funzionalità pensate per trasformare i datari.

Nuove funzionalità pivot_longer() и pivot_wider() sono stati ispirati da alcune delle funzionalità del pacchetto cdata, creato da John Mount e Nina Zumel.

Installazione della versione più recente di tidyr 0.8.3.9000

Per installare la nuova versione più recente del pacchetto ordinato 0.8.3.9000, dove sono disponibili nuove funzionalità, utilizzare il codice seguente.

devtools::install_github("tidyverse/tidyr")

Al momento in cui scrivo, queste funzioni sono disponibili solo nella versione dev del pacchetto su GitHub.

Transizione a nuove funzionalità

Non è infatti difficile trasferire vecchi script per lavorare con nuove funzioni; per una migliore comprensione prenderò un esempio dalla documentazione delle vecchie funzioni e mostrerò come le stesse operazioni vengono eseguite utilizzando quelle nuove pivot_*() funzioni.

Converti il ​​formato largo in formato lungo.

Codice di esempio dalla documentazione della funzione di raccolta

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

Conversione del formato lungo in formato grande.

Codice di esempio dalla documentazione della funzione di diffusione

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

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

Perché negli esempi precedenti di lavoro con pivot_longer() и pivot_wider(), nella tabella originale riserve nessuna colonna elencata negli argomenti nomi_a и valori_a i loro nomi devono essere tra virgolette.

Una tabella che ti aiuterà a capire più facilmente come passare a lavorare con un nuovo concetto ordinato.

Pacchetto R tidyr e le sue nuove funzioni pivot_longer e pivot_wider

Nota dell'autore

Tutto il testo seguente è adattivo, direi anche traduzione libera vignette dal sito web ufficiale della biblioteca tidyverse.

Un semplice esempio di conversione dei dati dal formato largo a quello lungo

pivot_longer () — allunga i set di dati riducendo il numero di colonne e aumentando il numero di righe.

Pacchetto R tidyr e le sue nuove funzioni pivot_longer e pivot_wider

Per eseguire gli esempi presentati nell'articolo, devi prima connettere i pacchetti necessari:

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

Diciamo di avere una tabella con i risultati di un sondaggio che (tra le altre cose) chiedeva alle persone la loro religione e il loro reddito annuo:

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

Questa tabella contiene i dati sulla religione degli intervistati in righe e i livelli di reddito sono sparsi nei nomi delle colonne. Il numero di intervistati di ciascuna categoria è memorizzato nei valori della cella all'intersezione tra religione e livello di reddito. Per portare la tabella in un formato pulito e corretto, è sufficiente utilizzare 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

Argomenti di funzione pivot_longer()

  • Primo argomento cols, descrive quali colonne devono essere unite. In questo caso, tutte le colonne tranne tempo.
  • argomento nomi_a dà il nome della variabile che verrà creata dai nomi delle colonne che abbiamo concatenato.
  • valori_a dà il nome di una variabile che verrà creata dai dati memorizzati nei valori delle celle delle colonne unite.

Specificazioni

Questa è una nuova funzionalità del pacchetto ordinato, che in precedenza non era disponibile quando si lavorava con funzioni legacy.

Una specifica è un frame di dati, ciascuna riga del quale corrisponde a una colonna nel nuovo frame di data di output e a due colonne speciali che iniziano con:

  • . Nome contiene il nome della colonna originale.
  • .valore contiene il nome della colonna che conterrà i valori della cella.

Le restanti colonne della specifica riflettono il modo in cui la nuova colonna visualizzerà il nome delle colonne compresse da cui . Nome.

La specifica descrive i metadati memorizzati nel nome di una colonna, con una riga per ogni colonna e una colonna per ogni variabile, combinata con il nome della colonna. Questa definizione può sembrare confusa al momento, ma dopo aver esaminato alcuni esempi diventerà molto più chiaro.

Il punto della specifica è che è possibile recuperare, modificare e definire nuovi metadati per il dataframe da convertire.

Per lavorare con le specifiche durante la conversione di una tabella da un formato ampio a un formato lungo, utilizzare la funzione pivot_longer_spec().

Il funzionamento di questa funzione è che prende qualsiasi frame di data e genera i relativi metadati nel modo descritto sopra.

Ad esempio, prendiamo il set di dati who fornito con il pacchetto ordinato. Questo set di dati contiene informazioni fornite dall'Organizzazione sanitaria internazionale sull'incidenza della tubercolosi.

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

Costruiamo la sua specifica.

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

Campo nazione, iso2, iso3 sono già variabili. Il nostro compito è capovolgere le colonne con new_sp_m014 su newrel_f65.

I nomi di queste colonne memorizzano le seguenti informazioni:

  • prefisso new_ indica che la colonna contiene dati su nuovi casi di tubercolosi, il datario attuale contiene informazioni solo su nuove malattie, quindi questo prefisso nel contesto attuale non ha alcun significato.
  • sp/rel/sp/ep descrive un metodo per diagnosticare una malattia.
  • m/f sesso del paziente.
  • 014/1524/2535/3544/4554/65 fascia di età del paziente.

Possiamo dividere queste colonne usando la funzione extract()utilizzando l'espressione regolare.

spec <- spec %>%
        extract(name, c("diagnosis", "gender", "age"), "new_?(.*)_(.)(.*)")

#> # A tibble: 56 x 5
#>    .name        .value diagnosis gender age  
#>    <chr>        <chr>  <chr>     <chr>  <chr>
#>  1 new_sp_m014  count  sp        m      014  
#>  2 new_sp_m1524 count  sp        m      1524 
#>  3 new_sp_m2534 count  sp        m      2534 
#>  4 new_sp_m3544 count  sp        m      3544 
#>  5 new_sp_m4554 count  sp        m      4554 
#>  6 new_sp_m5564 count  sp        m      5564 
#>  7 new_sp_m65   count  sp        m      65   
#>  8 new_sp_f014  count  sp        f      014  
#>  9 new_sp_f1524 count  sp        f      1524 
#> 10 new_sp_f2534 count  sp        f      2534 
#> # … with 46 more rows

Si prega di notare la colonna . Nome dovrebbe rimanere invariato poiché questo è il nostro indice nei nomi delle colonne del set di dati originale.

Sesso ed età (colonne genere и ) hanno valori fissi e noti, quindi si consiglia di convertire queste colonne in fattori:

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

Infine, per applicare le specifiche che abbiamo creato al frame della data originale che dobbiamo usare un argomento spec in funzione 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

Tutto ciò che abbiamo appena fatto può essere schematicamente rappresentato come segue:

Pacchetto R tidyr e le sue nuove funzioni pivot_longer e pivot_wider

Specifica utilizzando più valori (.value)

Nell'esempio sopra, la colonna delle specifiche .valore conteneva un solo valore, nella maggior parte dei casi è così.

Ma occasionalmente può verificarsi una situazione in cui è necessario raccogliere dati da colonne con diversi tipi di dati nei valori. Utilizzando una funzione legacy spread() questo sarebbe abbastanza difficile da fare.

L'esempio seguente è tratto da vignette al pacchetto tabella dati.

Creiamo un dataframe di addestramento.

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

Il riquadro della data creato contiene dati sui figli di una famiglia in ciascuna riga. Le famiglie possono avere uno o due figli. Per ogni bambino, vengono forniti i dati sulla data di nascita e sul sesso, e i dati per ogni bambino sono in colonne separate; il nostro compito è portare questi dati nel formato corretto per l'analisi.

Tieni presente che abbiamo due variabili con informazioni su ciascun bambino: il sesso e la data di nascita (colonne con il prefisso battesimo contenere data di nascita, colonne con prefisso genere contenere il sesso del bambino). Il risultato atteso è che dovrebbero apparire in colonne separate. Possiamo farlo generando una specifica in cui la colonna .value avrà due significati diversi.

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

Quindi, diamo uno sguardo passo passo alle azioni eseguite dal codice sopra.

  • pivot_longer_spec(-family) — crea una specifica che comprime tutte le colonne esistenti tranne la colonna della famiglia.
  • separate(col = name, into = c(".value", "child")) - dividere la colonna . Nome, che contiene i nomi dei campi sorgente, utilizzando il carattere di sottolineatura e inserendo nelle colonne i valori risultanti .valore и bambino.
  • mutate(child = parse_number(child)) — trasforma i valori del campo bambino dal testo al tipo di dati numerico.

Ora possiamo applicare la specifica risultante al dataframe originale e portare la tabella nella forma desiderata.

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

Usiamo l'argomentazione na.rm = TRUE, perché la forma attuale dei dati impone la creazione di righe aggiuntive per osservazioni inesistenti. Perché la famiglia 2 ha un solo figlio, na.rm = TRUE garantisce che la famiglia 2 avrà una riga nell'output.

Conversione di cornici di data dal formato lungo a quello largo

pivot_wider() - è la trasformazione inversa, e viceversa aumenta il numero di colonne del riquadro della data riducendo il numero di righe.

Pacchetto R tidyr e le sue nuove funzioni pivot_longer e pivot_wider

Questo tipo di trasformazione viene utilizzato estremamente raramente per portare i dati in una forma accurata, tuttavia, questa tecnica può essere utile per creare tabelle pivot utilizzate nelle presentazioni o per l'integrazione con altri strumenti.

In realtà le funzioni pivot_longer() и pivot_wider() sono simmetrici e producono azioni tra loro inverse, ovvero: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) и df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) restituirà il df originale.

L'esempio più semplice di conversione di una tabella in un formato ampio

Per dimostrare come funziona la funzione pivot_wider() utilizzeremo il set di dati incontri_di_pesce, che memorizza informazioni su come le diverse stazioni registrano il movimento dei pesci lungo il fiume.

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

Nella maggior parte dei casi, questa tabella sarà più informativa e più facile da usare se presenterai le informazioni per ciascuna stazione in una colonna separata.

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>

Questo set di dati registra solo informazioni quando i pesci sono stati rilevati dalla stazione, ad es. se qualche pesce non è stato registrato da qualche stazione, questi dati non saranno presenti nella tabella. Ciò significa che l'output verrà riempito con NA.

Tuttavia, in questo caso sappiamo che l'assenza di una registrazione significa che il pesce non è stato visto, quindi possiamo utilizzare l'argomento valori_riempimento in funzione pivot_wider() e riempi questi valori mancanti con zeri:

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>

Generazione di un nome di colonna da più variabili di origine

Immaginiamo di avere una tabella contenente una combinazione di prodotto, paese e anno. Per generare un periodo di data di prova, è possibile eseguire il seguente codice:

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

Il nostro compito è espandere il frame dei dati in modo che una colonna contenga dati per ciascuna combinazione di prodotto e paese. Per fare ciò, basta passare l'argomento nomi_da un vettore contenente i nomi dei campi da unire.

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

È inoltre possibile applicare specifiche a una funzione pivot_wider(). Ma quando sottoposto a pivot_wider() la specifica esegue la conversione opposta pivot_longer(): le colonne specificate in . Nome, utilizzando valori da .valore e altre colonne.

Per questo set di dati, puoi generare una specifica personalizzata se desideri che ogni possibile combinazione di paese e prodotto abbia la propria colonna, non solo quelle presenti nei dati:

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

Diversi esempi avanzati di lavoro con il nuovo concetto di tidyr

Ripulitura dei dati utilizzando come esempio il set di dati sui redditi e sugli affitti del censimento degli Stati Uniti.

Set di dati noi_rendita_reddito contiene informazioni sul reddito medio e sull'affitto per ogni stato degli Stati Uniti per il 2017 (set di dati disponibile nel pacchetto ordinato).

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

Nella forma in cui i dati sono archiviati nel set di dati noi_rendita_reddito lavorare con loro è estremamente scomodo, quindi vorremmo creare un set di dati con colonne: affitta, rent_moe, Venire, reddito_moe. Esistono molti modi per creare questa specifica, ma il punto principale è che dobbiamo generare ogni combinazione di valori variabili e stima/moee quindi generare il nome della colonna.

  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

Fornire questa specifica pivot_wider() ci dà il risultato che stiamo cercando:

us_rent_income %>% pivot_wider(spec = spec)

#> # A tibble: 52 x 6
#>    GEOID NAME                 income income_moe  rent rent_moe
#>    <chr> <chr>                 <dbl>      <dbl> <dbl>    <dbl>
#>  1 01    Alabama               24476        136   747        3
#>  2 02    Alaska                32940        508  1200       13
#>  3 04    Arizona               27517        148   972        4
#>  4 05    Arkansas              23789        165   709        5
#>  5 06    California            29454        109  1358        3
#>  6 08    Colorado              32401        109  1125        5
#>  7 09    Connecticut           35326        195  1123        5
#>  8 10    Delaware              31560        247  1076       10
#>  9 11    District of Columbia  43198        681  1424       17
#> 10 12    Florida               25952         70  1077        3
#> # … with 42 more rows

La Banca mondiale

A volte portare un set di dati nella forma desiderata richiede diversi passaggi.
Set di dati banca_mondiale_pop contiene i dati della Banca Mondiale sulla popolazione di ciascun paese tra il 2000 e il 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>

Il nostro obiettivo è creare un set di dati accurato con ciascuna variabile nella propria colonna. Non è chiaro esattamente quali passaggi siano necessari, ma inizieremo con il problema più ovvio: l'anno è distribuito su più colonne.

Per risolvere questo problema è necessario utilizzare la funzione 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

Il passo successivo è guardare la variabile indicatore.
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

Dove SP.POP.GROW è la crescita della popolazione, SP.POP.TOTL è la popolazione totale e SP.URB. *la stessa cosa, ma solo per le aree urbane. Dividiamo questi valori in due variabili: area - area (totale o urbana) e una variabile contenente i dati effettivi (popolazione o crescita):

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

Ora non dobbiamo fare altro che dividere la variabile in due colonne:

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

Lista dei contatti

Un ultimo esempio, immagina di avere un elenco di contatti che hai copiato e incollato da un sito web:

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

Classificare questo elenco è piuttosto difficile perché non esiste una variabile che identifichi quali dati appartengono a quale contatto. Possiamo risolvere questo problema notando che i dati di ogni nuovo contatto iniziano con "nome", quindi possiamo creare un identificatore univoco e incrementarlo di uno ogni volta che la colonna del campo contiene il valore "nome":

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

Ora che abbiamo un ID univoco per ciascun contatto, possiamo trasformare il campo e il valore in colonne:

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>

conclusione

La mia opinione personale è che il nuovo concetto ordinato veramente più intuitivo e significativamente superiore in termini di funzionalità rispetto alle funzioni legacy spread() и gather(). Spero che questo articolo ti abbia aiutato ad affrontare pivot_longer() и pivot_wider().

Fonte: habr.com

Aggiungi un commento