Nella pratica statistica i dati raramente vengono creati direttamente
in R: più spesso provengono da file esterni (.csv,
.txt, .dat, .xlsx, …).
⚠️ Prima di importare qualsiasi file è fondamentale impostare correttamente la working directory, ovvero la cartella in cui R cerca i file per default.
getwd() # mostra la working directory corrente
setwd("") # imposta una nuova working directory (inserire il percorso tra virgolette)💡 In RStudio è possibile impostare la working directory anche da menu:
- Session → Set Working Directory
- Files → More → Set As Working Directory.
- Tasto destro sul file R → Set Working Directory
read.table() e read.csv()Le funzioni principali per importare dati tabulari sono
read.table() e read.csv(). La seconda è un
caso speciale della prima, ottimizzata per file con separatore
virgola.
# Sintassi generale
read.table("<percorso/file>", <argomenti>)
read.csv("<percorso/file>", <argomenti>)I principali argomenti comuni:
| Argomento | Significato | Default read.csv |
|---|---|---|
header |
la prima riga contiene i nomi delle variabili | TRUE |
sep |
separatore di colonna | "," |
dec |
separatore decimale | "." |
na.strings |
stringa da interpretare come NA |
"NA" |
stringsAsFactors |
converte automaticamente i character in factor | FALSE (R ≥ 4.0) |
row.names |
colonna da usare come nomi delle righe | NULL |
# File CSV con separatore virgola (default)
Food <- read.csv("importazione dataset/food_coded1.csv")
str(Food)
# Equivalente a
Food <- read.csv("importazione dataset/food_coded1.csv", header = TRUE, sep = ";")
str(Food)
# Nota: alcuni file File CSV presentano come separatore punto e virgola (comune nei file italiani)
# Vediamo cosa succede in questo caso
Food <- read.csv("importazione dataset/food_coded1.csv", header = TRUE, sep = ";")
str(Food)
# File .txt senza intestazione
cigarette <- read.table("importazione dataset/cigarette.txt", header = FALSE)
str(cigarette)
# File .dat con header e conversione automatica character → factor
cipolle <- read.table("importazione dataset/cipolle.dat", header = TRUE, stringsAsFactors = TRUE)
str(cipolle)
# File .data con valori mancanti codificati come "?"
macchine <- read.csv("importazione dataset/macchine/macchine.data", header = FALSE, na.strings = "?")
str(macchine)
# File con separatore decimale virgola (comune in Europa)
windmill <- read.table("importazione dataset/windmill.txt", dec = ",", header = TRUE)
str(windmill)💡 La lettura dei file .txt e .dat sopra è stata fatta mediante
read.table(). Tuttaviaread.csv()può essere utile allo scopo ma dobbiamo considerare come separatore lo spazio vuoto (sep = ""), dato che il separatore è preimpostato asep = ",".
# File cigarette letto con read.csv
cigarette <- read.csv("importazione dataset/cigarette.txt",
header = FALSE, sep = "")
str(cigarette)
# File cipolle letto con read.csv
cipolle <- read.csv("importazione dataset/cipolle.dat",
header = T, stringsAsFactors = T,
sep = "")
str(cipolle)💡 Per file
.xlsxsi può usare il pacchettoopenxlsx:
💡 Per altri formati (SAS, SPSS, Stata, …) è disponibile il pacchetto
foreign, che offre funzioni comeread.spss(),read.dta(), ecc.
Quando un file CSV è stato salvato con write.csv() senza
specificare row.names = FALSE, R aggiunge automaticamente
una colonna di indici (di solito chiamata X). Per evitare
il problema:
# In lettura: usa la prima colonna come nomi riga (non entra nel data frame)
dati <- read.csv("file.csv", row.names = 1)
# Oppure: rimuovi la colonna dopo la lettura
dati <- dati[, -1]
dati$X <- NULLDopo l’importazione è buona pratica ispezionare il dataset prima di
qualsiasi analisi. Usiamo il dataset Food come esempio:
contiene le risposte di studenti universitari americani su abitudini
alimentari e stile di vita.
💡 Il dataset è disponibile su Kaggle.
Food <- read.csv("importazione dataset/food_coded1.csv", header = TRUE, sep = ",")
dim(Food) # numero di righe e colonne#> [1] 125 48
#> 'data.frame': 125 obs. of 48 variables:
#> $ GPA : num 2.4 3.65 3.3 3.2 3.5 ...
#> $ Gender : int 2 1 1 1 1 1 2 1 1 1 ...
#> $ breakfast : int 1 1 1 1 1 1 1 1 1 1 ...
#> $ calories_chicken : int 430 610 720 430 720 610 610 720 430 430 ...
#> $ calories_day : int NA 3 4 3 2 3 3 3 NA 3 ...
#> $ calories_scone : int 315 420 420 420 420 980 420 420 420 315 ...
#> $ coffee : int 1 2 2 2 2 2 2 1 1 2 ...
#> $ comfort_food_reasons_coded: int 9 1 1 2 1 4 1 1 2 1 ...
#> $ cook : int 2 3 1 2 1 3 2 3 3 3 ...
#> $ cuisine : int NA 1 3 2 2 NA 1 1 1 1 ...
#> $ diet_current_coded : int 1 2 3 2 2 2 3 1 1 1 ...
#> $ drink : int 1 2 1 2 2 2 1 2 1 1 ...
#> $ eating_changes_coded : int 1 1 1 1 3 1 2 2 2 1 ...
#> $ eating_changes_coded1 : int 1 2 3 3 4 3 5 5 8 3 ...
#> $ eating_out : int 3 2 2 2 2 1 2 2 5 3 ...
#> $ employment : int 3 2 3 3 2 3 3 2 2 3 ...
#> $ ethnic_food : int 1 4 5 5 4 4 5 2 5 5 ...
#> $ exercise : int 1 1 2 3 1 2 1 2 NA 1 ...
#> $ father_education : int 5 2 2 2 4 1 4 3 5 5 ...
#> $ fav_cuisine_coded : int 3 1 1 3 1 6 4 5 1 1 ...
#> $ fav_food : int 1 1 3 1 3 3 1 1 3 1 ...
#> $ fries : int 2 1 1 2 1 1 1 1 1 1 ...
#> $ fruit_day : int 5 4 5 4 4 2 4 5 4 5 ...
#> $ grade_level : int 2 4 3 4 4 2 4 2 1 1 ...
#> $ greek_food : int 5 4 5 5 4 2 5 3 5 5 ...
#> $ healthy_feeling : int 2 5 6 7 6 4 4 3 7 3 ...
#> $ ideal_diet_coded : int 8 3 6 2 2 2 2 2 6 2 ...
#> $ income : int 5 4 6 6 6 1 4 5 5 4 ...
#> $ indian_food : int 5 4 5 5 2 5 5 1 5 4 ...
#> $ italian_food : int 5 4 5 5 5 5 5 3 5 5 ...
#> $ life_rewarding : int 1 1 7 2 1 4 8 3 8 3 ...
#> $ marital_status : int 1 2 2 2 1 2 1 1 2 2 ...
#> $ mother_education : int 1 4 2 4 5 1 4 2 5 5 ...
#> $ nutritional_check : int 5 4 4 2 3 1 4 4 2 5 ...
#> $ on_off_campus : int 1 1 2 1 1 1 2 1 1 1 ...
#> $ parents_cook : int 1 1 1 1 1 2 2 1 2 3 ...
#> $ pay_meal_out : int 2 4 3 2 4 5 2 5 3 3 ...
#> $ persian_food : int 5 4 5 5 2 5 5 1 5 4 ...
#> $ self_perception_weight : int 3 3 6 5 4 5 4 3 4 3 ...
#> $ soup : int 1 1 1 1 1 1 1 1 2 1 ...
#> $ sports : int 1 1 2 2 1 2 1 2 2 1 ...
#> $ thai_food : int 1 2 5 5 4 4 5 1 5 4 ...
#> $ tortilla_calories : int 1165 725 1165 725 940 940 940 725 725 580 ...
#> $ turkey_calories : int 345 690 500 690 500 345 690 500 345 345 ...
#> $ veggies_day : int 5 4 5 3 4 1 4 4 3 5 ...
#> $ vitamins : int 1 2 1 1 2 2 1 2 2 1 ...
#> $ waffle_calories : int 1315 900 900 1315 760 1315 1315 1315 760 900 ...
#> $ weight : int 187 155 NA NA 190 190 180 137 180 125 ...
#> [1] 48
Le operazioni su Data Frame viste in precedenza (usando R base e dplyr) si applicano nello stesso modo per data frame importati esternamente.
📌 Esercizio Con riferimento al dataset food:
- Estraiamo la variabile GPA
- Convertiamo Gender in factor
- Contiamo quante donne ci sono nel dataset
- Otteniamo: (i) GPA e peso dei maschi; (ii)sport ed employment per le sole donne; (iii) genere dei soggetti con sports == 1 e vitamins == 1
Esercizio per casa: Si ottengano gli stessi risultati usando la sintassi dplyr
#> [1] 2.400 3.654 3.300 3.200 3.500 2.250 3.800 3.300 3.300 3.300 3.500 3.904
#> [13] 3.400 3.600 3.100 NA 4.000 3.600 3.400 2.200 3.300 3.870 3.700 3.700
#> [25] 3.900 2.800 3.700 3.000 3.200 3.500 4.000 4.000 3.400 2.800 3.650 3.000
#> [37] 3.700 3.400 3.890 3.000 3.400 2.900 3.600 3.500 3.200 3.605 3.800 2.800
#> [49] 3.500 3.830 3.600 3.300 3.300 3.292 3.500 3.350 3.800 2.800 3.500 3.700
#> [61] 3.600 NA 3.900 2.600 3.500 3.200 3.000 3.600 3.200 3.670 3.730 4.000
#> [73] 3.100 3.790 2.710 3.000 3.700 3.100 3.000 3.900 3.400 3.500 3.700 3.700
#> [85] 3.830 2.600 3.000 3.200 3.500 3.200 3.680 3.800 3.300 3.200 3.750 3.500
#> [97] 3.920 3.900 3.900 3.200 3.500 3.400 NA 3.700 NA 3.000 3.000 3.800
#> [109] 3.800 3.400 3.700 2.900 3.900 3.600 2.800 3.300 3.400 3.770 3.630 3.200
#> [121] 3.500 3.000 3.882 3.000 3.900
#Food[, "GPA"] # per nome
#Food[, 1] # per indice numerico
str(Food[[1]]) # [[ ]] estrae come vettore (equivalente a $)#> num [1:125] 2.4 3.65 3.3 3.2 3.5 ...
Una volta selezionata una colonna, è possibile trattarla come un vettore e applicare tutte le operazioni viste in precedenza.
subset()
— Alternativa Espressiva a R Basesubset() accetta tre argomenti principali:
subset(data, subset = <condizione righe>, select = <selezione colonne>).
Il vantaggio principale è che non serve ripetere il nome del data frame nelle condizioni. La notazione : per selezionare un range di colonne per nome (nome_start:nome_end) è disponibile solo in subset() e non nel subsetting standard con [.
# Seleziona fruit_day e veggies_day per i maschi sportivi
Food2 <- Food[Food$Gender == "Male" & Food$sports == 1,
c("fruit_day", "veggies_day")]
# Equivalente con subset()
Food.subset <- subset(Food,
subset = (Gender == "Male" & sports == 1),
select = c(fruit_day, veggies_day))
# Escludere una colonna (indice negativo) con condizione sulle righe
Food[Food$employment == 1 | Food$employment == 2, -48]# Equivalente
#Food[Food$employment < 3, -48]
subset(Food,
subset = (employment == 1 | employment == 2),
select = -c(weight))# Nuovo data frame: maschi con reddito > 3, variabili da income a nutritional_check
newdata <- subset(Food,
subset = Gender == "Male" & income > 3,
select = c(income:nutritional_check))
str(newdata)#> 'data.frame': 0 obs. of 7 variables:
#> $ income : int
#> $ indian_food : int
#> $ italian_food : int
#> $ life_rewarding : int
#> $ marital_status : int
#> $ mother_education : int
#> $ nutritional_check: int
💡
subset()è comoda per uso interattivo. In funzioni o pacchetti è preferibile il subsetting esplicito con[, perchésubset()usa la non-standard evaluation e può dare risultati inattesi in contesti programmati.
NA)I valori mancanti sono una realtà comune nei dati reali. R li
rappresenta con NA (Not Available). Prima di
qualsiasi analisi è fondamentale capire quanti NA sono
presenti, dove si trovano e come gestirli.
💡 Su un singolo vettore/colonna
#> [1] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
#> [13] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [37] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
#> [49] TRUE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE
#> [61] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE TRUE
#> [73] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE
#> [85] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE TRUE
#> [97] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
#> [109] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE
#> [121] FALSE FALSE TRUE FALSE TRUE
#> [1] 1 9 16 40 48 49 54 60 66 72 83 93 94 96 102 117 118 123 125
#> [1] 19
#> [1] 0.152
💡 Su un intero data frame:
#> GPA Gender
#> 4 0
#> breakfast calories_chicken
#> 0 0
#> calories_day calories_scone
#> 19 1
#> coffee comfort_food_reasons_coded
#> 0 19
#> cook cuisine
#> 3 17
#> diet_current_coded drink
#> 0 2
#> eating_changes_coded eating_changes_coded1
#> 0 0
#> eating_out employment
#> 0 9
#> ethnic_food exercise
#> 0 13
#> father_education fav_cuisine_coded
#> 1 0
#> fav_food fries
#> 2 0
#> fruit_day grade_level
#> 0 0
#> greek_food healthy_feeling
#> 0 0
#> ideal_diet_coded income
#> 0 1
#> indian_food italian_food
#> 0 0
#> life_rewarding marital_status
#> 1 1
#> mother_education nutritional_check
#> 3 0
#> on_off_campus parents_cook
#> 1 0
#> pay_meal_out persian_food
#> 0 1
#> self_perception_weight soup
#> 1 1
#> sports thai_food
#> 2 0
#> tortilla_calories turkey_calories
#> 1 0
#> veggies_day vitamins
#> 0 0
#> waffle_calories weight
#> 0 4
#> GPA Gender
#> 0.032 0.000
#> breakfast calories_chicken
#> 0.000 0.000
#> calories_day calories_scone
#> 0.152 0.008
#> coffee comfort_food_reasons_coded
#> 0.000 0.152
#> cook cuisine
#> 0.024 0.136
#> diet_current_coded drink
#> 0.000 0.016
#> eating_changes_coded eating_changes_coded1
#> 0.000 0.000
#> eating_out employment
#> 0.000 0.072
#> ethnic_food exercise
#> 0.000 0.104
#> father_education fav_cuisine_coded
#> 0.008 0.000
#> fav_food fries
#> 0.016 0.000
#> fruit_day grade_level
#> 0.000 0.000
#> greek_food healthy_feeling
#> 0.000 0.000
#> ideal_diet_coded income
#> 0.000 0.008
#> indian_food italian_food
#> 0.000 0.000
#> life_rewarding marital_status
#> 0.008 0.008
#> mother_education nutritional_check
#> 0.024 0.000
#> on_off_campus parents_cook
#> 0.008 0.000
#> pay_meal_out persian_food
#> 0.000 0.008
#> self_perception_weight soup
#> 0.008 0.008
#> sports thai_food
#> 0.016 0.000
#> tortilla_calories turkey_calories
#> 0.008 0.000
#> veggies_day vitamins
#> 0.000 0.000
#> waffle_calories weight
#> 0.000 0.032
#> [1] TRUE
NA sui Calcoli⚠️ Le funzioni statistiche restituiscono
NAse il vettore contiene valori mancanti. Usare semprena.rm = TRUEper ignorarli:
#> [1] NA
#> [1] 3.028302
💡 Rimuove solo le righe con NA su una variabile o rimuove tutte le righe con almeno un NA (casi completi)
#> [1] 106 48
#> [1] 57 48
💡
complete.cases()restituisce un vettore logico:TRUEper le righe senza alcunNA. È equivalente a!is.na()applicato su tutto il data frame contemporaneamente.
⚠️
na.omit()rimuove qualsiasi riga con almeno unNA, anche su variabili irrilevanti per l’analisi. Meglio filtrare solo sulle colonne che interessano quando il dataset ha molte variabili con dati mancanti.
Una volta manipolato il dataset, si può salvare il risultato in un
file esterno o in un file .RData per riutilizzarlo in
sessioni future.
write.table() e write.csv()# Salva come file .dat (separatore spazio, con row names)
write.table(Food, file = "Food.dat")
# Salva come CSV senza row names (consigliato) e con separatore
write.table(Food, file = "Food.csv", sep = ",", row.names = FALSE)
# Equivalente con write.csv()
write.csv(Food, file = "Food.csv", row.names = FALSE)💡 Specificare sempre
row.names = FALSEper evitare che R scriva una colonna di indici numerici nel file — quella colonnaXche poi compare in fase di reimportazione.
.RData)save() salva uno o più oggetti R in un file binario
.RData, preservando tipi, attributi e strutture esattamente
come sono in memoria.
# Salva l'oggetto
save(Food, file = "Food.RData")
save(Food, Food_clean, file = "Food2.RData") # Si può salvare più di un oggetto
# Salva l'intera sessione (tutti gli oggetti nell'environment)
save.image(file = "sessione.RData")
# Ricarica gli oggetti salvati
load("Food.RData")💡
.RDataè il formato più efficiente per scambiare dati tra sessioni R: preserva fattori, livelli, attributi e strutture complesse senza perdita di informazione, a differenza del CSV.
📌 Esercizio 2 Carica il dataset
feelingdadata/feeling.Rdata. Definisci una variabile categoriale daft_immig_2016con le seguenti classi:
Classe Intervallo "strongly unfavorable"0–Q1 "unfavorable/indifferent"Q1–Q2 "lightly favorable"Q2–Q3 "favorable"Q3–100 dove Qj è il j-esimo quartile. Si usi la funzione
quantile().Aggiungi la nuova variabile al data frame. Usa la funzione
cut().
load("importazione dataset/feeling.Rdata")
Q <- quantile(feeling$ft_immig_2016,
prob = c(0.25,0.5,0.75),
na.rm = T)
feeling$immig_class <- cut(
feeling$ft_immig_2016,
breaks = c(0, Q, 100),
labels = c("strongly unfavorable", "unfavorable/indifferent",
"lightly favorable", "favorable"),
include.lowest = TRUE
)
str(feeling$immig_class)#> Factor w/ 4 levels "strongly unfavorable",..: 4 4 3 4 4 4 4 1 4 4 ...
#>
#> strongly unfavorable unfavorable/indifferent lightly favorable
#> 1966 1946 1885
#> favorable
#> 1891
⚠️ Quartili
Un quartile è un valore che divide i dati ordinati in quattro parti uguali.
Prima si ordinano i dati dal più piccolo al più grande. Poi si individuano tre valori che li dividono in quattro gruppi.
Questi valori si chiamano:
Q1 (primo quartile): il valore sotto cui si trova circa il 25% dei dati (25% dei dati è minore o uguale a questo valore)
Q2 (secondo quartile): il valore sotto cui si trova il 50% dei dati (è la mediana, 50% dei dati è minore o uguale a questo valore)
Q3 (terzo quartile): il valore sotto cui si trova circa il 75% dei dati (75% dei dati è minore o uguale a questo valore)
📌 Esercizio 3 Rimuovi i valori mancanti dal dataset
feelinged esporta il data frame risultante in un file.csvsenza colonna indice.
#> [1] 5383 16
header = TRUE (default in read.csv)
se la prima riga contiene i nomi delle variabili.dec = ",").na.strings = "?" (o altro simbolo) per far
riconoscere a R i valori mancanti codificati diversamente da
NA.row.names = FALSE in write.csv() per
evitare la colonna indice indesiderata..RData per scambi interni a R; preferire
.csv per interoperabilità con altri software.# WORKING DIRECTORY
getwd() # Vedi percorso attuale
setwd("percorso/cartella") #Imposta percorso
# IMPORTAZIONE
# Lettura file di default e con specifiche su
# header, separatori di colonna e decimali,
# con conversione automatica di stringhe a fattori
read.csv("file.csv", header = TRUE, sep = ";", dec = ",", stringsAsFactors = T)
# ... con specifica della codifica degli NA
read.table("file.txt", header = FALSE, na.strings = "?", dec = ",")
# Selezione Colonne e Filtraggio Righe
df[righe, colonne]
df[df$col > 5, ]
subset(df, subset = col > 5, select = c(col1, col2))
# VALORI MANCANTI
# Su vettore
is.na(x) # C i sono NA?
sum(is.na(x)); mean(is.na(x)) #Somma e proporzione NA
x[!is.na(x)]; na.omit(x) # omissione NA
mean(x, na.rm = TRUE) #Rimozione NA per applicare funzione
# Su data frame
# Somma e proporzione NA per colonna
colSums(is.na(df)); colMeans(is.na(Food))
any(is.na(df)) # almeno un NA?
# Omissione NA
na.omit(df); df[complete.cases(df), ]
# ESPORTAZIONE
# Scrittura files con specifica su nomi righe e
# separatori
write.csv(df, "file.csv", row.names = FALSE)
write.table(df, "file.csv", row.names = FALSE, sep = ",")
# Salvataggio e caricamento file .RData
save(obj1, obj2, file = "dati.RData")
load("dati.RData")