6 Funzioni e Librerie

In R, è possibile trovare una grandissima quantità di funzioni matematiche, probabilistiche e statistiche. Non sarà perciò possibile affrontarle tutte in questo corso.

6.1 Le funzioni

Usare una funzione è piuttosto semplice. Basta scrivere il nome della funzione e poi i gli oggetti su cui applicarla tra parentesi tonde:

round(3.1415)
## [1] 3
factorial(3) #3!
## [1] 6

Gli oggetti che passiamo nella funzione sono chiamati argomenti. Un argomento può essere un dato grezzo, un oggetto di R o, addirittura il risultato di un’altra funzione.

In quest ultimo caso, R risolverà prima la funzione interna e poi quella esterna.

mean(1:6)
## [1] 3.5
mean(valore)
## [1] 6
round(mean(valore))  # prima mean e poi round
## [1] 6

Per estrarre casualmente una carta dal nostro mazzo possiamo usare la funzione sample().

La funzione sample() ha 2 argomenti obbligatori: un vettore chiamato x e un numero chiamato size. sample(x,size) ritonerà un vettore di size elementi campionati da x. Ad esempio:

sample(x = 1:4, size = 2)
## [1] 3 4

Per estrarre una carta casualmente impostiamo la seguente funzione:

carta <- deck[sample(x = 1 : dim(deck)[1], size = 1), ] #dim(deck)[1] è il numero di righe di deck, equivalente a nrow(deck)

Molte funzioni richiedono diversi argomenti, ogni argomento stabilisce una precisa variante della funzione. Gli argomenti vanno separati dalle virgole.

In R tutti gli argomenti hanno un nome (sono tutte keyword). Per specificare il valore di un argomento si usa l’operatore = come fatto nel precedente script. L’ordine degli argomenti non conta se si usano i loro nomi.

Tuttavia, se si segue l’ordine degli argomenti, non è obbligatorio specificare il nome di un argomento, di solito il primo argomento viene inserito senza nome:

sample(1 : dim(deck)[1], size = 1)
## [1] 19

Cosa succede se si definisce il valore di un argomento che non esiste?

round(3.1415, corners = 2)
## Error in round(3.1415, corners = 2): unused argument (corners = 2)

Se non si è sicuri di quali argomenti passare alla funzione, si può usare la funzione args().

Ad esempio, possiamo vedere che la funzione round() accetta 2 argomenti: x e digits:

args(round)

Si noti che con args() è possibile vedere anche il valore di default dell’argomento.

Ad esempio il valore di default di digits è 0. Quindi, se non definiamo un valore per questo argomento R arrotonderà alla prima cifra unitaria (0 decimali).

Con args() è possibile vedere anche l’ordinamento degli argomenti. Qualora si decida di non nominare un argomento è necessario rispettarne l’ordine. Nel caso contrario possono apparire nell’ordine che si preferisce.

Ad esempio:

sample(1 : nrow(deck), 1)             # = sample(1 : nrow(deck), size = 1)
## [1] 10
sample(size = 1, x = 1 : nrow(deck))  # se nomino gli argomenti posso decidere l'ordine che voglio
## [1] 47

6.1.1 Estraiamo una carta con reintroduzione

Di default, sample() campiona senza reintroduzione, vale a dire che l’argomento replace è uguale a FALSE, se non diversamente specificato. Proviamo ad esempio a estrarre 53 carte dal nostro mazzo.

sample(1 : nrow(deck), size = 53)   
## Error in sample.int(length(x), size, replace, prob): cannot take a sample larger than the population when 'replace = FALSE'

Estrarre senza reintroduzione 53 carte da un mazzo di 52 carte non è possibile. Impostiamo l’argumento replace = FALSE per permettere di campionare con reintroduzione.

sample(1 : nrow(deck), size = 53, replace = TRUE)   
##  [1] 35 13 42  1 16  4 15 25 39 16 28 10 43 24  6 49 37
## [18] 10 34 11 51 32 28 14 52 38 51  8 30 46 29 15 26 33
## [35] 38 50 17 36  4  5 32 31 49 27 46 47 12  8 27 14 46
## [52] 40 19

Abbiamo appena eseguito la nostra prima simulazione con R!

Esercizio 15

Creare un dado con 6 facce. Simulare il lancio di 3 dadi attraverso la funzione sample(). Sommare il risultato ottenuto usando la funzione sum().

dado <- 1 : 6
lancio <- sample(dado, size = 3, replace = TRUE)
sum(lancio)
## [1] 14

Ora che sappiamo come estrarre una carta dal mazzo avremo bisogno di un oggetto che ogni volta che viene richiamato estragga una carta. Si può crare questo genere di oggetto scrivendo la propria funzione.

6.2 Scrivere una funzione

Ricapitolando, scrivendo le seguenti righe di codice possiamo estrarre una carta:

carta <- sample(1 : nrow(deck), size = 1, replace = TRUE)

