1 Tipi di Dati

R è un linguaggio tipizzato dinamicamente: il tipo di una variabile viene determinato automaticamente al momento dell’assegnazione. I principali tipi scalari sono:

Tipo Esempio str() restituisce
numeric 3.14, 10 num
integer 2L, 10L int
character "hello" chr
logical TRUE, FALSE logi

R è orientato agli oggetti: possiamo creare entità denominate oggetti. Come operatori di assegnamento possiamo usare <- o =. Gli oggetti creati vengono raccolti nell’environment.

1.1 Numeric

Le variabili numeriche in R sono di default in virgola mobile (double). La funzione str() mostra il tipo e la struttura di un oggetto.

a <- 10
b <- 3 / 10                 # b vale 0.3
c <- a * (a + b) / (a - b)  # operazione aritmetica

str(a)  # Visualizziamo tipo e struttura oggetto
#>  num 10
str(b)  
#>  num 0.3
c  # Stampa il risultato
#> [1] 10.61856

Gli oggetti b e c sono stati costruiti utilizzando operatori aritmetici. I principali operatori aritmetici possono essere visualizzati mediante la funzione help(), utile a capire come utilizzare una funzione e cosa fa.

help("+")
help("str")

Qualche nota sul nome degli oggetti:

  • possono essere usati solo lettere e numeri
  • non possono essere usati spazi e smboli, eccetto: . e _
  • R fa distinzione tra maiuscole e minuscole
  • ogni nome deve iniziare con una lettera alfabetica
a.1 <- 1
a.1 
#> [1] 1
# A  # ❌ ERRORE:   l'oggetto A non esiste
# 1a <- 1 # ❌ ERRORE:  non può iniziare con un numero

1.2 Character

Le stringhe si delimitano con virgolette doppie o singole. Attenzione: "1" + "2" produce un errore, perché + non concatena stringhe.

d <- "1"
e <- "2"
# d + e  # ❌ ERRORE: non puoi sommare stringhe

s <- "Ciao"
str(s)  
#>  chr "Ciao"

💡 Per concatenare stringhe si usa paste() o paste0().

paste("Ciao", "mondo")  #considera spaziatura tra stringhe        
#> [1] "Ciao mondo"
paste("Ciao", "mondo", sep = "")  #rimuovere la spaziatura
#> [1] "Ciaomondo"
paste("Ciao", "mondo", sep = "-")  #separare con trattino
#> [1] "Ciao-mondo"
paste0("Ciao", "mondo", ".txt")   # non considera spaziatura
#> [1] "Ciaomondo.txt"

1.3 Integer

Per forzare un valore intero si usa il suffisso L. Senza L, R crea un numeric (double) anche se il valore sembra intero.

var     <- 2   # tipo: numeric (double)
var.int <- 2L  # tipo: integer

str(var)        
#>  num 2
str(var.int)    
#>  int 2

1.4 Logical

I valori TRUE e FALSE (abbreviabili in T e F) possono essere usati in operazioni aritmetiche: TRUE = 1, FALSE = 0.

x <- TRUE
y <- F

x + y   # (1 + 0)
#> [1] 1
2 * x   # (2 * 1)
#> [1] 2
str(x)  
#>  logi TRUE

1.5 Verifica del tipo e conversione tra Tipi (is.* e as.*)

Le funzioni is.* verificano il tipo

var     <- 3   
var.int <- 3L  
s <- "Hello"
x <- FALSE

is.integer(var)     
#> [1] FALSE
is.integer(var.int) 
#> [1] TRUE
is.numeric(var)
#> [1] TRUE
is.numeric(var.int)
#> [1] TRUE
is.character(s)  
#> [1] TRUE
is.logical(x)
#> [1] TRUE

💡 Le funzioni as.*() convertono da un tipo all’altro.

as.numeric("3.14")   # → 3.14
#> [1] 3.14
as.integer(3.7)      # → 3 (troncamento, NON arrotondamento)
#> [1] 3
as.character(100)    # → "100"
#> [1] "100"
as.numeric(FALSE)    # → 0
#> [1] 0
as.logical(0)        # → FALSE
#> [1] FALSE
as.logical(1)        # → TRUE
#> [1] TRUE
as.numeric("hello")  # → NA + Warning (non convertibile)
#> [1] NA

⚠️ Quando la conversione non è possibile, R restituisce NA e un Warning.


1.5.1 Esercizio

Esercizio 1
Prova a prevedere il risultato prima di eseguire il codice, poi verifica:

