Pacchetto tidyr è parte del nucleo di una delle librerie più popolari nel linguaggio R — tidyverse.
Lo scopo principale del pacchetto è rendere i dati in forma ordinata.
Su Habr c'è già dedicata a questo pacchetto, ma risale al 2015. Voglio raccontare i cambiamenti più rilevanti di cui ha parlato l'autore Hadley Wickham alcuni giorni fa.

SJK: Le funzioni gather() e spread() saranno considerate obsolete?
Hadley Wickham: In una certa misura. Smetteremo di raccomandare l'uso di queste funzioni e di correggere eventuali errori, ma rimarranno presenti nel pacchetto nello stato attuale.
Contenuto
Se sei interessato all'analisi dei dati, potresti trovare interessanti i miei e canali. Gran parte dei contenuti è dedicata al linguaggio R.
Concetto di TidyData
Obiettivo tidyr — aiutarti a rendere i dati in quella che viene definita forma ordinata. I dati ordinati sono dati in cui:
- Ogni variabile si trova in una colonna.
- Ogni osservazione è una riga.
- Ogni valore è una cella.
Con dati in formato tidy è molto più semplice e più comodo lavorare durante l'analisi.
Funzioni principali incluse nel pacchetto tidyr
tidyr contiene un insieme di funzioni destinate alla trasformazione delle tabelle:
fill()— riempie i valori mancanti in una colonna con i valori precedenti;separate()— divide un campo in più utilizzando un delimitatore;unite()— unisce più campi in uno, l'operazione inversa della funzioneseparate();pivot_longer()— funzione che trasforma i dati da formato largo a lungo;pivot_wider()— funzione che trasforma i dati da formato lungo a largo. Operazione inversa rispetto a quella svolta dalla funzionepivot_longer().gather()obsoleta — funzione che trasforma i dati da formato largo a lungo;spread()obsoleta — funzione che trasforma i dati da formato lungo a largo. Operazione inversa rispetto a quella svolta dalla funzionegather().
Nuovo concetto di trasformazione dei dati da formato largo a lungo e viceversa
In precedenza, per questo tipo di trasformazione venivano utilizzate funzioni gather() e spread(). Nel corso degli anni, è diventato evidente che per la maggior parte degli utenti, incluso l'autore del pacchetto, i nomi di queste funzioni e dei loro argomenti non erano affatto chiari e causavano difficoltà nel cercarli e nel comprendere quale di queste funzioni trasformasse un data frame da formato largo a lungo e viceversa.
A questo proposito, tidyr sono state aggiunte due nuove funzioni importanti, progettate per la trasformazione dei data frame.
Nuove funzionalità pivot_longer() e pivot_wider() sono state create sotto l'influenza di alcune funzioni 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 tidyr 0.8.3.9000, che include le nuove funzioni, usa il seguente codice.
devtools::install_github("tidyverse/tidyr")
Al momento della scrittura di questo articolo, queste funzioni sono disponibili solo nella versione dev del pacchetto su GitHub.
Passaggio a nuove funzioni
In realtà, non è difficile adattare i vecchi script per lavorare con le nuove funzioni; per una maggiore comprensione, prenderò un esempio dalla documentazione delle vecchie funzioni e mostrerò come queste stesse operazioni possono essere effettuate utilizzando le nuove pivot_*() funzioni.
Trasformazione da formato largo a lungo.
Esempio di codice dalla documentazione della funzione gather
# 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")
Trasformazione da formato lungo a largo.
Esempio di codice dalla documentazione della funzione spread
# old
stocks_spread <- stocks_gather %>% spread(key = stock,
value = price)
# new
stock_wide <- stocks_long %>% pivot_wider(names_from = "stock",
values_from = "price")
Poiché negli esempi forniti sopra, nella tabella originale pivot_longer() e pivot_wider()non ci sono colonne elencate negli argomenti stocks names_to values_to e è necessario specificarne i nomi tra virgolette. Una tabella con cui sarà più facile capire come passare alla nuova concezione
Tutto il testo che segue è una traduzione adattiva, direi addirittura libera, delle tidyr.

