0Pricing
R Academy · Lezione

Vettorizzazione per aumentare la velocità

Sostituisca i cicli espliciti con operazioni vettorializzate per ottenere notevoli incrementi di velocità

Vettorizzazione per aumentare la velocità è una lezione R Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento R Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso R Academy include 4 lezioni in totale.

Perché la vettorializzazione è importante

R è un linguaggio interpretato, quindi i cicli for comportano un sovraccarico a ogni iterazione: dispatch delle chiamate di funzione, controllo dei limiti e coercizione dei tipi. Le operazioni vettorializzate spostano questo lavoro nel codice C compilato, che viene eseguito molto più velocemente.

La vettorializzazione è l’ottimizzazione più efficace disponibile in R base.

Esempio di ciclo con cumsum()

Il confronto tra il calcolo di un totale progressivo con un ciclo for e la funzione integrata cumsum() mostra chiaramente la differenza. cumsum() richiama codice compilato a livello C ed elabora l’intero vettore in un’unica passata.

n <- 500000
x <- rnorm(n)

t_loop <- system.time({
  result <- numeric(n)
  result[1] <- x[1]
  for (i in 2:n) result[i] <- result[i-1] + x[i]
})['elapsed']

t_vec <- system.time({
  result2 <- cumsum(x)
})['elapsed']