as.integer(3.9)       # ?
as.numeric("abc")     # ?
as.logical("TRUE")    # ?
as.character(3.14)    # ?
as.integer(3.9)       # 3 → troncamento, non arrotondamento
#> [1] 3
as.numeric("abc")     # NA → con Warning
#> [1] NA
as.logical("TRUE")    # TRUE
#> [1] TRUE
as.character(3.14)    # "3.14"
#> [1] "3.14"

2 Vettori

Un vettore è la struttura di base di R. Contiene elementi dello stesso tipo

db_v <- c(10, 15, 6.4, 3, 18)   # numeric
int_v <- c(1L, 6L, 10L)          # integer
log_v <- c(TRUE, FALSE, T, F)    # logical
ch_v  <- c("A", "B", "C")        # character

str(db_v)
#>  num [1:5] 10 15 6.4 3 18
str(int_v)
#>  int [1:3] 1 6 10
str(log_v)
#>  logi [1:4] TRUE FALSE TRUE FALSE
str(ch_v)
#>  chr [1:3] "A" "B" "C"

⚠️ Se si mescolano tipi diversi, R applica la coercizione automatica:

\[\text{logical} \rightarrow \text{integer} \rightarrow \text{double} \rightarrow \text{character}\]

str(c(TRUE, 1L))   # → tutto diventa integer
#>  int [1:2] 1 1
str(c(TRUE, 1L, 2))   # → tutto diventa numeric
#>  num [1:3] 1 1 2
str(c(TRUE, 1L, 2, "a"))   # → tutto diventa character!
#>  chr [1:4] "TRUE" "1" "2" "a"

2.1 Creare Sequenze

R offre diversi modi per generare sequenze numeriche:

💡 Operatore :, il più veloce

x <- 1:10
x
#>  [1]  1  2  3  4  5  6  7  8  9 10

💡 Funzione seq(), più flessibile

seq(from = 1, to = 10)             # passo 1 (default)
#>  [1]  1  2  3  4  5  6  7  8  9 10
seq(from = 1, to = 10, by = 3)     # passo 3
#> [1]  1  4  7 10
seq(from = 1, to = 10, length = 5) # 5 valori equidistanti
#> [1]  1.00  3.25  5.50  7.75 10.00

💡 Funzione rep(), per ripetizioni

rep(1, 15)                    # 15 volte il valore 1
#>  [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
rep(c("a", "b"), times = 5)   # "a" "b" "a" "b" ...
#>  [1] "a" "b" "a" "b" "a" "b" "a" "b" "a" "b"
rep(c("a", "b"), each = 5)    # "a" "a" "a" "a" "a" "b" "b" ...
#>  [1] "a" "a" "a" "a" "a" "b" "b" "b" "b" "b"

2.1.1 Esercizio

Esercizio 2
Prova a generare un vettore di 3 elementi contenenti i nomi dei files: “file1.txt” “file2.txt” “file3.txt”.

💡 Suggerimento: usa le funzioni di concatemento stringhe e di generamento di sequenze numeriche