Nota dell'autore
vignette pivot_longer ()
Un semplice esempio di trasformazione dei dati da formato largo a lungo
rende i set di dati più lunghi, riducendo il numero di colonne e aumentando il numero di righe. Per eseguire gli esempi presentati nell'articolo, è inizialmente necessario caricare i pacchetti necessari:

library(tidyr) library(dplyr) library(readr)
Supponiamo di avere una tabella con i risultati di un sondaggio, in cui (tra l'altro) si chiedeva alle persone della loro religione e del loro reddito annuale:Supponiamo di avere una tabella con i risultati di un sondaggio, in cui (tra l'altro) si chiedeva alle persone della loro religione e del reddito annuale:
#> # 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 dati sulla religione dei rispondenti nelle righe, mentre il livello di reddito è distribuito nei nomi delle colonne. Il numero di rispondenti di ciascuna categoria è conservato nei valori delle celle all'incrocio tra religione e livello di reddito. Per portare la tabella a un formato ordinato 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")
#> # Un tibble: 180 x 3
#> religion income count
#>
#> 1 Agnostico 2 Agnostico $10-20k 34
#> 3 Agnostico $20-30k 60
#> 4 Agnostico $30-40k 81
#> 5 Agnostico $40-50k 76
#> 6 Agnostico $50-75k 137
#> 7 Agnostico $75-100k 122
#> 8 Agnostico $100-150k 109
#> 9 Agnostico >150k 84
#> 10 Agnostico Non so/rifiutato 96
#> # … con 170 righe aggiuntiveArgomenti della funzione pivot_longer()
- Primo argomento cols, descrive quali colonne devono essere unite. In questo caso, tutte le colonne tranne time.
- Argomento values_to fornisce il nome della variabile che sarà creata dai nomi delle colonne che abbiamo unito.
- è necessario specificarne i nomi tra virgolette. fornisce il nome della variabile che sarà creata dai dati contenuti nei valori delle celle delle colonne unite.
Specifiche
Questa è una nuova funzionalità del pacchetto tidyr, che prima con le funzioni deprecate non era disponibile.
La specifica è un data frame, ogni riga del quale corrisponde a una colonna nel nuovo data frame di uscita, e due colonne speciali che iniziano con:
- .name contiene il nome originale della colonna.
- .value contiene il nome della colonna in cui entreranno i valori delle celle.
Le altre colonne della specifica riflettono come nel nuovo colonna verrà visualizzato il nome delle colonne compresse da .name.
La specifica descrive i metadati contenuti nel nome della colonna, con una riga per ogni colonna e una colonna per ogni variabile, unita al nome della colonna; probabilmente ora tale definizione sembra confusa, ma dopo aver considerato alcuni esempi tutto diventerà molto più chiaro.
Il significato della specifica è che è possibile estrarre, modificare e impostare nuovi metadati sul data frame trasformato.
Per lavorare con le specifiche durante la trasformazione della tabella da un formato largo a uno lungo, viene utilizzata la funzione pivot_longer_spec().
Come funziona questa funzione, essa prende qualsiasi data frame e ne forma i metadati nel modo sopra descritto.
Per esempio, prendiamo il set di dati who, fornito insieme al pacchetto. tidyrQuesto set di dati contiene informazioni fornite dall'Organizzazione Mondiale della Sanità sulla diffusione della tubercolosi.
who
# > # A tibble: 7,240 x 60
# > country iso2 iso3 year new_sp_m014 new_sp_m1524 new_sp_m2534
# > <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
# > # … con 7,230 righe in più, e 53 variabili in piùCreeremo 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 rowsCampi country, iso2, iso3 già sono variabili. Il nostro compito è di ruotare le colonne con new_sp_m014 da newrel_f65.
Nei nomi di queste colonne è memorizzata la seguente informazione:
- Prefisso
new_indica che la colonna contiene dati sui nuovi casi di tubercolosi, il dataframe corrente contiene informazioni solo sui nuovi casi, pertanto questo prefisso in questo contesto non ha alcun significato. sp/rel/sp/epdescrive il metodo di diagnosi della malattia.m/fsesso del paziente.014/1524/2535/3544/4554/65fascia d'età del paziente.
Possiamo separare queste colonne utilizzando la funzione extract(), utilizzando un'espressione regolare.
spec <- spec %>%
extract(name, c("diagnosi", "genere", "età"), "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 rowsSi prega di notare che la colonna .name deve rimanere invariata, poiché è il nostro indice nei nomi delle colonne del set di dati originale.
Il genere e l'età (colonne gender e age) hanno valori fissi e noti, quindi è consigliabile trasformare 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 la specifica che abbiamo creato al dataframe originale who dobbiamo utilizzare l'argomento spec nella 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 rowsTutto quello che abbiamo appena fatto può essere schematicamente rappresentato come segue:

Specifiche con più valori (.value)
Nell'esempio sopra, la colonna della specifica .value contiene solo un valore, e nella maggior parte dei casi è così.
Tuttavia, può occasionalmente verificarsi una situazione in cui è necessario raccogliere nei valori dati da colonne con diversi tipi di dati. Utilizzando una funzione obsoleta, spread() sarebbe piuttosto difficile farlo.
L'esempio riportato di seguito è tratto da al pacchetto data.table.
Creiamo un dataframe di esempio.
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 % 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 1Il dataframe creato contiene in ogni riga i dati sui bambini di una famiglia. Le famiglie possono avere uno o due bambini. Per ciascun bambino vengono forniti dati sulla data di nascita e sul genere, con i dati di ogni bambino che si trovano in colonne separate. Il nostro compito è quello di portare questi dati in un formato corretto per l'analisi.
Si noti che abbiamo due variabili con informazioni su ogni bambino: il suo genere e la data di nascita (le colonne con il prefisso dob contengono la data di nascita, le colonne con il prefisso gender contengono il genere del bambino). Nel risultato atteso, dovrebbero trovarsi in colonne separate. Possiamo farlo generando una specifica in cui la colonna .value avrà due valori diversi.
spec %
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 2Quindi, analizziamo passo dopo passo le azioni eseguite dal codice sopra riportato.
pivot_longer_spec(-family)— creiamo una specifica che comprime tutte le colonne esistenti, tranne la colonna family.separate(col = name, into = c(".value", "child"))— separiamo la colonna .name, che contiene i nomi dei campi originali, utilizzando il trattino basso e assegnamo i valori ottenuti alle colonne .value e child.mutate(child = parse_number(child))— convertiamo i valori del campo child da formato testuale a formato numerico.
Ora possiamo applicare la specifica ottenuta al dataframe originale e portare la tabella nel formato desiderato.
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 1Utilizziamo l'argomento na.rm = TRUE, perché l'attuale forma dei dati costringe a creare righe extra per osservazioni non esistenti. Poiché nella famiglia 2 è presente solo un bambino, na.rm = TRUE si garantisce che la famiglia 2 abbia una sola riga negli output.
Trasformazione di data frame da formato lungo a largo
pivot_wider() è una trasformazione inversa e, viceversa, aumenta il numero di colonne del data frame riducendo il numero di righe.

Questo tipo di trasformazione è estremamente raro e utilizzato per rendere i dati più ordinati; tuttavia, questa tecnica può risultare utile per creare tabelle sintetiche da utilizzare nelle presentazioni o per integrare con altri strumenti.
In realtà, le funzioni pivot_longer() e pivot_wider() sono simmetriche e compiono azioni inverse l'una dell'altra, cioè: df %>% pivot_longer(spec = spec) %>% pivot_wider(spec = spec) e df %>% pivot_wider(spec = spec) %>% pivot_longer(spec = spec) restituirà il df originale.
Esempio più semplice di come rendere una tabella in formato largo
Per dimostrare il funzionamento della funzione pivot_wider() utilizzeremo il set di dati fish_encounters, che contiene informazioni su come diverse stazioni registrano il movimento dei pesci nel 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 rowsNella maggior parte dei casi, questa tabella sarà più informativa e comoda da usare se presentiamo le informazioni per ogni stazione in colonne separate.
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
#> <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
#> # … con 9 righe in più e 1 variabile aggiuntiva: MAW <int>Questo set di dati registra informazioni solo nei casi in cui il pesce è stato rilevato dalla stazione, cioè se un pesce non è stato registrato da una qualsiasi stazione, quei dati non saranno presenti nella tabella. Ciò significa che i risultati saranno valorizzati con NA.
Tuttavia, in questo caso sappiamo che l'assenza di registrazione significa che il pesce non è stato notato, quindi possiamo utilizzare l'argomento values_fill nella funzione pivot_wider() e riempire 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 del nome di una colonna da più variabili di origine
Immaginate di avere una tabella contenente una combinazione di prodotto, paese e anno. Per generare un data frame di test, è possibile eseguire il seguente codice:
df %
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 rowsIl nostro obiettivo è espandere il data frame in modo che una colonna contenga i dati per ogni combinazione di prodotto e paese. Per questo basta passare nell'argomento names_from un vettore che contiene 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 rowsPuoi anche applicare specifiche alla funzione pivot_wider(). Ma quando viene fornito a pivot_wider() la specifica esegue una trasformazione opposta pivot_longer(): vengono creati le colonne indicate in .name, utilizzando i valori di .value e di 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, e non solo quelle presenti nei dati:
spec %
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 valuedf %>% 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.594Alcuni esempi avanzati di utilizzo del nuovo concetto di tidyr
Rendere i dati in forma ordinata utilizzando un set di dati sul censimento dei redditi e degli affitti negli Stati Uniti
Set di dati us_rent_income contiene informazioni sul reddito medio e sull'affitto per ogni stato degli Stati Uniti nel 2017 (il set di dati è disponibile nel pacchetto tidycensus).
us_rent_income
#> # A tibble: 104 x 5
#> GEOID NAME variable estimate moe
#>
#> 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
#> # … con 94 righe in piùNella forma in cui i dati sono memorizzati nel dataset us_rent_income lavorarci è estremamente scomodo, quindi vorremmo creare un set di dati con le colonne: rent, rent_moe, come, income_moe. Ci sono molti modi per creare questa specifica, ma l'importante è che dobbiamo generare ogni combinazione di valori della variabile e estimate/moe, e poi generare il nome della colonna.
spec %
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_moeFornire questa specifica pivot_wider() ci dà il risultato che cerchiamo:
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 rowsBanca Mondiale
A volte, la preparazione di un insieme di dati nella forma desiderata richiede diversi passaggi.
Dataset world_bank_pop contiene i dati della Banca Mondiale sulla popolazione di ogni paese nel periodo dal 2000 al 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 insieme di dati ordinato, in cui ogni variabile si trova in una colonna separata. Non è ancora chiaro quali passaggi siano necessari, ma inizieremo con il problema più ovvio: l'anno è suddiviso in più colonne.
Per risolvere questo problema è necessario utilizzare la funzione pivot_longer().
pop2 %
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 rowsIl passo successivo è considerare la variabile indicator.
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 4752Dove SP.POP.GROW è la crescita della popolazione, SP.POP.TOTL è la popolazione totale, e SP.URB. * è lo stesso, ma solo per le aree urbane. Separiamo questi valori in due variabili: area — area (total o urban) e una variabile contenente i dati effettivi (population o growth):
pop3 %
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 rowsOra dobbiamo solo separare la variabile variable 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 rowsElenco dei contatti
Ultimo esempio, immaginate di avere un elenco di contatti che avete copiato e incollato da un sito web:
contacts <- tribble(
~field, ~value,
"name", "Jiena McLellan",
"company", "Toyota",
"name", "John Smith",
"company", "google",
"email", "john@google.com",
"name", "Huxley Ratcliffe"
)Portare questo elenco in forma tabellare è abbastanza difficile, perché non esiste una variabile che identifichi a quali dati appartiene ciascun contatto. Possiamo risolvere questo problema notando che i dati per ogni nuovo contatto iniziano con il nome ("name"), quindi possiamo creare un identificatore unico, incrementandolo di uno ogni volta che nella colonna field appare il valore “name”:
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 john@google.com 2
#> 6 name Huxley Ratcliffe 3Ora che abbiamo un identificatore unico per ogni 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 john@google.com
#> 3 3 Huxley Ratcliffe <NA> <NA>Conclusione
Personalmente, penso che il nuovo concetto tidyr sia davvero intuitivamente più chiaro e superi di gran lunga le funzionalità delle funzioni obsolete. spread() e gather(). Spero che questo articolo ti abbia aiutato a capire pivot_longer() e pivot_wider().
Fonte: habr.com