cat('Loop  :', t_loop, 's
')
cat('cumsum:', t_vec, 's
')

ifelse() contro for + if

ifelse(condition, yes, no) è una condizione vettorializzata che valuta la condizione su un intero vettore in una sola volta. Sostituisce i cicli for + if elemento per elemento con un’unica passata a livello C.

n <- 300000
x <- rnorm(n)

t_loop <- system.time({
  result <- numeric(n)
  for (i in seq_len(n)) result[i] <- if (x[i] > 0) x[i] else -x[i]
})['elapsed']

t_vec <- system.time({
  result2 <- ifelse(x > 0, x, -x)
})['elapsed']

cat('for+if :', t_loop, 's
')
cat('ifelse :', t_vec, 's
')

Preallocare i vettori risultato

Quando un ciclo è inevitabile, preallochi il vettore risultato prima del ciclo. Far crescere un vettore con c(result, new_val) all’interno di un ciclo copia l’intero vettore a ogni iterazione: O(n^2) operazioni complessive sulla memoria.

n <- 20000

t_grow <- system.time({
  result <- c()
  for (i in seq_len(n)) result <- c(result, i^2)
})['elapsed']

t_prealloc <- system.time({
  result2 <- numeric(n)
  for (i in seq_len(n)) result2[i] <- i^2
})['elapsed']

cat('Growing vector:', t_grow, 's
')
cat('Pre-allocated :', t_prealloc, 's
')

Tipi corretti per la preallocazione

Utilizzi il costruttore tipizzato corrispondente ai dati per evitare conversioni implicite durante la preallocazione:

  • numeric(n) — numeri in virgola mobile a doppia precisione
  • integer(n) — numeri interi
  • character(n) — stringhe vuote
  • logical(n) — valori FALSE
  • vector('list', n) — lista di valori NULL
n <- 5
cat('numeric  :', numeric(n), '
')
cat('integer  :', integer(n), '
')
cat('logical  :', logical(n), '
')
cat('character:', character(n), '
')
cat('list len :', length(vector('list', n)), '
')

colSums() e rowSums() contro apply()

Per le operazioni sulle matrici, colSums(m), rowSums(m), colMeans(m) e rowMeans(m) sono routine C fortemente ottimizzate. Sono costantemente più veloci di apply(m, 1, sum), che richiama la funzione R sum una volta per riga.

m <- matrix(rnorm(1000 * 2000), nrow = 1000)

t_apply <- system.time(apply(m, 2, sum))['elapsed']
t_colsums <- system.time(colSums(m))['elapsed']

cat('apply(m,2,sum):', t_apply, 's
')
cat('colSums(m)    :', t_colsums, 's
')

L’aritmetica vettorializzata è sempre veloce

Le operazioni aritmetiche di base sui vettori — +, -, *, /, ^, sqrt(), log(), exp() — sono tutte vettorializzate. Operano elemento per elemento sull’intero vettore con un’unica chiamata C. Le preferisca sempre ai cicli.

x <- 1:1000000

t1 <- system.time(y <- x^2 + 2*x + 1)['elapsed']

t2 <- system.time({
  y2 <- numeric(length(x))
  for (i in seq_along(x)) y2[i] <- x[i]^2 + 2*x[i] + 1
})['elapsed']

cat('Vectorized:', t1, 's
')
cat('Loop      :', t2, 's
')

Subset logico invece dei cicli

Filtrare un vettore con una condizione logica è un'operazione vettorializzata. Invece di usare un ciclo e aggiungere elementi in base a una condizione, crei un indice logico ed esegui il subset una sola volta: il codice C sottostante effettua un'unica scansione.

x <- rnorm(500000)

t_loop <- system.time({
  pos <- c()
  for (v in x) if (v > 0) pos <- c(pos, v)
})['elapsed']

t_vec <- system.time({
  pos2 <- x[x > 0]
})['elapsed']

cat('Loop filter:', t_loop, 's
')
cat('Logical idx:', t_vec, 's
')

which() e tabulate() per lavorare con gli indici

Quando servono le posizioni dei valori TRUE, which(condition) è vettorializzato e veloce. tabulate(bin_vector) conta le occorrenze intere più velocemente di table() per intervalli di interi densi.

x <- sample(1:10, 100000, replace = TRUE)

t_table    <- system.time(table(x))['elapsed']
t_tabulate <- system.time(tabulate(x, nbins = 10))['elapsed']

cat('table()   :', t_table, 's
')
cat('tabulate():', t_tabulate, 's
')

idx <- which(x == 5)
cat('Positions of 5: first 5 =', head(idx, 5), '
')

Quando i cicli sono ancora accettabili

Non tutti i cicli sono dannosi. I cicli sono accettabili quando:

  • Ogni iterazione dipende dal risultato precedente (dipendenza sequenziale)
  • Il numero di iterazioni è ridotto (< 1000)
  • Il corpo del ciclo chiama una funzione complessa senza un equivalente vettorializzato

In questi casi, si concentri sulla preallocazione ed eviti di aumentare le strutture all'interno del ciclo.

# Sequential dependency -- loop is correct here
fib <- function(n) {
  result <- integer(n)
  result[1] <- 1L
  if (n >= 2) result[2] <- 1L
  for (i in seq_len(n)[-c(1,2)]) result[i] <- result[i-1] + result[i-2]
  result
}
cat('Fibonacci:', fib(10), '
')

Riepilogo della vettorializzazione

Regole fondamentali per ottenere codice R veloce tramite la vettorializzazione:

  • Usi cumsum/cumprod/diff per gli accumuli sequenziali
  • Usi ifelse() per le condizioni elemento per elemento
  • Preallochi con numeric(n) / vector('list',n)
  • Preferisca colSums/rowSums/colMeans/rowMeans a apply()
  • Il subset logico è preferibile ai cicli di filtraggio

Controllo rapido: preallocazione

Perché aumentare un vettore con result <- c(result, new_val) all'interno di un ciclo è così lento per valori grandi di n?

Riepilogo della vettorializzazione

La vettorializzazione è il principale strumento di R per migliorare le prestazioni:

  • Le funzioni vettorializzate (cumsum, ifelse, gli operatori aritmetici) chiamano codice C compilato e sono da 10 a 100 volte più veloci degli equivalenti cicli R
  • Preallochi i contenitori dei risultati prima di qualsiasi ciclo inevitabile per evitare copie O(n^2)
  • colSums/rowSums sono più veloci di apply() per le aggregazioni sulle matrici
  • Il subset logico sostituisce i cicli di filtraggio in modo semplice e veloce

Domande Frequenti

La lezione «Vettorizzazione per aumentare la velocità» è gratuita?

Sì — il testo completo di «Vettorizzazione per aumentare la velocità» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso R Academy, passa a CoddyKit PRO. Il corso R Academy include 4 lezioni in totale.

Cosa imparerò in «Vettorizzazione per aumentare la velocità»?

Sostituisca i cicli espliciti con operazioni vettorializzate per ottenere notevoli incrementi di velocità Eserciti R Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare R Academy?

Non è richiesta alcuna esperienza precedente. R Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Vettorizzazione per aumentare la velocità»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione R Academy?

Sì. Ogni lezione R Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. system.time() e proc.time()
  2. Profilazione del codice con Rprof e profvis
  3. Vettorizzazione per aumentare la velocità
  4. Benchmark con microbenchmark
← Torna a R Academy