Possiamo scrivere ogni volta questa linea di codice o possiamo creare una funzione che, una volta richiamata, svolga la stessa operazione.

Andremo a chiamare questa funzione estraiCarta().

6.2.1 Il costruttore function

Ogni funzione in R è costituita da tre parti fondamentali: un nome, un corpo (il codice) e un set di argomenti. Per creare la nostra funzione dobbiamo scrivere queste tre parti e salvarle in un oggetto di R. Questa operazione può essere svolta tramite il costruttore function nel seguente modo:

myFunction <- function() {
#corpo
}

function costruirà una funzione a partire dal codice (corpo) che verrà inserito all’interno delle parentesi graffe. Ad esempio, possiamo inserire il nostro codice per estrarre una carta all’interno della funzione estraiCarta():

estraiCarta <- function(mazzo) {
  riga <- sample(1 : nrow(mazzo), size = 1, replace = TRUE)
  mazzo[riga, ]
}

Si noti che la funzione avrà bisogno di un argomento, che nel nostro caso sarà un dataframe di carte. Abbiamo chiamato il primo argomento di estraiCarta mazzo. Per usare la funzione basta richiamarla:

estraiCarta(mazzo = deck) # equivalente a estraiCarta(deck)
##    figura  seme valore id
## 20  sette fiori      7 20

Ogni volta che la funzione viene richiamata, una nuova carta verrà campionata dal mazzo. Se si richiama la funzione senza le parentesi tonde, R vi stamperà nella console il suo contenuto:

estraiCarta
## function(mazzo) {
##   riga <- sample(1 : nrow(mazzo), size = 1, replace = TRUE)
##   mazzo[riga, ]
## }

Nel corpo della funzione vanno indicate tutte le operazioni da svolgere. L’ultima linea corrisponde al valore da ritornare in output, si può usare anche il comando return(risultato), oppure non produrre nessun risultato:

estraiCarta <- function(mazzo) { #uguale a prima ma uso return()
  riga <- sample(1 : nrow(mazzo), size = 1, replace = TRUE)
  return(mazzo[riga, ])
}
estraiCartaNoOutput <- function(mazzo) { #nessun risultato
  riga <- sample(1 : nrow(mazzo), size = 1, replace = TRUE)
  cartaEstratta <- mazzo[riga, ]
}
estraiCartaNoOutput(mazzo)
## Error in nrow(mazzo): oggetto "mazzo" non trovato

6.3 Ambiente locale e globale

Tutti gli oggetti creati all’interno di una funzione (ambiente locale) non vengono salvati nel Global Environment (ambiente globale) percui non possono essere richiamati al di fuori della funzione.

cartaEstratta
## Error in eval(expr, envir, enclos): oggetto "cartaEstratta" non trovato

Allo stesso tempo, tutti gli oggetti richiamati all’interno della funzione (ambiente locale) devono essere stati creati all’interno della funzione oppure devono essere stati passati attraverso gli argomenti. Ad esempio, vediamo cosa succede se si crea una funzione che usa l’oggetto deck ma quest ultimo non viene passato attraverso gli argomenti:

myFunction <- function() {
  head(deck)
} #fin qui nessun errore
myFunction() #errore?
##   figura   seme valore id
## 1     re picche     13  1
## 2 regina picche     12  2
## 3   jack picche     11  3
## 4  dieci picche     10  4
## 5   nove picche      9  5
## 6   otto picche      8  6

E invece no! R non produce un errore in questo caso poichè l’oggetto deck si trova nell’ambiente globale che è superiore all’ambiente locale. R esegue una ricerca seguendo un ordine gerarchico degli ambienti: prima cercherà l’oggetto all’interno dell’ambiente locale, se non lo trova, va a esaminare l’ambiente superiore (in questo caso l’ambiente globale) fino a che non raggiunge l’ambiente globale. Se non trova l’oggetto nell’ambiente globale ritorna un errore.

Ricapitolando, all’interno di ogni ambiente si possono utilizzare solo oggetti creati nell’ambiente stesso o negli ambienti superiori. Per poter usare oggetti provenienti da ambienti inferiori o dello stesso livello questi devono essere stati passati attraverso gli argomenti o attraverso la funzione return().

6.4 Argomenti

Possiamo dare alla nostra funzione quanti argomenti vogliamo. Basta separare i nomi degli argomenti da delle virgole quando creiamo la funzione.

Abbiamo inoltre visto che un argomento può avere un valore di default, come faccio a impostarlo?

Per rispondere a questa domanda proviamo a creare una funzione che estragga dal nostro mazzo un numero di carte nCarte definito dall’utente, di default la funzione estrarrà 2 carte:

estraiCarte <- function(mazzo, nCarte = 2) { # estrae 2 carte se non diversamente specificato
  righe <- sample(1 : nrow(mazzo), size = nCarte, replace = TRUE)
  return(mazzo[righe, ])
}
estraiCarte(deck)            #estrae 2 carte
##    figura  seme valore id
## 50    tre cuori      3 50
## 14     re fiori     13 14
estraiCarte(deck, nCarte = 3)  #estrae 3 carte
##    figura   seme valore id
## 51    due  cuori      2 51
## 9  cinque picche      5  9
## 42   jack  cuori     11 42

