Tracce
/
R
R
/
Programma
/
Matrici e array
Ma

Matrici e array in R

{one: "1 esercizio", many: "%{count} esercizi", other: "%{count} esercizi"}

Informazioni su Matrici e array

Quando si gestiscono collezioni di dati atomici omogenei (ad esempio tutti numerici o tutti di tipo carattere), in R sono disponibili le seguenti strutture:

  • vector per dati monodimensionali, a cui si accede con un singolo indice.
  • matrix per dati bidimensionali, rettangolari (non irregolari).
  • array per il caso generale a N dimensioni. Una matrix non è altro che un array con N=2.
Note

Per chi programma in Python: questa sezione potrebbe sembrare familiare.

Qualsiasi somiglianza con NumPy non è casuale (ma R è leggermente più vecchio e ha tutto questo nel linguaggio di base).

Matrici

Una matrice non è altro che un vettore, più il numero di righe e colonne che ne definisce la forma.

C'è una funzione matrix() per crearle:

# data already available 
matrix(1:6, nrow=2, ncol=3)
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

# empty matrix to be filled in later
matrix(nrow=2, ncol=3)
#>      [,1] [,2] [,3]
#> [1,]   NA   NA   NA
#> [2,]   NA   NA   NA

Nota che i dati sono memorizzati in ordine per colonne (cioè prima in verticale, poi in orizzontale). È lo stesso ordinamento di Fortran, Matlab e Julia, ma è diverso da C/C++ e NumPy.

Trova le dimensioni di una matrice con dim() per righe e colonne insieme, oppure nrow() o ncol() per ottenere una singola dimensione. Usando length() otterrai solo la lunghezza del vettore sottostante: il numero totale di elementi (6 nell'esempio precedente).

Alcune altre funzioni producono come output una matrice, per esempio str_split_fixed().

library(stringr)

str_split_fixed(c("split some words", "and some more"), " ", 3)
#>      [,1]    [,2]   [,3]   
#> [1,] "split" "some" "words"
#> [2,] "and"   "some" "more" 

Indicizzazione delle matrici

Funziona esattamente come l'indicizzazione dei vettori, tranne per il fatto che devi specificare riga (o righe) e colonna (o colonne), in quest'ordine. Lascia vuota una dimensione per ottenere l'intera riga o colonna.

Usando la stessa matrice di sopra:

m <- matrix(1:6, nrow=2, ncol=3)
m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

m[2, 3]    # row 2, col 3
#> [1] 6

m[1:2, 3]  # rows 1:2, col 3
#> [1] 5 6

 m[2, ]    # all of row 2
#> [1] 2 4 6

Nota che un sottoinsieme con una sola riga e/o colonna viene restituito come vettore, non come matrice.

class(m)
#> [1] "matrix" "array" 
class(m[2,])
#> [1] "integer"

Questo tipo di dimension reduction non è sempre desiderato, soprattutto in una pipeline di funzioni che si aspetta matrici in input.

Specificare drop=FALSE garantisce che un input matriciale restituisca una matrice. Nota che una matrice con una sola riga o una sola colonna non è dello stesso tipo di un vettore.

m[2, , drop = FALSE]
#>      [,1] [,2] [,3]
#> [1,]    2    4    6

La funzione as.matrix() converte un vettore in una matrice a una sola colonna.

as.matrix(1:3)
#>      [,1]
#> [1,]    1
#> [2,]    2
#> [3,]    3

Come per i vettori, puoi modificare porzioni di una matrice semplicemente assegnando nuovi valori.

m[, 2] <- c(10, 20)
m
#>      [,1] [,2] [,3]
#> [1,]    1   10    5
#> [2,]    2   20    6

Aggiungere ed eliminare righe e colonne

Con i vettori, la funzione c() bastava per inserire e accodare dati. Le matrici hanno una struttura più articolata, quindi richiedono più attenzione nello specificare ciò che serve.

Le funzioni rbind() e cbind() aggiungono nuove righe o colonne alla matrice.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
m2 <- matrix(7:9, nrow = 1, ncol = 3)

rbind(m, m2)
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
#> [3,]    7    8    9

Per eliminare una riga o una colonna si usa l'indicizzazione negativa, come nel caso dei vettori.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

m[, -2]
     #> [,1] [,2]
#> [1,]    1    5
#> [2,]    2    6

Filtraggio e funzioni

Tutte le funzioni per i vettori funzionano anche sulle matrici, applicandosi a tutti gli elementi. Tuttavia, potrebbero perdere la struttura di matrice e restituire un vettore.

sqrt(m)
#>          [,1]     [,2]     [,3]
#> [1,] 1.000000 1.732051 2.236068
#> [2,] 1.414214 2.000000 2.449490

sum(m)
#> [1] 21

m[m > 3]
#> [1] 4 5 6

Per le matrici possono essere utili anche le funzioni colSums() e rowSums().

In questo caso la riduzione di dimensione è voluta, quindi i valori restituiti sono vettori.

# m as defined above
rowSums(m)
#> [1]  9 12
colSums(m)
#> [1]  3  7 11

Allo stesso modo, esistono le funzioni rowMeans() e colMeans() per calcolare le medie.

Funzioni generiche che operano sulle matrici

In altri concetti abbiamo visto che le librerie del Tidyverse possono semplificare le operazioni su vettori e liste (purrr) o su dataframe (dplyr).

Tuttavia, matrici ed array sono per lo più fuori dall'ambito degli input del Tidyverse (almeno per ora).

La funzione apply() fa parte del Base R ed è ancora lo strumento migliore per un controllo più fine della programmazione con le matrici.

I parametri, nell'ordine, sono:

  1. La matrice di input.
  2. La dimensione su cui operare (1 per le righe, 2 per le colonne).
  3. La funzione da applicare.
  4. Facoltativamente, eventuali parametri aggiuntivi per la funzione.

La funzione rowMeans() esiste nel Base R, ma potremmo anche simularla con apply() e mean().

randm <- sample(1:10, 16, replace = TRUE) |> matrix(4, 4)
randm
#>      [,1] [,2] [,3] [,4]
#> [1,]    4    9    8    3
#> [2,]    6    8    5    5
#> [3,]    4    2    1    2
#> [4,]    4    2    5    4

apply(randm, 1, mean)
#> [1] 6.00 6.00 2.25 3.75

rowMeans(randm)
#> [1] 6.00 6.00 2.25 3.75

Algebra lineare

Note

Questa sezione presuppone un livello di conoscenze matematiche al di fuori dell'ambito di Exercism.

Se non ha senso per te, salta pure.

Tuttavia, R è un linguaggio per la data science e molti utenti hanno una formazione in matematica applicata e statistica. Questo breve riassunto potrebbe interessare ad alcuni studenti.

Il R moderno è «ovviamente» un linguaggio per la data science: progettato per lavorare principalmente con dati tabulari e molto usato da utenti come statistici, epidemiologi e scienziati sociali. Il principale concorrente in questo campo è lo stack scientifico di Python, che comprende pacchetti come Pandas o Polars.

Tuttavia, nei decenni precedenti R era visto anche come un possibile linguaggio per l'ingegneria, in diretta concorrenza con Matlab.

Forse sorprendentemente, il Base R supporta ancora tutta una serie di operazioni di algebra lineare su vettori e matrici. Non è chiaro quanto queste compaiano ormai nel codice dell'utente finale, ma tecniche del genere restano molto importanti internamente per implementare le funzioni statistiche.

L'algebra lineare in R viene ancora insegnata in alcuni corsi universitari, come questo corso di econometria. Una buona guida alla matematica (non alla programmazione) è su 3Blue1Brown.

Advanced

La trattazione più completa dell'algebra lineare su Exercism si trova nel percorso di Julia (gli stessi autori del percorso di R).

Riteniamo che oggi Julia (o Python, nei contesti in cui è disponibile il modulo numpy.linalg) sarebbe un linguaggio più adatto di R per un lavoro serio di algebra lineare.

Diagonali

La diagonale principale di una matrice quadrata (N x N)) A va dall'angolo in alto a sinistra (A[1,1]) a quello in basso a destra (A[N,N]).

