tidyr - Riordinare Dati in R
tidyr e’ il pacchetto del tidyverse dedicato alla riorganizzazione e pulizia dei dati. Il suo obiettivo principale e’ trasformare i dataset in formato “tidy”, dove ogni variabile ha la propria colonna, ogni osservazione ha la propria riga e ogni valore occupa una singola cella. Questa struttura e’ fondamentale per lavorare efficacemente con gli altri pacchetti del tidyverse come dplyr e ggplot2.
Installazione e Caricamento
# Installazione del pacchetto
install.packages("tidyr")
# Caricamento
library(tidyr)
# Oppure caricare l'intero tidyverse
library(tidyverse)
Il Concetto di Tidy Data
I dati “tidy” (ordinati) seguono tre regole fondamentali definite da Hadley Wickham:
- Ogni variabile occupa una colonna
- Ogni osservazione occupa una riga
- Ogni valore occupa una singola cella
Spesso i dataset reali non rispettano queste regole. I dati possono essere in formato “largo” (wide), dove i valori di una variabile sono distribuiti su piu’ colonne, oppure in formato “lungo” (long), dove piu’ variabili sono compresse in una sola colonna.
# Esempio di dati NON tidy (formato largo)
dati_larghi <- data.frame(
studente = c("Anna", "Marco", "Lucia"),
matematica = c(28, 25, 30),
fisica = c(26, 27, 29),
chimica = c(30, 24, 28)
)
print(dati_larghi)
# studente matematica fisica chimica
# 1 Anna 28 26 30
# 2 Marco 25 27 24
# 3 Lucia 30 29 28
# Qui i nomi delle materie sono sparsi su piu' colonne
# La variabile "materia" e il valore "voto" non hanno colonne proprie
pivot_longer() - Da Formato Largo a Lungo
pivot_longer() (che sostituisce la vecchia funzione gather()) trasforma le colonne in righe, allungando il dataset. Si usa quando i nomi delle colonne sono in realta’ valori di una variabile.
library(tidyr)
library(dplyr)
# Dati in formato largo
dati_larghi <- data.frame(
studente = c("Anna", "Marco", "Lucia"),
matematica = c(28, 25, 30),
fisica = c(26, 27, 29),
chimica = c(30, 24, 28)
)
# Trasformare in formato lungo
dati_lunghi <- dati_larghi %>%
pivot_longer(
cols = c(matematica, fisica, chimica), # Colonne da trasformare
names_to = "materia", # Nome della nuova colonna per i nomi
values_to = "voto" # Nome della nuova colonna per i valori
)
print(dati_lunghi)
# # A tibble: 9 x 3
# studente materia voto
# <chr> <chr> <dbl>
# 1 Anna matematica 28
# 2 Anna fisica 26
# 3 Anna chimica 30
# 4 Marco matematica 25
# 5 Marco fisica 27
# 6 Marco chimica 24
# 7 Lucia matematica 30
# 8 Lucia fisica 29
# 9 Lucia chimica 28
# Selezionare le colonne da trasformare con -
dati_larghi %>%
pivot_longer(
cols = -studente, # Tutte le colonne tranne "studente"
names_to = "materia",
values_to = "voto"
)
Esempio con Dati Temporali
# Fatturato trimestrale
fatturato <- data.frame(
azienda = c("Alpha", "Beta", "Gamma"),
Q1_2025 = c(150, 200, 180),
Q2_2025 = c(170, 210, 195),
Q3_2025 = c(160, 230, 200),
Q4_2025 = c(190, 250, 220)
)
fatturato_lungo <- fatturato %>%
pivot_longer(
cols = starts_with("Q"),
names_to = "trimestre",
values_to = "ricavi"
)
print(fatturato_lungo)
# # A tibble: 12 x 3
# azienda trimestre ricavi
# <chr> <chr> <dbl>
# 1 Alpha Q1_2025 150
# 2 Alpha Q2_2025 170
# 3 Alpha Q3_2025 160
# 4 Alpha Q4_2025 190
# 5 Beta Q1_2025 200
# ...
pivot_wider() - Da Formato Lungo a Largo
pivot_wider() (che sostituisce la vecchia funzione spread()) e’ l’operazione inversa: trasforma le righe in colonne, allargando il dataset.
# Partendo dai dati lunghi, torniamo al formato largo
dati_larghi_2 <- dati_lunghi %>%
pivot_wider(
names_from = materia, # Colonna i cui valori diventeranno nomi di colonne
values_from = voto # Colonna da cui prendere i valori
)
print(dati_larghi_2)
# # A tibble: 3 x 4
# studente matematica fisica chimica
# <chr> <dbl> <dbl> <dbl>
# 1 Anna 28 26 30
# 2 Marco 25 27 24
# 3 Lucia 30 29 28
# Esempio pratico: tabella di riepilogo
vendite <- data.frame(
mese = c("Gen", "Gen", "Feb", "Feb", "Mar", "Mar"),
prodotto = c("A", "B", "A", "B", "A", "B"),
quantita = c(100, 150, 120, 130, 140, 160)
)
vendite %>%
pivot_wider(
names_from = prodotto,
values_from = quantita
)
# # A tibble: 3 x 3
# mese A B
# <chr> <dbl> <dbl>
# 1 Gen 100 150
# 2 Feb 120 130
# 3 Mar 140 160
separate() - Separare una Colonna
separate() divide una singola colonna in piu’ colonne usando un separatore.
# Dati con colonna da separare
dipendenti <- data.frame(
id = 1:4,
nome_completo = c("Mario Rossi", "Anna Bianchi", "Luca Verdi", "Sara Neri"),
data_assunzione = c("2020-03-15", "2019-07-22", "2021-01-10", "2018-11-05")
)
# Separare nome e cognome
dipendenti %>%
separate(nome_completo, into = c("nome", "cognome"), sep = " ")
# id nome cognome data_assunzione
# 1 1 Mario Rossi 2020-03-15
# 2 2 Anna Bianchi 2019-07-22
# 3 3 Luca Verdi 2021-01-10
# 4 4 Sara Neri 2018-11-05
# Separare la data in anno, mese, giorno
dipendenti %>%
separate(data_assunzione, into = c("anno", "mese", "giorno"), sep = "-")
# id nome_completo anno mese giorno
# 1 1 Mario Rossi 2020 03 15
# 2 2 Anna Bianchi 2019 07 22
# 3 3 Luca Verdi 2021 01 10
# 4 4 Sara Neri 2018 11 05
# Mantenere la colonna originale con remove = FALSE
dipendenti %>%
separate(data_assunzione, into = c("anno", "mese", "giorno"),
sep = "-", remove = FALSE)
unite() - Unire Colonne
unite() e’ l’operazione inversa di separate(): combina piu’ colonne in una sola.
# Dati con colonne da unire
indirizzi <- data.frame(
nome = c("Mario", "Anna", "Luca"),
citta = c("Roma", "Milano", "Napoli"),
provincia = c("RM", "MI", "NA"),
cap = c("00100", "20100", "80100")
)
# Unire citta', provincia e CAP
indirizzi %>%
unite("indirizzo_completo", citta, provincia, cap, sep = " - ")
# nome indirizzo_completo
# 1 Mario Roma - RM - 00100
# 2 Anna Milano - MI - 20100
# 3 Luca Napoli - NA - 80100
# Unire mantenendo le colonne originali
indirizzi %>%
unite("citta_prov", citta, provincia, sep = " (", remove = FALSE) %>%
mutate(citta_prov = paste0(citta_prov, ")"))
drop_na() - Rimuovere Valori Mancanti
drop_na() rimuove le righe che contengono valori NA.
# Dati con valori mancanti
esami <- data.frame(
studente = c("Anna", "Marco", "Lucia", "Paolo", "Sara"),
voto_scritto = c(25, NA, 28, 30, NA),
voto_orale = c(27, 26, NA, 29, 24)
)
print(esami)
# studente voto_scritto voto_orale
# 1 Anna 25 27
# 2 Marco NA 26
# 3 Lucia 28 NA
# 4 Paolo 30 29
# 5 Sara NA 24
# Rimuovere righe con qualsiasi NA
esami %>%
drop_na()
# studente voto_scritto voto_orale
# 1 Anna 25 27
# 2 Paolo 30 29
# Rimuovere NA solo in colonne specifiche
esami %>%
drop_na(voto_scritto)
# studente voto_scritto voto_orale
# 1 Anna 25 27
# 2 Lucia 28 NA
# 3 Paolo 30 29
fill() - Riempire Valori Mancanti
fill() riempie i valori NA usando il valore precedente (o successivo) della stessa colonna. Utile quando i dati hanno valori che si ripetono ma sono scritti solo una volta.
# Dati con valori mancanti da riempire
report <- data.frame(
trimestre = c("Q1", NA, NA, "Q2", NA, NA),
mese = c("Gen", "Feb", "Mar", "Apr", "Mag", "Giu"),
vendite = c(100, 120, 110, 130, 140, 135)
)
print(report)
# trimestre mese vendite
# 1 Q1 Gen 100
# 2 <NA> Feb 120
# 3 <NA> Mar 110
# 4 Q2 Apr 130
# 5 <NA> Mag 140
# 6 <NA> Giu 135
# Riempire verso il basso (default)
report %>%
fill(trimestre, .direction = "down")
# trimestre mese vendite
# 1 Q1 Gen 100
# 2 Q1 Feb 120
# 3 Q1 Mar 110
# 4 Q2 Apr 130
# 5 Q2 Mag 140
# 6 Q2 Giu 135
# Riempire verso l'alto
report %>%
fill(trimestre, .direction = "up")
complete() e nesting() - Completare Combinazioni
complete() genera tutte le combinazioni possibili di valori e inserisce NA dove mancano dati. nesting() limita le combinazioni a quelle gia’ presenti nei dati.
# Dati con combinazioni mancanti
presenze <- data.frame(
dipendente = c("Mario", "Mario", "Anna", "Anna"),
giorno = c("Lun", "Mar", "Lun", "Mer"),
ore = c(8, 7, 8, 6)
)
# Completare tutte le combinazioni
presenze %>%
complete(dipendente, giorno)
# # A tibble: 6 x 3
# dipendente giorno ore
# <chr> <chr> <dbl>
# 1 Anna Lun 8
# 2 Anna Mar NA
# 3 Anna Mer 6
# 4 Mario Lun 8
# 5 Mario Mar 7
# 6 Mario Mer NA
# Completare e riempire con un valore predefinito
presenze %>%
complete(dipendente, giorno, fill = list(ore = 0))
# # A tibble: 6 x 3
# dipendente giorno ore
# <chr> <chr> <dbl>
# 1 Anna Lun 8
# 2 Anna Mar 0
# 3 Anna Mer 6
# 4 Mario Lun 8
# 5 Mario Mar 7
# 6 Mario Mer 0
Esempio Completo: Pipeline di Pulizia Dati
Ecco un esempio realistico che combina piu’ funzioni tidyr e dplyr.
library(tidyr)
library(dplyr)
# Dataset di un sondaggio con dati disordinati
sondaggio <- data.frame(
partecipante = c("P001", "P002", "P003", "P004"),
info = c("Mario_Rossi_M", "Anna_Bianchi_F", "Luca_Verdi_M", "Sara_Neri_F"),
soddisfazione_2024 = c(7, 8, NA, 9),
soddisfazione_2025 = c(8, 9, 7, NA)
)
# Pipeline di pulizia completa
risultato <- sondaggio %>%
separate(info, into = c("nome", "cognome", "genere"), sep = "_") %>%
pivot_longer(
cols = starts_with("soddisfazione"),
names_to = "anno",
names_prefix = "soddisfazione_",
values_to = "punteggio"
) %>%
drop_na(punteggio) %>%
group_by(genere, anno) %>%
summarise(
media_punteggio = mean(punteggio),
n_risposte = n(),
.groups = "drop"
) %>%
arrange(anno, genere)
print(risultato)
# # A tibble: 4 x 4
# genere anno media_punteggio n_risposte
# <chr> <chr> <dbl> <int>
# 1 F 2024 8.5 2
# 2 M 2024 7 1
# 3 F 2025 9 1
# 4 M 2025 7.5 2
Conclusione
tidyr e’ il pacchetto essenziale per riorganizzare i dati in formato tidy, condizione necessaria per un’analisi efficace in R. Le funzioni pivot_longer() e pivot_wider() permettono di passare tra formato lungo e largo con facilita’, mentre separate(), unite(), drop_na(), fill() e complete() coprono le esigenze piu’ comuni di pulizia e ristrutturazione dei dati. Combinato con dplyr, tidyr forma la base su cui si costruisce ogni pipeline di analisi dati nel tidyverse.