Esercizio 16

Creare una funzione che mischi le carte del mazzo deck e dia come risultato il mazzo mischiato.

shuffle <- function(mazzo) { # estrae 5 carte se non diversamente specificato
  righe <- sample(1 : nrow(mazzo), size = nrow(mazzo), replace = FALSE)
  return(mazzo[righe, ])
}
shuffledDeck <- shuffle(deck) 
head(deck)
##   figura   seme valore id
## 1     re picche     13  1
## 2 regina picche     12  2
## 3   jack picche     11  3
## 4  dieci picche     10  4
## 5   nove picche      9  5
## 6   otto picche      8  6
head(shuffledDeck)
##    figura   seme valore id
## 51    due  cuori      2 51
## 1      re picche     13  1
## 3    jack picche     11  3
## 31   nove quadri      9 31
## 44   nove  cuori      9 44
## 48 cinque  cuori      5 48

6.5 Librerie (packages)

Molte delle funzioni statistiche di R non vengono installate con R o RStudio ma risiedono in dei pacchetti o librerie (packages) che possono essere installati “on top”.

Per installare un pacchetto basta usare la funzione seguente:

install.packages("<nome pacchetto>")

Un pacchetto non è altro che un insieme di file (script di R e altro) che vengono salvati nella cartella “R-ver/win-library” quando vengono installati. Perciò non è necessario eseguire il comando precedente ogni volta che si vuole usare un pacchetto già installato.

Per usare il pacchetto (e le funzioni al suo interno) bisogna “caricare” il pacchetto attraverso la funzione:

library(<nome pacchetto>)

Notare che questa volta non sono necessarie le virgolette per richiamare il pacchetto. Infatti, una volta installato, un pacchetto diventa un oggetto di R.

Per avere delle informazioni su un pacchetto è possibile scaricare la sua documentazione cercando su google “documentation <nome pacchetto>”. Altrimenti, in R si può digitare il comando:

help(package = "<nome pacchetto>")

La funzione help() può essere usata anche per leggere la documentazione relativa a una funzione. Ad esempio:

help(mean) # mean() serve a calcolare la media
#or
?mean
help(mean)

Figure 6.1: help(mean)

La documentazione relativa a una funzione contiene 6 componenti principali: Description, Usage, Arguments, Details, Value e Examples.

  • Nella Description vi è una descrizione sintetica della funzione.
  • L’Usage contiene i metodi della funzione, vale a dire le diverse implementazioni della funzione, in questo caso, mean() può essere usata con un solo argomento (x) oppure specificando anche gli argomenti trim e na.rm.
  • Nella sezione Arguments sono descritti tutti gli argomenti personalizzabili della funzione. Nelle prime parole di ogni argomento troviamo il tipo di oggetto che può assumere. Ad esempio, trim può essere un valore compreso tra 0 e 0.5.
  • In Details troviamo i dettagli tecnici della funzione come le tipologie di metodologie implementate o come sono strutturati gli oggetti dati in input. In mean() questa sezione è mancante perchè non necessaria.
  • Il Value è l’oggetto che ritorna la funzione, cioè quello che compare nel return(). A volte, un oggetto in output può essere molto complesso e necessita di una descrizione dettagliata.
  • In Examples vi sono svariati esempi di utilizzo della funzione, molto utili a capire come usarla nella pratica.

Esercizio 17

  1. Quanti argomenti ha la funzione quantile()?
  2. Quanti elementi ha l’oggetto ritornato dalla funzione quantile()?
  3. Che valori può assumere il vettore probs?
  4. Spiegare cosa succede se inserisco come argomento probs = c(NA, 0.2, 0.5).
  5. A che cosa serve la funzione lm()?
  6. Quali caratteristiche dell’oggetto in output saranno mancanti se imposto qr = FALSE?
  7. Com’è strutturato l’oggetto in output? Di che classe di oggetti fa parte?
  1. 5
  2. Tanti quanti la lunghezza di probs. Uno per quantile calcolato.
  3. Può essere uguale a un vettore numerico con valori compresi tra 0 e 1.
  4. “NA and NaN values in probs are propagated to the result.” significa che se inserisco un NA al posto di un valore [0,1] in probs, la funzione ritornerà un NA nella posizione corrispondente. Nel nostro caso avremmo un vettore di 3 elementi che come primo elemento ha un NA.
  5. Serve per stimare modelli lineari (e non solo stimare).
  6. Nell’oggetto in uscita non verrà ritornata la scomposizione QR.
  7. Fa parte della classe apposita “lm” e contiene numerose caratteristiche del modello, dai dati in entrata (x,y,…) alle stime (coefficients), dalle impostazioni (weights, model,…) ai residui (residuals).

:::