Data Analytics

Controllo Funzioni

Introduzione ad R
RCode4_Controllo-Funzioni.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
  ))
}