00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

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:

  1. Ogni variabile occupa una colonna
  2. Ogni osservazione occupa una riga
  3. 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.