```

paste("file", 1:3, ".txt", sep = "")  
#> [1] "file1.txt" "file2.txt" "file3.txt"

2.2 Selezione di Elementi (Subsetting)

⚠️ In R gli indici partono da 1 (non da 0 come in Python o C). Possiamo selezionare un singolo o molteplici elementi di un vettore

a <- c(rep(2,3), 4, 5, rep(1,5), 11:15)
a 
#>  [1]  2  2  2  4  5  1  1  1  1  1 11 12 13 14 15
a[6]        # sesto elemento
#> [1] 1
a[2:4]      # dal secondo al quarto
#> [1] 2 2 4
a[c(2, 5)]  # secondo e quinto
#> [1] 2 5
x <- c(1, 2, 4, 8, 16, 32)
x[-4]       # tutti tranne il quarto (NON assegnato: x non cambia)
#> [1]  1  2  4 16 32
x           # x è ancora intatto
#> [1]  1  2  4  8 16 32
x <- x[-4]  # x è cambiato: sovrascrittura
x
#> [1]  1  2  4 16 32

2.2.1 Selezione con Condizioni Logiche

Possiamo selezionare gli elementi di un vettore mediante usando operatori relazionali (>, >=, <, <=, ==, !=) e condizioni logiche (!, &, |)

x <- 0:7

x >= 7
#> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
x < 5
#> [1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE
x >= 7 | x < 5   # OR
#> [1]  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE  TRUE
x >= 3 & x <= 6  # AND
#> [1] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE
x.or <- x[x >= 7 | x < 5]
x.or 
#> [1] 0 1 2 3 4 7
x.and <-  x[x >= 3 & x <= 6]  
x.and
#> [1] 3 4 5 6

2.3 Operazioni su Vettori

Le operazioni in R sono vettorializzate: si applicano elemento per elemento.

x <- 1:10
x * 2    # moltiplica ogni elemento per 2
#>  [1]  2  4  6  8 10 12 14 16 18 20
x^2      # quadrato di ogni elemento
#>  [1]   1   4   9  16  25  36  49  64  81 100

2.3.1 Recycling

Se i vettori hanno lunghezza diversa, il più corto viene riciclato (recycling). Se le lunghezze non sono esattamente multiple, R lancia un Warning.

x <- rep(10, 8)
y <- c(1, 2)
z <- c(1, 2, 3)
x * y   # y viene riciclato: 10*1, 10*2, 10*1, 10*2 
#> [1] 10 20 10 20 10 20 10 20
x * z   # y viene riciclato: 10*1, 10*2, 10*1, 10*2, ... + Warning
#> Warning in x * z: la lunghezza più lunga dell'oggetto non è un multiplo della
#> lunghezza più corta dell'oggetto
#> [1] 10 20 30 10 20 30 10 20

2.3.2 Esercizi

Esercizio 3
Definisci y <- c(8, 3, 5, 7, 6, 6, 8, 9, 2).
Tutti gli elementi sono minori di 5? Crea un vettore z con i soli elementi minori di 5.

y <- c(8, 3, 5, 7, 6, 6, 8, 9, 2)

all(y < 5)    # FALSE → non tutti sono < 5
#> [1] FALSE
y < 5         # vettore logico: dove è TRUE?
#> [1] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
z <- y[y < 5] # → 3 2
z
#> [1] 3 2

Esercizio 4
Fornisci un esempio di vettore in cui valori logici vengono convertiti in 0/1 tramite un operatore aritmetico.

y <- c(TRUE, TRUE, FALSE)
z <- 1 * y   # coercizione: TRUE→1, FALSE→0
z            # → 1 1 0
#> [1] 1 1 0
# Oppure con +
y + 0        # stessa coercizione
#> [1] 1 1 0

Esercizio 5
Crea un vettore logico di lunghezza 3 e moltiplicalo per runif(3). Cosa succede?

y <- c(TRUE, TRUE, FALSE)
y * runif(3)
#> [1] 0.005836157 0.334926841 0.000000000
# I logici vengono convertiti in 0/1 prima della moltiplicazione.
# TRUE=1 → moltiplicato per un numero casuale tra 0 e 1
# FALSE=0 → risultato sempre 0

2.4 Operatori Logici: all() e any()

L’operatore logico all() verifica se tutte le condizioni in un vettore sono vere, mentre l’operatore any() verifica se almeno una condizione è vera

x <- 0:7

all(x > 0)   # FALSE → x contiene 0
#> [1] FALSE
any(x < 0)   # FALSE → nessun elemento negativo
#> [1] FALSE
all(x >= 0)  # TRUE  → tutti >= 0
#> [1] TRUE
any(x <= 0)  # TRUE  → x contiene 0
#> [1] TRUE

2.5 Funzioni Statistiche Utili

Qui una rassegna di funzioni statistiche utili

x <- 3:22

length(x)  # numero di elementi
#> [1] 20
sum(x)     # somma
#> [1] 250
mean(x)    # media
#> [1] 12.5
max(x)     # massimo
#> [1] 22
min(x)     # minimo
#> [1] 3
range(x)   # c(minimo, massimo)
#> [1]  3 22
prod(x)    # prodotto di tutti gli elementi
#> [1] 5.620004e+20

💡 Riordinare un vettore mediante sort()

x[20] <- 2
x
#>  [1]  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21  2
sort(x)    # ordina in modo crescente
#>  [1]  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21
sort(x, decreasing  = TRUE) # ordina in modo decrescente
#>  [1] 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2
order(x)   # restituisce gli INDICI che ordinerebbero x
#>  [1] 20  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19

2.5.1 Funzioni su vettori logici

Su vettori logici si possono usare alcune funzioni statistiche utili

x <- c(FALSE, FALSE, TRUE)
sum(x)    # 1  → conta i TRUE
#> [1] 1
mean(x)   # 0.333... → proporzione di TRUE
#> [1] 0.3333333

2.6 Nomi degli Elementi

Si possono dare nomi agli alementi di un oggetto * durante la creazione (Metodo 1) * utilizzando la funzione names()

x <- c(a = 1, b = 2, c = 3) # Metodo 1
x
#> a b c 
#> 1 2 3
x <- 1:3
names(x) <- c("a1", "b1", "c1") # Metodo 2
x
#> a1 b1 c1 
#>  1  2  3

Si può quindi accedere per nome

x["a1"]       # → a1 = 1
#> a1 
#>  1
names(x)[2]   # → "b1"
#> [1] "b1"

2.6.1 Esercizi

Esercizio 6 Usa set.seed(123) e crea x <- sample(10) < 4. Cosa fa which(x)?


Esercizio 7 Definisci y <- c(9, 2, 3, 9, 4, 10, 4, 11). Calcola la somma dei tre valori più grandi.

y <- c(9, 2, 3, 9, 4, 10, 4, 11)

# Metodo 1: sort decrescente
sum(sort(y, decreasing = TRUE)[1:3])   # → 30
#> [1] 30
# Metodo 2: sort + rev
y_ord <- rev(sort(y))
sum(y_ord[1:3])                        # → 30
#> [1] 30

Esercizio 8 Crea x <- 1:4. Cosa restituisce x[c(TRUE, TRUE, NA, FALSE)]? Perché?

x <- 1:4
x[c(TRUE, TRUE, NA, FALSE)]
#> [1]  1  2 NA
# → 1 2 NA
#
# TRUE  → include l'elemento (pos 1 e 2: valori 1 e 2)
# NA    → incertezza: R non sa se includere → restituisce NA
# FALSE → esclude (pos 4: valore 4 non incluso)

3 Matrici

Una matrice è una struttura bidimensionale che generalizza il vettore: contiene elementi dello stesso tipo, organizzati in righe e colonne. Si crea con la funzione matrix().

x <- matrix(c(2, 3, 5, 7, 11, 13), nrow = 3)
x
#>      [,1] [,2]
#> [1,]    2    7
#> [2,]    3   11
#> [3,]    5   13
x <- matrix(c(2, 3, 5, 7, 11, 13), ncol = 3)
x
#>      [,1] [,2] [,3]
#> [1,]    2    5   11
#> [2,]    3    7   13

⚠️ Per default R riempie la matrice per colonna (byrow = FALSE). Per riempire per riga si usa byrow = TRUE.

mx <- matrix(c(2, 3, 5, 7, 11, 13), ncol = 3, byrow = TRUE)
mx
#>      [,1] [,2] [,3]
#> [1,]    2    3    5
#> [2,]    7   11   13

3.1 Dimensioni e Nomi

Le funzioni nrow(), ncol() e dim() restituiscono rispettivamente il numero di righe, di colonne e le due dimensioni come vettore.

nrow(mx)
#> [1] 2
ncol(mx)
#> [1] 3
dim(mx)
#> [1] 2 3
dim(mx)[1]  # solo il numero di righe
#> [1] 2

Con rownames() e colnames() si assegnano nomi alle righe e alle colonne:

rownames(mx) <- c("A", "B")
colnames(mx) <- c("a", "b", "c")
mx
#>   a  b  c
#> A 2  3  5
#> B 7 11 13

3.2 Selezione di Elementi (Subsetting)

La selezione per matrici segue la notazione [riga, colonna], analoga ai vettori ma su due dimensioni. Lasciando vuota una delle due dimensioni si seleziona l’intera riga o colonna.

mx[2, 1]      # elemento in riga 2, colonna 1
#> [1] 7
mx[2, 2]      # elemento in riga 2, colonna 2
#> [1] 11
mx[2, ]       # intera seconda riga
#>  a  b  c 
#>  7 11 13
mx["B", ]     # stessa selezione, usando il nome di riga
#>  a  b  c 
#>  7 11 13
mx[, 3]       # intera terza colonna
#>  A  B 
#>  5 13
mx[, "c"]     # stessa selezione, usando il nome di colonna
#>  A  B 
#>  5 13
mx[, c("a", "b")]  # più colonne per nome
#>   a  b
#> A 2  3
#> B 7 11

💡 Come per i vettori, è possibile usare indici per selezionare righe o colonne.

x <- matrix(1:16, ncol = 4)
x
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    5    9   13
#> [2,]    2    6   10   14
#> [3,]    3    7   11   15
#> [4,]    4    8   12   16
y <- x[c(1, 4), 2:4]  # righe 1 e 4, colonne da 2 a 4
y
#>      [,1] [,2] [,3]
#> [1,]    5    9   13
#> [2,]    8   12   16

💡 Come per i vettori, è possibile usare indici negativi per escludere righe o colonne.

z <- x[-c(1:2),-1]
z
#>      [,1] [,2] [,3]
#> [1,]    7   11   15
#> [2,]    8   12   16

3.2.1 Esercizio

Esercizio 9
Crea la matrice m <- matrix(1:9, nrow = 3, byrow = TRUE).
Seleziona l’elemento in posizione (2, 3). Poi estrai la seconda colonna e la prima riga.

m <- matrix(1:9, nrow = 3, byrow = TRUE)
m[2, 3]   # → 6
#> [1] 6
m[, 2]    # → 2 5 8
#> [1] 2 5 8
m[1, ]    # → 1 2 3
#> [1] 1 2 3

3.3 Operazioni di Algebra Lineare

R offre funzioni native per le operazioni di algebra lineare, fondamentali nell’analisi statistica.

y <- c(1.2, 3, 0.4, 10)
2 * y   # moltiplicazione scalare (vettorializzata)
#> [1]  2.4  6.0  0.8 20.0

3.3.1 Prodotto Scalare e tra Matrici

Operazione Funzione / Operatore
Prodotto scalare crossprod(x, y) o x %*% y
Prodotto riga×colonna a %*% b
\(A^\top B\) crossprod(a, b)
\(A B^\top\) tcrossprod(a, b)
crossprod(c(1, 2, 3), c(0, 12, 13))  # prodotto scalare
#>      [,1]
#> [1,]   63
m <- c(1, 2, 3) %*% c(0, 12, 13)
m
#>      [,1]
#> [1,]   63
is.matrix(m)   # l'operatore %*% restituisce sempre una matrice
#> [1] TRUE
a <- matrix(c(1, 2, 3, 4), ncol = 2, byrow = TRUE)
b <- matrix(c(1, -1, 0, 1), ncol = 2, byrow = TRUE)
a
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    3    4
b
#>      [,1] [,2]
#> [1,]    1   -1
#> [2,]    0    1
a * b      # prodotto elemento per elemento
#>      [,1] [,2]
#> [1,]    1   -2
#> [2,]    0    4
a %*% b    # prodotto matriciale
#>      [,1] [,2]
#> [1,]    1    1
#> [2,]    3    1
crossprod(a, b)    # t(a) %*% b
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    2    2
tcrossprod(a, b)   # a %*% t(b)
#>      [,1] [,2]
#> [1,]   -1    2
#> [2,]   -1    4

💡 crossprod() è più efficiente di t(X) %*% X per matrici grandi, perché evita il calcolo esplicito della trasposizione.

X <- matrix(runif(100000), 50)
system.time(crossprod(X))    # più veloce
system.time(t(X) %*% X)     # più lento

3.3.2 Inversa e Sistemi Lineari

a <- matrix(c(1, 1, -1, 1), nrow = 2, ncol = 2)
a
#>      [,1] [,2]
#> [1,]    1   -1
#> [2,]    1    1
solve(a)            # matrice inversa A^{-1}
#>      [,1] [,2]
#> [1,]  0.5  0.5
#> [2,] -0.5  0.5
b <- c(2, 4)
solve(a, b)         # soluzione del sistema Ax = b → x = A^{-1}b
#> [1] 3 1

3.3.3 Funzioni Utili

diag(a)    # estrae la diagonale principale
#> [1] 1 1
diag(b)    # matrice diagonale con b sulla diagonale
#>      [,1] [,2]
#> [1,]    2    0
#> [2,]    0    4
diag(3)    # matrice identità 3×3
#>      [,1] [,2] [,3]
#> [1,]    1    0    0
#> [2,]    0    1    0
#> [3,]    0    0    1
det(a)     # determinante
#> [1] 2

3.3.4 Esercizio

Esercizio 10
Definisci A <- matrix(c(2, 1, 5, 3), nrow = 2, byrow = TRUE) e b <- c(1, 2).
Calcola l’inversa di A e verifica che A %*% solve(A) restituisca la matrice identità.
Poi risolvi il sistema \(Ax = b\).

A <- matrix(c(2, 1, 5, 3), nrow = 2, byrow = TRUE)
b <- c(1, 2)

solve(A)           # inversa
#>      [,1] [,2]
#> [1,]    3   -1
#> [2,]   -5    2
A %*% solve(A)     # dovrebbe dare la matrice identità (con errori numerici minimi)
#>      [,1]         [,2]
#> [1,]    1 2.220446e-16
#> [2,]    0 1.000000e+00
solve(A, b)        # soluzione del sistema
#> [1]  1 -1

4 Array

Un array è la generalizzazione della matrice a più di due dimensioni. Le matrici sono array con dim = c(righe, colonne).

x <- 1:20
a.x <- array(x, dim = c(5, 2, 2))
a.x
#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    1    6
#> [2,]    2    7
#> [3,]    3    8
#> [4,]    4    9
#> [5,]    5   10
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]   11   16
#> [2,]   12   17
#> [3,]   13   18
#> [4,]   14   19
#> [5,]   15   20
dim(a.x)
#> [1] 5 2 2

La selezione è analoga alle matrici, ma con tre (o più) indici:

a.x[3, 2, 1]   # elemento in posizione [3, 2, 1]
#> [1] 8
a.x[, 2, ]     # tutte le righe, seconda colonna, tutte le "fette"
#>      [,1] [,2]
#> [1,]    6   16
#> [2,]    7   17
#> [3,]    8   18
#> [4,]    9   19
#> [5,]   10   20
a.x[-1, , 1]   # escludi la prima riga, prima "fetta"
#>      [,1] [,2]
#> [1,]    2    7
#> [2,]    3    8
#> [3,]    4    9
#> [4,]    5   10

4.1 Matrice vs Vettore vs Array

Una matrice è un vettore con un attributo dim. È importante capire quando R “degrada” una struttura a vettore durante il subsetting:

str(2:4)
#>  int [1:3] 2 3 4
y <- matrix(2:4, nrow = 1)    # vettore riga
y
#>      [,1] [,2] [,3]
#> [1,]    2    3    4
str(y)
#>  int [1, 1:3] 2 3 4
y <- matrix(2:4, ncol = 1)    # vettore colonna
y
#>      [,1]
#> [1,]    2
#> [2,]    3
#> [3,]    4
str(y)
#>  int [1:3, 1] 2 3 4
a <- matrix(1:6, ncol = 3, nrow = 2)

is.matrix(a)   # TRUE
#> [1] TRUE
is.array(a)    # TRUE → una matrice è anche un array
#> [1] TRUE
b <- a[, 2]    # selezionando una colonna si perde la struttura di matrice
str(b)
#>  int [1:2] 3 4
is.vector(b)   # TRUE
#> [1] TRUE
is.matrix(b)   # FALSE
#> [1] FALSE
dim(b)         # NULL
#> NULL
as.matrix(b)   # si può riconvertire in matrice colonna
#>      [,1]
#> [1,]    3
#> [2,]    4

5 Liste

Una lista è una collezione di oggetti che possono essere di tipo diverso (vettori, matrici, altre liste…). È la struttura più flessibile di R.

x1 <- 1:3
x2 <- c("A", "B", "C", "D", "E")
x3 <- matrix(1:12, nrow = 3)

mylist <- list(x1, x2, x3)
str(mylist)
#> List of 3
#>  $ : int [1:3] 1 2 3
#>  $ : chr [1:5] "A" "B" "C" "D" ...
#>  $ : int [1:3, 1:4] 1 2 3 4 5 6 7 8 9 10 ...

5.1 Selezione degli Elementi

⚠️ In una lista si usano doppie parentesi quadre [[i]] per estrarre un elemento; le parentesi singole [i] restituiscono una sotto-lista.

mylist[[1]]          # primo elemento (il vettore x1)
#> [1] 1 2 3
mylist[[2]][3]       # terzo elemento del secondo componente
#> [1] "C"
mylist[[3]]          # la matrice x3
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    4    7   10
#> [2,]    2    5    8   11
#> [3,]    3    6    9   12
mylist[[3]][1, 1]    # elemento [1,1] della matrice in posizione 3
#> [1] 1
l1 <- mylist[[1]]
l1[2]                # o equivalentemente: mylist[[1]][2]
#> [1] 2

5.1.1 Esercizio

Esercizio rapido Seleziona le prime due righe e le prime tre colonne della matrice in posizione 3 della lista.

mylist[[3]][1:2, 1:3]
#>      [,1] [,2] [,3]
#> [1,]    1    4    7
#> [2,]    2    5    8

5.2 Liste con Nomi

Si possono assegnare nomi ai componenti di una lista durante la creazione oppure in seguito. L’accesso per nome avviene con $ o [["nome"]].

mylist2 <- list(comp1 = x1, comp2 = x2, comp3 = x3)

mylist2$comp1        # accesso con $
#> [1] 1 2 3
mylist2$comp2[3]     # terzo elemento del secondo componente
#> [1] "C"
mylist2[["comp1"]]   # accesso con [[nome]]
#> [1] 1 2 3

5.2.1 Esercizio

Esercizio 11
Crea una lista con tre componenti: un vettore numerico, una stringa e una matrice 2×2.
Assegna nomi ai componenti e accedi a ciascuno di essi in almeno due modi diversi.

mylist3 <- list(
  numeri  = c(1.5, 2.5, 3.5),
  testo   = "ciao",
  matrice = matrix(1:4, nrow = 2)
)

mylist3$numeri
#> [1] 1.5 2.5 3.5
mylist3[["numeri"]]
#> [1] 1.5 2.5 3.5
mylist3[[2]]
#> [1] "ciao"
mylist3$testo
#> [1] "ciao"
mylist3[[3]][1, 2]
#> [1] 3
mylist3$matrice[1, 2]
#> [1] 3

5.3 Aggiungere Elementi e Unire Liste

x <- list()
x[[1]] <- x1
x[[4]] <- "questo è il quarto elemento della lista x"
str(x)
#> List of 4
#>  $ : int [1:3] 1 2 3
#>  $ : NULL
#>  $ : NULL
#>  $ : chr "questo è il quarto elemento della lista x"
newlist <- c(mylist, mylist2)   # unione di due liste
is.list(newlist)
#> [1] TRUE
str(newlist)
#> List of 6
#>  $      : int [1:3] 1 2 3
#>  $      : chr [1:5] "A" "B" "C" "D" ...
#>  $      : int [1:3, 1:4] 1 2 3 4 5 6 7 8 9 10 ...
#>  $ comp1: int [1:3] 1 2 3
#>  $ comp2: chr [1:5] "A" "B" "C" "D" ...
#>  $ comp3: int [1:3, 1:4] 1 2 3 4 5 6 7 8 9 10 ...

Anche dopo l’unione si possono assegnare o modificare i nomi:

names(mylist) <- c("A", "B", "C")
names(mylist2)
#> [1] "comp1" "comp2" "comp3"
newlist <- c(mylist, mylist2)  
newlist[[1]]
#> [1] 1 2 3
newlist$A
#> [1] 1 2 3
newlist[["A"]]
#> [1] 1 2 3

5.4 Valori Speciali

R prevede alcuni valori speciali utili nella pratica:

Valore Significato Esempio
NA dato mancante (Not Available) as.numeric("a")NA
NaN non è un numero (Not a Number) 0 / 0
Inf infinito 1 / 0
-Inf meno infinito -1 / 0
NULL oggetto vuoto / assenza di valore list()
0 / 0           # NaN
#> [1] NaN
1 / 0           # Inf
#> [1] Inf
a <- -1 / 0
a               # -Inf
#> [1] -Inf
a - a           # NaN
#> [1] NaN
as.numeric("a") # NA (con Warning)
#> [1] NA

6 Fattori

Un fattore (factor) è un vettore per variabili categoriali. Ogni categoria distinta è un livello (level). I fattori sono efficienti in memoria e fondamentali per la modellazione statistica.

country <- c("Italia", "Germania", 
             "Francia", "Germania", 
             "Germania", "Germania",
             "Francia",
             "Italia", "Italia",
             "Francia")
str(country)            # character, non ancora un factor
#>  chr [1:10] "Italia" "Germania" "Francia" "Germania" "Germania" "Germania" ...
countryf <- factor(country)
str(countryf)
#>  Factor w/ 3 levels "Francia","Germania",..: 3 2 1 2 2 2 1 3 3 1
is.factor(countryf)
#> [1] TRUE
levels(countryf)        # livelli in ordine alfabetico
#> [1] "Francia"  "Germania" "Italia"

💡 factor() e as.factor() producono lo stesso risultato, ma factor() è più flessibile perché permette di specificare livelli e ordine.

6.1 Confronto tra character e factor

Il comando cbind applicato a vettori e/o matrici (purchè aventi egual numero di righe) permette di affiancare gli oggetti in colonna.

cbind(country, countryf)  # il factor viene mostrato come intero!
#>       country    countryf
#>  [1,] "Italia"   "3"     
#>  [2,] "Germania" "2"     
#>  [3,] "Francia"  "1"     
#>  [4,] "Germania" "2"     
#>  [5,] "Germania" "2"     
#>  [6,] "Germania" "2"     
#>  [7,] "Francia"  "1"     
#>  [8,] "Italia"   "3"     
#>  [9,] "Italia"   "3"     
#> [10,] "Francia"  "1"

6.2 Modificare l’Ordine dei Livelli

Per impostare un livello di riferimento diverso si usa relevel(), oppure si specifica l’ordine direttamente in factor():

a <- relevel(countryf, "Italia")
a
#>  [1] Italia   Germania Francia  Germania Germania Germania Francia  Italia  
#>  [9] Italia   Francia 
#> Levels: Italia Francia Germania
factor(country, 
       levels = c("Italia", "Germania", "Francia"))
#>  [1] Italia   Germania Francia  Germania Germania Germania Francia  Italia  
#>  [9] Italia   Francia 
#> Levels: Italia Germania Francia
# → i livelli sono ordinati: Italia < Germania < Francia
# → le etichette restano "Italia", "Germania", "Francia"

factor(country, labels = c("FR", "GE", "IT"))
#>  [1] IT GE FR GE GE GE FR IT IT FR
#> Levels: FR GE IT
# → R ordina i livelli alfabeticamente: Francia, Germania, Italia
# → poi assegna le label in quell'ordine: France→"FR", Germany→"GE", Italy→"IT"

factor(country, 
       levels = c("Italia", "Germania", "Francia"),  # definisci l'ordine
       labels = c("IT",    "GE",      "FR"))       # rinomina in corrispondenza
#>  [1] IT GE FR GE GE GE FR IT IT FR
#> Levels: IT GE FR

⚠️ Modificare levels() direttamente rinomina i livelli (non ne cambia l’ordine interno). Attenzione a non scambiare le etichette!

countryf2 <- countryf
levels(countryf)
#> [1] "Francia"  "Germania" "Italia"
levels(countryf2) <- c("Italia", "Germania", "Francia")  # rinomina i livelli
cbind(countryf, countryf2)
#>       countryf countryf2
#>  [1,]        3         3
#>  [2,]        2         2
#>  [3,]        1         1
#>  [4,]        2         2
#>  [5,]        2         2
#>  [6,]        2         2
#>  [7,]        1         1
#>  [8,]        3         3
#>  [9,]        3         3
#> [10,]        1         1
cbind.data.frame(countryf, countryf2)

6.3 Funzioni Utili sui Fattori

age <- c(47, 44, 44, 40, 38, 36, 42, 34, 34, 44)
tapply(age, countryf, mean)  # media dell'età per paese
#>  Francia Germania   Italia 
#> 43.33333 39.50000 38.33333
gender <- c(1, 1, 2, 1, 1, 2, 1, 2, 2, 2)
genderf <- factor(gender)
genderf
#>  [1] 1 1 2 1 1 2 1 2 2 2
#> Levels: 1 2
levels(genderf)
#> [1] "1" "2"
levels(genderf) <- c("F", "M")  # rinomina 1→F, 2→M
str(genderf)
#>  Factor w/ 2 levels "F","M": 1 1 2 1 1 2 1 2 2 2

6.3.1 Esercizi

Esercizio 12
Definisci x <- c(5, 12, 13, 12). Converti questo vettore in factor e ispeziona la struttura. Come vengono definiti i livelli?

x <- c(5, 12, 13, 12)
xf <- factor(x)
str(xf)
#>  Factor w/ 3 levels "5","12","13": 1 2 3 2
levels(xf)  # → "5" "12" "13" — ordinati numericamente, ma come character
#> [1] "5"  "12" "13"

6.3.2 Esercizi

Esercizio 13
Crea un factor dalla sequenza c("1", "1", "0", "1", "1", "0").
Cosa restituiscono length() e mode()?

x <- c("1", "1", "0", "1", "1", "0")
xf <- factor(x)
length(xf)  # → 6
#> [1] 6
mode(xf)    # → "numeric" (i factor sono internamente interi)
#> [1] "numeric"
str(xf)
#>  Factor w/ 2 levels "0","1": 2 2 1 2 2 1

Esercizio 14
Converti il factor del punto precedente assegnando le etichette "f" e "m" ai livelli "0" e "1". Cosa produce table()?

x2 <- factor(x, levels = c("1", "0"), labels = c("f", "m"))
x2
#> [1] f f m f f m
#> Levels: f m
table(x2)  # conta le occorrenze per livello
#> x2
#> f m 
#> 4 2

Esercizio 15
Esegui il codice seguente e poi rispondi: cosa succede a v1 quando modifichi i suoi livelli? In cosa differisce v2 da v1?

v1 <- factor(letters[1:5])
levels(v1) <- rev(levels(v1))
v2 <- factor(letters[1:5], levels = rev(letters[1:5]))
v1 <- factor(letters[1:5])
levels(v1) <- rev(levels(v1))
# In v1 si RINOMINANO i livelli: "a" diventa "e", "b" diventa "d", ecc.
# Gli elementi cambiano etichetta, non posizione interna

v2 <- factor(letters[1:5], levels = rev(letters[1:5]))
# In v2 si cambia l'ORDINE dei livelli: "e" < "d" < "c" < "b" < "a"
# Gli elementi mantengono il loro valore, ma l'ordinamento cambia

v1
#> [1] e d c b a
#> Levels: e d c b a
v2
#> [1] a b c d e
#> Levels: e d c b a