saluta <- function(){
str_saluto <- "Benvenut* nel mondo della programmazione"
return(str_saluto)
}Creazione di funzioni in R
Funzioni in R
Le funzioni sono un particolare tipo di oggetto R che vengono create sfruttando la parola chiave function e che permettono di estendere l’ambiente R usando la stessa logica delle funzioni predefinite e di quelle presenti nei package aggiuntivi. Ecco un semplice esempio di una funzione saluta che non ha nessun input e che restuisce in output una stringa di testo:
La funzione, al pari di tutti gli altri oggetti in R, ha mode e class:
mode(saluta)[1] "function"
class(saluta)[1] "function"
Se si richiama la funzione inserendo solo il nome sul prompt è possibile vedere la sua definizione (corpo della funzione):
salutafunction ()
{
str_saluto <- "Benvenut* nel mondo della programmazione"
return(str_saluto)
}
Se invece si vuole sfruttare la funzionalità per la quale la funzione è stata progettata deve essere richiamata usando la sintassi tipica delle funzioni in R, ovvero usando le parentesi tonde:
saluta()[1] "Benvenut* nel mondo della programmazione"
Tipicamente una funzione prende argomenti in input: alcuni argomenti possono essere obbligatori ed altri opzionali, prevedendo per questi ultimi un valore di default. Proviamo ad utilizzare i due tipi di argomenti nella funzione saluta:
saluta <- function(str_nome, str_saluto = "benvenut*"){
str_saluto <- paste0(str_nome,
", ",
str_saluto,
" nel mondo della programmazione")
return(str_saluto)
}Richiamando la funzione con il solo nome, la funzione usa il valore di default impostato al secondo argomento, per formulare il saluto di benvenuto:
saluta("Domenico")[1] "Domenico, benvenut* nel mondo della programmazione"
E’ possibile però declinare il saluto sfruttando il secondo argomento, “sovrascrivendo” il valore di default.
saluta("Domenico", "benvenuto")[1] "Domenico, benvenuto nel mondo della programmazione"
saluta(str_nome = "Domenico", str_saluto = "benvenuto")[1] "Domenico, benvenuto nel mondo della programmazione"
Dai due esempi sopra, si nota come il passaggio degli argomenti alla funzione segue le stesse regole delle funzioni predefinite in R, ovvero si possono passare gli argomenti sia con la sintassi di chiamata per nome che con la sintassi di chiamata per posizione.
Proviamo ora a definire una funzione che calcola la mediana per una serie grezza di dati di numerosità dispari:
mediana_dispari <- function(x){
x <- sort(x)
n <- length(x)
pos_mediana <- (n + 1) / 2
return(x[pos_mediana])
}Ed una funzione analoga che effettua il calcolo per una serie grezza di dati di numerosità pari:
mediana_pari <- function(x){
x <- sort(x)
n <- length(x)
pos_uno <- n / 2
pos_due <- pos_uno + 1
mediana <- (x[pos_uno] + x[pos_due]) / 2
return(mediana)
}Sfruttando il costrutto condizionale if - else è possibile definire un’ulteriore funzione che richiama la funzione opportuna a seconda che il vettore in input sia di numerosità pari o dispari:
mediana <- function(x){
n <- length(x)
if(n %% 2 == 0){
out <- mediana_pari(x)
} else {
out <- mediana_dispari(x)
}
return(out)
}La funzione “esterna” mediana può quindi essere chiamata per un generico vettore in input ed in base al numero di elementi contenuti nel vettore chiamerà a sua volta la funzione mediana_pari o mediana_dispari:
mediana(c(20, 40, 10, 5))[1] 15
mediana(c(20, 40, 10, 5, 30))[1] 20
Un semplice modo per seguire il flusso del codice al variare dell’input e per eseguire rudimentali operazioni di debug consiste nell’inserire delle istruzioni di print (esistono strumenti di debugging più avanzati che mostreremo nelle prossime lezioni):
mediana <- function(x){
print("Sono appena 'entrato' nella funzione...")
print("Vettore in input:")
print(x)
n <- length(x)
x <- sort(x)
print("Vettore ordinato:")
print(x)
if(n %% 2 == 0){
print("Ora applico la regola di calcolo per n pari...")
pos_uno <- n / 2
pos_due <- pos_uno + 1
out <- (x[pos_uno] + x[pos_due]) / 2
} else {
print("Ora applico la regola di calcolo per n dispari...")
pos_mediana <- (n + 1) / 2
out <- x[pos_mediana]
}
print("Ora restituisco in output il valore calcolato...")
return(out)
print("Purtroppo sono già uscito dalla funzione...")
}Proviamo a richiamare la funzione passando in input un vettore di numerosità pari:
mediana(c(20, 40, 10, 5))[1] "Sono appena 'entrato' nella funzione..."
[1] "Vettore in input:"
[1] 20 40 10 5
[1] "Vettore ordinato:"
[1] 5 10 20 40
[1] "Ora applico la regola di calcolo per n pari..."
[1] "Ora restituisco in output il valore calcolato..."
[1] 15
ed un vettore di numerosità pari:
mediana(c(20, 40, 10, 5, 30))[1] "Sono appena 'entrato' nella funzione..."
[1] "Vettore in input:"
[1] 20 40 10 5 30
[1] "Vettore ordinato:"
[1] 5 10 20 30 40
[1] "Ora applico la regola di calcolo per n dispari..."
[1] "Ora restituisco in output il valore calcolato..."
[1] 20
Le istruzioni di print ci permettono di seguire il flusso del codice regolato dall’istruzione condizionale if - else. In entrambi i casi si nota come l’ultima istruzione print, inserita dopo il return, non viene mai raggiunta durante l’esecuzione della funzione.
Possiamo ulteriormente migliorare la nostra funzione in input inserendo un argomento opzionale na.rm, che impostiamo a FALSE alla stregua dell’analoga funzione median già disponibile in R e che restituisce un NA se nel vettore è presente un missing:
mediana <- function(x, na.rm = FALSE){
if(na.rm == TRUE)
x <- na.omit(x)
if(sum(is.na(x)) > 0)
out <- NA
else{
x <- sort(x)
n <- length(x)
if(n %% 2 == 0){
pos_uno <- n / 2
pos_due <- pos_uno + 1
out <- (x[pos_uno] + x[pos_due]) / 2
} else {
pos_mediana <- (n + 1) / 2
out <- x[pos_mediana]
}
}
return(out)
}Proviamo ora ad utilizzare la funzione su un vettore in input che contiene un valore mancante cambiando l’argomento che indica come gestire gli NA:
mediana(c(20, 40, 10, NA, 30))[1] NA
mediana(c(20, 40, 10, NA, 30), na.rm = TRUE)[1] 25