La funzione diag() si comporta in modo diverso a seconda dell'argomento che le viene passato.

Con un vettore v, diag(v) crea una matrice diagonale.

diag(1:3)
#>      [,1] [,2] [,3]
#> [1,]    1    0    0
#> [2,]    0    2    0
#> [3,]    0    0    3

Con una matrice quadrata A, diag(A) restituisce come vettore gli elementi sulla diagonale principale.

A
#>      [,1] [,2] [,3]
#> [1,]   10    5    2
#> [2,]    9    3    7
#> [3,]    8   10    4
diag(A)
#> [1] 10  3  4

Con un singolo intero k, diag(k) crea una matrice identità k × k: forse non è quello che avresti immaginato.

Persino i manuali di R più seri tendono ad aggiungere commenti come «Vai a capire» a questo punto.

diag(3)
#>      [,1] [,2] [,3]
#> [1,]    1    0    0
#> [2,]    0    1    0
#> [3,]    0    0    1

Trasposizione e inversa

Per scambiare righe e colonne, la funzione t() traspone qualsiasi matrice.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
t(m)
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    3    4
#> [3,]    5    6

Per una matrice a valori complessi, il risultato non è il coniugato complesso. Usa Conj(t(A)) per ottenere la trasposta coniugata (aggiunta) della matrice A.

Per array di dimensioni superiori, aperm() permuta le dimensioni.

Per l'inversa di una matrice quadrata A possiamo usare solve(A). Alcuni di noi trovano questa sintassi ... inattesa.

Moltiplicando una matrice per la sua inversa si ottiene la matrice identità (a meno di errori di arrotondamento molto piccoli).

A <- matrix(sample(1:10, 9, replace = TRUE), 3, 3)
A
#>      [,1] [,2] [,3]
#> [1,]   10    5    2
#> [2,]    9    3    7
#> [3,]    8   10    4

solve(A)
#>             [,1]        [,2]        [,3]
#> [1,]  0.16666667  0.00000000 -0.08333333
#> [2,] -0.05747126 -0.06896552  0.14942529
#> [3,] -0.18965517  0.17241379  0.04310345

A %*% solve(A) |> round(digits = 10)
     [,1] [,2] [,3]
#> [1,]    1    0    0
#> [2,]    0    1    0
#> [3,]    0    0    1

Moltiplicazione

I soliti operatori aritmetici come +, -, *, ^ operano sulle matrici elemento per elemento. Se le dimensioni non corrispondono, si applicano le solite regole del riciclo.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

m * m
#>      [,1] [,2] [,3]
#> [1,]    1    9   25
#> [2,]    4   16   36

# same calculation, different syntax
m ^ 2
#>      [,1] [,2] [,3]
#> [1,]    1    9   25
#> [2,]    4   16   36

Spiegare i vari altri tipi di moltiplicazione tra vettori e matrici esula dal nostro ambito, ma ecco un riassunto molto breve:

Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda