Last chance! 50% off DataCamp Premium
Sale ends in
La famiglia Apply di R è un insieme di funzioni in R che permettono di applicare una funzione agli elementi di un vettore, lista o matrice. Tuttavia, è considerata funzionalità legacy e non dovrebbe essere usata per nuovo codice. Al suo posto, si consiglia di usare il pacchetto purrr per tutti i loop in R. Il pacchetto purrr offre una sintassi coerente per lavorare con funzioni che accettano input e output multipli, rendendo più facile scrivere e leggere il codice. Inoltre, purrr mette a disposizione una suite di funzioni ottimizzate per ambienti di calcolo moderni, risultando più veloce ed efficiente rispetto alla famiglia Apply. In generale, sebbene la famiglia Apply di R possa ancora funzionare per alcune applicazioni, è consigliabile usare il pacchetto purrr per tutti i nuovi progetti R.
Questo post ti mostrerà come usare la funzione R apply(), le sue varianti come mapply() e alcuni dei “parenti” di apply(), applicati a diverse strutture dati. Ovviamente, non tutte le varianti possono essere discusse, ma quando possibile ti verrà presentato l’uso di queste funzioni in cooperazione, tramite un paio di esempi un po’ più sostanziosi.
Potresti anche trovare utile dare un’occhiata a questo corso introduttivo a R per capire meglio liste, vettori, array e dataframe, anche se non è necessario aver completato il tutorial per seguire questo post!
apply()La famiglia apply() fa parte del pacchetto base di R ed è composta da funzioni per manipolare porzioni di dati da matrici, array, liste e dataframe in modo ripetitivo. Queste funzioni permettono di attraversare i dati in vari modi ed evitano l’uso esplicito di costrutti di loop. Agiscono su una lista, una matrice o un array di input e applicano una funzione specificata con uno o più argomenti opzionali.
La funzione chiamata può essere:
Le funzioni apply() costituiscono la base di combinazioni più complesse e aiutano a eseguire operazioni con pochissime righe di codice. Più nello specifico, la famiglia è composta dalle funzioni apply(), lapply() , sapply(), vapply(), mapply(), rapply() e tapply().
Ma come e quando dovremmo usarle?
Dipende dalla struttura dei dati su cui vuoi operare e dal formato dell’output di cui hai bisogno.
apply() in RIniziamo con il capostipite della famiglia, apply(), che opera sugli array. Per semplicità, il tutorial si limita agli array 2D, noti anche come matrici.
Il manuale base di R indica che si chiama così: apply(X, MARGIN, FUN, ...)
dove:
X è un array o una matrice se la dimensione dell’array è 2;MARGIN è una variabile che definisce come viene applicata la funzione: quando MARGIN=1, si applica sulle righe, mentre con MARGIN=2 lavora sulle colonne. Nota che usando la costruzione MARGIN=c(1,2), si applica sia a righe che a colonne; eFUN è la funzione che vuoi applicare ai dati. Può essere qualsiasi funzione R, inclusa una UDF (User Defined Function).Ora, i principianti potrebbero avere difficoltà a visualizzare cosa succede, quindi un’immagine e un po’ di codice torneranno utili per capirlo.
Costruiamo una matrice 5 x 6 e immaginiamo di voler sommare i valori di ciascuna colonna.
Puoi scrivere qualcosa del genere:
# Construct a 5 x 6 matrix
my_matrix <- matrix(1:30, nrow = 5, ncol = 6)
# Calculate the sum of each column
col_sums <- apply(my_matrix, 2, sum)
# Print the result
print(col_sums)
La funzione matrix() crea una matrice 5 x 6 con valori da 1 a 30. Usiamo poi la funzione apply() per applicare la funzione sum() a ciascuna colonna della matrice (2 specifica che vogliamo applicare la funzione alle colonne). Il vettore risultante con le somme per colonna viene salvato nella variabile col_sums e stampato in console.
Ricorda che in R una matrice può essere vista come una raccolta di vettori riga quando la attraversi dall’alto verso il basso (lungo la dimensione o margine 1), oppure come un elenco di vettori colonna, attraversando la matrice da sinistra a destra lungo la dimensione o margine 2.
Questo significa che l’istruzione che hai appena inserito, illustrata in figura 1, si traduce in: “applica la funzione ‘sum’ alla matrice X lungo il margine 2 (per colonna), sommando i valori di ciascuna colonna”.
Nota che, per non affollare l’immagine, solo una delle colonne è evidenziata.
Ottieni un vettore riga contenente le somme dei valori di ciascuna colonna.
L’output del codice sopra, un vettore riga, sarebbe stato restituito anche se avessi sommato lungo le righe della matrice. È così che R mostra il risultato.
Una nota per quanto segue: nella maggior parte dei casi R può restituire un valore anche se questo non è stato specificato, o più precisamente se il valore di ritorno della funzione non è stato assegnato a una variabile. R restituisce semplicemente l’ultimo oggetto valutato. In pratica, però, quando vuoi controllare il valore restituito e quando devi effettuare ulteriori operazioni su quei valori, è meglio assegnare esplicitamente i risultati di una data funzione a una variabile.
lapply()Vuoi applicare una determinata funzione a ogni elemento di una lista e ottenere come risultato una lista. Se esegui ?lapply, vedrai che la sintassi assomiglia a quella di apply().
La differenza è che:
Per vedere come funziona, crea alcune matrici ed estrai da ciascuna una determinata colonna.
Questa è un’operazione piuttosto comune sui dati reali quando si fanno confronti o aggregazioni da dataframe diversi.
Il nostro esempio giocattolo, illustrato in figura 2, può essere codificato così:

L’operazione è mostrata nella parte sinistra della figura 2.
Di nuovo, inizi specificando l’oggetto di interesse, la lista Mylist. Usi l’operatore di selezione standard di R [ e poi ometti il primo parametro (che quindi si traduce in “qualsiasi”, ecco perché vedi le due virgole).
Poi specifichi il secondo parametro, che è 2: il nostro margine è “colonna”. Quindi estrai la seconda colonna da tutte le matrici all’interno della lista.
Alcune note sul codice sopra:
[ è l’operatore di selezione. Ricorda, ad esempio, che per estrarre tutti gli elementi della terza riga di B serve: B[3,];[[ ]] indica che stiamo lavorando con liste: [[2]] significa il secondo elemento della lista. Questo è mostrato anche nell’output restituito da R;lapply(MyList,"[", 1, 2)Nella parte destra della figura 2, puoi vedere un’estrazione alternativa: questa volta ometti il primo parametro e ottieni la prima riga da ciascuna delle matrici.
Provalo tu! Seleziona la seconda colonna da ogni matrice nella lista:
sapply()La funzione sapply() funziona come lapply(), ma prova a semplificare l’output alla struttura dati più elementare possibile. In effetti, sapply() è una funzione “wrapper” per lapply().
Un esempio può aiutare a capire: supponiamo di voler ripetere l’operazione di estrazione di un singolo elemento come nell’ultimo esempio, ma ora prendere il primo elemento della seconda riga (indici 2 e 1) per ciascuna matrice.
Applicare la funzione lapply() ci darebbe una lista, a meno che tu non passi simplify=FALSE come parametro a sapply(). In tal caso verrà restituita una lista. Vedi come funziona nel blocco di codice qui sotto:
Al contrario, una funzione come unlist() può istruire lappy() a restituire un vettore!
In ogni caso, per evitare confusione è meglio usare queste funzioni nel loro “formato nativo” ed evitare conversioni se non strettamente necessarie.
rep()Qualcosa che spesso si usa insieme alle funzioni apply() è rep(). Applicata a un vettore o a un fattore x, la funzione replica i suoi valori un numero specificato di volte.
Usiamo uno dei vettori che hai generato sopra con lapply() dentro MyList.
Questa volta, però, selezioni solo gli elementi della prima riga e della prima colonna da ciascun elemento della lista MyList (e usi sapply() per ottenere un vettore):
Vedi che il codice sopra replica i valori di Z un numero di volte stabilito da c(3,1,2): tre volte il primo, una volta il secondo e due volte il terzo.
Comodo, no?
mapply()La funzione mapply() sta per “multivariate” apply. Il suo scopo è poter vettorializzare gli argomenti di una funzione che di solito non accetta vettori come argomenti.
In breve, mapply() applica una funzione a più liste o a più vettori come argomenti.
Vediamo un esempio con mapply() in cui crei una matrice 4 x 4 con chiamate ripetute alla funzione rep():
Ma si vede che c’è un modo più efficiente per concatenare i risultati di rep() invece che con c(): quando chiami mapply(), vettorializzi l’azione della funzione rep().
apply()Funzioni con struttura simile sono talvolta usate in congiunzione con gli elementi della famiglia apply(): il tutorial offrirà solo una panoramica di alcune di queste.
Sweep()La funzione sweep() è probabilmente la più vicina alla famiglia apply(). La usi quando vuoi replicare azioni diverse sugli elementi MARGIN che hai scelto (qui limitandoci al caso delle matrici).
Uno scenario tipico si verifica nel clustering, dove potresti dover produrre ripetutamente dati normalizzati e centrati o “standardizzati”.
Cosa significa?
Supponi di avere un certo numero di punti dati in un gruppo di dati. Per prima cosa trovi il centro dei dati (“baricentro”) e osservi quanto sono dispersi rispetto a questo centro. Due quantità di base ti danno questa informazione: la media e la deviazione standard.
Ipotizza che i tuoi punti dati siano i vettori colonna di una matrice dei tuoi dati e usiamo la matrice B creata all’inizio del post, che ora chiami dataPoints.
Per prima cosa trovi le medie per colonna e la dispersione o deviazione standard con l’aiuto di una delle funzioni apply(). Poi trasli tutti i punti rispetto al loro centro. Questo significa che la media trovata all’inizio verrà usata per normalizzare i dati rispetto alla loro deviazione standard:
Hai prodotto i punti centrati con una chiamata a sweep(). Questa funzione si aspetta i seguenti elementi:
2 per indicare le colonne;mean); e“–” per la sottrazione.Questo significa: “prendi gli elementi delle colonne del dataset MyPoints e sottrai la media, dataPoints_means, da ciascuno di essi”.
Ora chiami di nuovo sweep() per dividere tutti i valori appena ottenuti per la loro deviazione standard. Questo passaggio si chiama “normalizzazione”. Ancora una volta selezioni MARGIN=2, poi fornisci il vettore delle deviazioni standard, dataPoints_sdev come operando. Quindi passi l’operatore “dividi per”, “/”.
Quello che chiedi a R è: “prendi gli elementi delle colonne del nuovo oggetto appena creato, dataPoints_Trans1, e dividili (“/”) per la loro deviazione standard dataPoints_sdev”.
Naturalmente, avresti potuto ottenere lo stesso risultato più rapidamente e in modo più conciso (come spesso accade in R!) e senza usare nomi diversi, con una singola riga di codice che contiene una chiamata annidata a sweep():
Statisticamente, hai appena creato una matrice di correlazione, e i dati standardizzati sono alla base di diverse procedure più avanzate (come riduzioni di dimensionalità via PCA, analisi del segnale e altro).
aggregate()Questa funzione è contenuta nel pacchetto stats e si usa così: aggregate(x, by, FUN, ..., simplify = TRUE).
In altre parole, funziona in modo simile alla funzione apply(): specifichi l’oggetto, la funzione e indichi se vuoi semplificare, proprio come con la funzione sapply(). La differenza fondamentale è l’uso della clausola by, che imposta la variabile o il campo del dataframe in base al quale vogliamo effettuare l’aggregazione.
La sezione successiva mostrerà come funziona.
aggregate()Considera il dataset di esempio chiamato Mydf, che contiene dati sulle vendite di un prodotto e in cui alcuni dei valori della colonna DepPC si ripetono.
Questa variabile classifica i dati su una località geografica, come la porzione di un CAP (qui i numeri corrispondono ai dipartimenti dell’Île-de-France, la regione che comprende Parigi).
Vuoi fare un po’ di statistiche sulle colonne delle vendite. Queste sono DProgr, un numero progressivo in ordine temporale crescente, e le vendite del prodotto (la quantità Qty), più una variabile logica, Delivered, che indica se il prodotto è stato consegnato (T) o no (F).
Per prima cosa, puoi fare alcune cose molto semplici per familiarizzare con il dataset, oltre a mostrarlo tutto digitandone semplicemente il nome (qui abbiamo solo 120 record, ma immagina di farlo per un file reale con migliaia di righe!).
Esploriamo i dati:
Nota che se vuoi vedere il numero di righe e colonne contenute nel dataframe, avresti potuto anche chiamare nrow(Mydf) e ncol(Mydf).
Molte altre interrogazioni sui dati sono possibili.
Qui, ad esempio, ti interessa sapere in quale dipartimento il prodotto vende meglio. Per questo dovresti raggruppare i dati per dipartimento, sommando le vendite, Qty, per ciascun dipartimento DepPC con l’aiuto della funzione aggregate():
Quindi, aggregate() dice a R che vuoi sommare tutte le Qty appartenenti allo stesso dipartimento.
Nota che R ha assegnato la somma a una variabile ‘x’ perché non hai specificato diversamente.
L’output è piuttosto leggibile così com’è, ma con un numero più elevato di dipartimenti potrebbe esserlo meno. In questi casi, puoi ricorrere a un output grafico: tracci i risultati usando uno dei sistemi grafici di R insieme alla funzione aggregate():
Questo ci fornisce le vendite per ciascun dipartimento.
Potresti farti la stessa domanda, ma solo per la merce consegnata. Per farlo, prima fai il subset dei dati per i quali delivered è vero (T) usando l’ormai familiare operatore di subsetting "[".
Nota che qui assegni il risultato a una nuova variabile Y, che è un nuovo dataframe che eredita gli stessi nomi di colonna dal dataframe padre Mydf. Lo fai per evitare di ripetere l’istruzione di aggregazione all’interno della chiamata al plotting per leggibilità:
Quindi avresti potuto porre domande diverse ai dati in modo vettorializzato come con aggregate(), e spesso lo fai in congiunzione con un comodo sistema di plotting come ggplot2, quindi hai colto il senso.
Nota che per ottenere questo ti sono bastate pochissime righe di codice.
Hai visto alcune variazioni sullo stesso tema, ovvero “agire su un insieme strutturato di dati in modo ripetitivo”. In questo senso, queste funzioni possono essere viste non solo come un’alternativa ai loop ma anche come una forma vettorializzata di operare.
“Vettorializzata” qui in senso lato, senza entrare nel dibattito se – e quali tra le – funzioni apply() siano davvero vettorializzate o meno (vedi ad esempio la discussione qui).
In pratica, per scegliere quale funzione apply() usare, devi considerare quanto segue:
FUN che vuoi passareQueste sono domande piuttosto generali che puoi porre per le funzioni correlate, tra cui abbiamo considerato aggregate(), by(), sweep(), ecc.
Ma ce ne sono molte altre! Non fermarti qui! Dai un’occhiata al tutorial di DataCamp su Array in R.
Come seguito a questo tutorial, valuta di seguire i corsi di DataCamp Introduction to R o Intermediate R.
Scopri di più su R
{count, plural, one {Corso} other {Corsi}}
{count, plural, one {Corso} other {Corsi}}
{count, plural, one {Corso} other {Corsi}}
{count, plural, one {blog} other {blogs}}
Tim Lu
12 min

{count, plural, one {blog} other {blogs}}
Abid Ali Awan
10 min

{count, plural, one {blog} other {blogs}}
Abid Ali Awan
15 min