Data Analytics
Controllo Funzioni
Introduzione ad R# Strutture di controllo in R -----
#
# - if/else
# - while e repeat
# - cicli for
# - break e next
# - apply
##
## if/ifelse -----
# Se la condizione è vera il codice viene eseguito.
# La sintassi è: if (cond) code
x <- 2
if(x < 3) print("x è minore di 3")
x <- 4
if(x < 3) print("x è minore di 3")
# Possiamo specificare anche un'istruzione da eseguire
# se la condizione è falsa mediante else
x <- 2
if(x < 3){
print(x)
print("x è minore di 3")
} else {
print(x)
print("x non è minore di 3")
}
# In singola linea
x <- 4
if(x <3) print("x è minore di 3") else print("x non è minore di 3")
# if else restituisce in output l'ultima istruzione eseguita,
# che può essere salvata in un oggetto
x <- 4
a <- if(x < 3) "x è minore di 3" else "x non è minore di 3"
a
b <- if(x < 3) "x è minore di 3" # NULL
b
x <- 2
c <- if(x < 3){
print(x)
"x è minore di 3"
} else {
"x non è minore di 3"
}
c
# equivalentemente (assegnazione dentro if/else)
x <- 4
if(x < 3) a <- "x è minore di 3" else a <- "x non è minore di 3"
a
### if-else annidati -----
x <- 6
if(x < 3){
print("x è minore di 3")
} else if(x == 3){
print("x è uguale a 3")
} else{
print("x è maggiore di 3")
}
#ifelse
b <- ifelse(x < 3, "x è minore di tre", "x non è minore di tre")
b
# Su un vettore intero
x <- c(1, 4, 2, 7, 3)
ifelse(x < 3, "piccolo", "grande")
# Esercizio
# Dato x <- c(3, 7, 1, 9, 4, 2), usa ifelse() per creare
# un vettore che contenga "pari" o "dispari" per ogni elemento.
# L’operatore modulo %% restituisce il resto della divisione intera.
x <- c(3, 7, 1, 9, 4, 2)
paridispari <- ifelse(x %% 2 == 0, "pari", "dispari" )
paridispari
## while e repeat -----
# il comando while esegue le istruzioni finchè la condizione è vera.
# La sintassi è: while (cond) code
i <- 0
while(i < 6){
i <- i + 1
print(i)
}
# Il comando repeat esegue le istruzioni finchè non incontra
# l'istruzione break. La sintassi è
# repeat {
# code
# if (cond) break
# }
# Notare che l'istruzione repeat non ha una condizione di stop,
# l'istruzione break deve quindi essere inserita per interromperne
# l'esecuzione
i <- 0
repeat{
i <- i + 1
print(i)
if(i > 5) break
}
## Cicli for ----
# Il codice del ciclo for viene eseguito finchè
# ci sono elementi in <vector> e l'indice <varname>
# scorre ad ogni iterazione su un elemento di <vector>
# for (<varname> in <vector>) {
# code to be executed
# }
for (i in 1:6) {
print(i)
}
# <vector> può essere di qualunque tipo
xval <- c("a","b","c","d","e")
for (i in xval) print(i)
### Cicli for annidati -----
xval <- c("a","b","c","d","e")
for (i in 1:5)
for (j in xval) print(paste(i,j))
## Modificatori: `break` e `next` -----
# Abbiamo già incontrato break con il comando repeat
# ma può essere utilizzato anche con while e for
i <- 0
# while con break
while(TRUE){
i <- i + 1
print(i)
if(i > 5) break
}
# for con break
for(i in 1:10){
print(i)
if(i > 5) break
}
# next permette di interropere l'iterazione attuale e
# andare all'iterazione successiva
# lo stesso output può essere ottenuto con le tre
# istruzioni cicliche
# Stampa tutti i valori da 1 a 6, saltando il 3
i <- 0
repeat{
i <- i + 1
if(i == 3) next
print(i)
if(i > 5) breaks
}
for(i in 1:10){
if (i==3) next
print(i)
if (i>5) break
}
# Esercizio 2
# Scrivi un ciclo for che stampi i numeri da 1 a 20,
# ma salti i multipli di 3 e si fermi quando incontra
# il primo multiplo di 7 maggiore di 10.
for(i in 1:20){
if (i %% 3 == 0) next # salta i multipli di 3
if (i %% 7 == 0 & i > 10) break # ferma al primo multiplo di 7 > 10
print(i)
}
# Apply ----
# la famiglia delle funzioni apply come alternativa ai cicli for
# apply
# lapply / vapply / sapply / mapply
# tapply
# `apply` è utilizzato come un ciclo su uno o più indici di un array
z <- matrix(1 : 50, nrow = 10, ncol = 5)
v1 <- apply(z, 1, mean) ; v1
v1 <- apply(z, 2, sd); v1
z[1, 1] <- NA
v1 <- apply(z, 2,mean); v1
v1 <- apply(z, 2,mean, na.rm = T)
v1 <- apply(z, 2, function(x) sum(x)/length(x))
v2 <- c()
for (i in 1:ncol(z)) v2[i] <- mean(z[,i], na.rm =T)
v1
v2
system.time({
for (i in 1:ncol(z)) v2[i] <- mean(z[,i])
})
system.time({
v1 <- apply(z,2,mean)
})
# alcuni esempi
apply(z, 1, sum, na.rm=T)
apply(z, 2, FUN = function(x) x^2)
apply(z, 2, FUN = function(x) x-min(x))
apply(z, 2, FUN = function(x) (x-mean(x))/sd(x))
apply(z, 2, scale)
# `lapply` è utilizzato come un ciclo sugli elementi di una lista e
# restuisce una lista con lo stesso numero di elementi della lista originale
# in cui ogni elemento è il risultato della funzione (`FUN`) valutata su ogni
# elemento della lista originale
# `lapply(X,FUN=...)`
x <- as.list(1:5)
v1 <- lapply(x, log)
v2 <- list()
for (i in seq_along(x)) v2[[i]] <- log(x[[i]])
v1;v2
# sapply è analoga ad lapply ma restituisce in
# output un vettore/matrice/array (quando possibile)
sapply(x, log);
sapply(3:9, seq)
# mapply: versione multivariata di sapply:
# applica una funzione passando in parallelo
# più vettori come argomenti.
mapply(rep, 1:4, 4:1)
# Esercizio 3
# Usando apply(), calcola per ogni colonna della matrice z
# (definita sopra): minimo, massimo e range (max - min).
# Confronta il risultato con un ciclo for equivalente.
z <- matrix(1:50, nrow = 10, ncol = 5)
# apply
apply(z, 2, min)
apply(z, 2, max)
apply(z, 2, function(x) max(x) - min(x))
# for equivalente
risultati <- matrix(NA, nrow = 3, ncol = ncol(z),
dimnames = list(c("min", "max", "range"), NULL))
for (i in 1:ncol(z)) {
risultati["min", i] <- min(z[, i])
risultati["max", i] <- max(z[, i])
risultati["range", i] <- max(z[, i]) - min(z[, i])
}
risultati
# Funzioni ----
# In R è possibile creare proprie funzioni ed organizzarle
# in pacchetti. I pacchetti possono essere pubblicati sul
# CRAN per renderli disponibili
# Le funzioni vengono salvate all'interno di oggetti nel workspace.
# La sintassi per scrivere una funzione è la seguente:
# function.name <- function(arg1, arg2, ...){ }
cube <- function(x){
y<-x^3
return(y)
}
b <- cube(3)
# equivalentemente
cube <- function(x){
return(x^3)
}
cube(2)
# senza return la funzione restituisce in output
# l'ultima istruzione eseguita
power <- function(x, exp){
x^exp
}
power(2,2)
a <- power(2,2); a
power <- function(x, exp = 1){ # valore di default
y <- x^exp
return(y)
}
a <- power(2,2); a
# se vogliamo in output più oggetti,
# vanno organizzati in un unico oggetto
power <- function(x, exp = 1){
y <- x^exp
return(c(result = y, input = x, exp = exp))
}
power(2)
power(2,2)
a <- power(2,2); a
power_list <- function(x, exp = 1){
y <- x^exp
return(list(result = y, input = x, exp = exp))
}
power_list(2)
power_list(2,2)
a_list <- power_list(2,2);
a_list[[1]] # equivalentemente a_list$results
a[1]
# scriviamo una funzione per calcolare la media di un vettore
x <- 1:10
mymean <- function(a) sum(a)/length(a)
mymean(x)
## Esercizio 4 ----
# Scrivere una funzione che restituisca la varianza di un insieme di n osservazioni
# e confrontare il risultato con quello ottenuto dalla funzione presente in R: var()
myvar <- function(x) {
n <- length(x)
xbar <- sum(x) / n
sum((x - xbar)^2) / (n - 1)
}
x <- c(2, 4, 4, 4, 5, 5, 7, 9)
myvar(x)
var(x) # devono coincidere
## Esercizio 5 ----
# Scrivi una funzione `mysummary()` che prenda in input
# un data frame e restituisca un data frame con,
# per ogni variabile numerica:
# minimo, massimo, media, i tre quartili,
# numero di `NA` e numero di valori unici.
# Verifica il funzionamento sul dataset `Insurance`
# del pacchetto `MASS`.
mysummary <- function(df) {
df2 <- df[, sapply(df, is.numeric)]
varMean <- apply(df2, 2, mean, na.rm = TRUE)
varQuant <- apply(df2, 2, quantile, na.rm = TRUE)
varNA <- apply(df2, 2, function(x) sum(is.na(x)))
varUnique <- apply(df2, 2, function(x) length(unique(x)))
varMin <- apply(df2, 2, min, na.rm = TRUE)
varMax <- apply(df2, 2, max, na.rm = TRUE)
as.data.frame(rbind(
min = varMin,
Q1 = varQuant[2, ],
mean = varMean,
median = varQuant[3, ],
Q3 = varQuant[4, ],
max = varMax,
n_NA = varNA,
n_unici = varUnique
))
}