L'istruzione if
Un'istruzione if in R esegue un blocco di codice solo quando una condizione è TRUE. La condizione va tra parentesi tonde, il codice tra graffe, e un else opzionale indica cosa succede altrimenti:
La condizione score >= 60 vale un singolo TRUE, quindi viene eseguito il primo ramo. Gli operatori di confronto e logici, cioè ==, >=, &&, %in% e compagnia, sono i mattoni con cui costruisci queste condizioni.
Per più di due esiti, concatena i controlli con else if. R percorre la catena dalla prima condizione in giù ed esegue il primo ramo la cui condizione è TRUE, saltando il resto:
L'ordine conta: dato che 85 >= 80 viene controllato prima di 85 >= 70, il voto è "B". Se avessi ordinato i controlli al contrario, ogni punteggio sopra 70 avrebbe preso una "C".
Il tranello delle graffe: dove deve andare else
È l'errore che morde quasi tutti al primo script R. In uno script (o in qualsiasi cosa eseguita con Rscript), else deve comparire sulla stessa riga della graffa di chiusura del ramo precedente. Questo non funziona:
if (score >= 60) {
print("You passed")
}
else { # Error: unexpected 'else'
print("Try again")
}
Il motivo: R analizza il codice istruzione per istruzione. Quando arriva alla } all'inizio di una riga, l'istruzione if è sintatticamente completa, e R la valuta lì per lì. L'else sulla riga successiva arriva come inizio di una nuova istruzione, e un'istruzione non può cominciare con else. La soluzione è lo schema usato in tutta questa pagina:
if (score >= 60) {
print("You passed")
} else { # brace and else on the same line
print("Try again")
}
Dentro il corpo di una funzione o dentro qualsiasi coppia di graffe esterna la regola si allenta (R continua a leggere finché la graffa esterna non si chiude), ma non contarci: lo stile } else { funziona ovunque ed è quello che prescrivono le guide di stile di R.
La condizione deve essere un solo TRUE o FALSE
if() pretende esattamente un valore logico. Due cose violano questa regola, ed entrambe sono errori:
if (NA) print("hi")
# Error: missing value where TRUE/FALSE needed
if (c(TRUE, FALSE)) print("hi")
# Error: the condition has length > 1
Il caso NA conta nella pratica perché i confronti con dati mancanti producono NA: se x è NA, allora x > 10 è NA, e un if con quella condizione blocca il tuo script. Lo schema difensivo usa il cortocircuito di &&:
Dato che !is.na(x) è FALSE, la parte x > 10 non viene mai valutata e nessun NA arriva alla condizione. Come NA si propaga nei confronti è spiegato in valori mancanti.
L'errore di lunghezza maggiore di 1 (imposto da R 4.2; prima R usava in silenzio il primo elemento) di solito significa che intendevi una di queste alternative: any(v > 5) se basta una corrispondenza, all(v > 5) se ogni elemento deve superare il test, oppure ifelse() se volevi una risposta per elemento, che è l'argomento della prossima sezione.
ifelse(): if else vettorizzato
if decide una volta sola. ifelse(test, yes, no) decide per ogni elemento di un vettore e restituisce un vettore della stessa lunghezza:
Ogni elemento di temps viene valutato in modo indipendente: "hot" "mild" "mild" "hot" "mild". È lo strumento per ricavare un nuovo vettore o una nuova colonna di data frame da uno esistente, senza bisogno di cicli.
A differenza di if, ifelse() gestisce con garbo i valori mancanti: un NA nel test produce un NA nel risultato invece di un errore:
Puoi annidare chiamate a ifelse() per suddivisioni a tre vie, e a questa profondità resta leggibile:
Ma fermati qui. Tre o più ifelse() annidati diventano un rompicapo di parentesi; a quel punto usa cut() (per intervalli numerici) o dplyr::case_when(), che mette ogni condizione sulla sua riga.
switch() per le scelte multiple
Quando confronti un valore con un elenco di nomi noti, e non stai testando intervalli, una lunga catena di else if è lo strumento goffo. switch() lo dice in modo diretto:
Tre comportamenti da conoscere:
- Corrispondenza:
switch()confronta il valore di tipo carattere con i nomi degli argomenti e restituisce il risultato corrispondente. - Passaggio al caso successivo: un nome con il lato destro vuoto (
sat = ,) passa al risultato del caso successivo; è così che"sat"e"sun"condividono"weekend"senza ripeterlo. - Default: l'ultimo argomento senza nome (
"weekday") viene restituito quando nulla corrisponde. Senza un default, un valore senza corrispondenza fa restituire aswitch()unNULLinvisibile: la tua funzione non restituisce niente senza dirlo, ed è un bug che confonde. Fornisci sempre un default (o rendilo unstop()esplicito).
switch() dà il meglio dentro funzioni che smistano in base a un argomento di modalità o metodo; per tutto ciò che si basa su soglie numeriche, resta su else if o cut().
Cosa ti porti a casa
if (condition) { } else { }: condizione tra parentesi, viene eseguito un solo ramo; concatena conelse if, vince la prima corrispondenza.- Negli script scrivi
} else {su una riga:elseall'inizio di una riga è un errore di sintassi. if()ha bisogno esattamente di unTRUE/FALSE:NAè un errore, un vettore più lungo è un errore (da R 4.2). Proteggiti con!is.na(x) &&, oppure riduci conany()/all().ifelse(test, yes, no)è la versione vettorizzata: usala per calcolare nuovi vettori, e non annidarla più di due volte.switch()gestisce lo smistamento multiplo per nome, con il passaggio al caso successivo tramite casi vuoti: dagli sempre un default.
Prossimo passo: fare qualcosa più di una volta, con i cicli for, e quando un ciclo è (e non è) lo strumento giusto.
Domande frequenti
Come si scrive un'istruzione if else in R?
if (condition) { ... } else { ... }: la condizione va tra parentesi tonde e ogni ramo tra parentesi graffe. Concatena altri controlli con else if (condition). In uno script, else deve stare sulla stessa riga della graffa di chiusura del ramo precedente (} else {), altrimenti R segnala unexpected 'else'.
Qual è la differenza tra if e ifelse() in R?
if è un'istruzione di controllo del flusso che valuta esattamente un valore TRUE/FALSE ed esegue un ramo di codice. ifelse(test, yes, no) è una funzione vettorizzata: valuta ogni elemento di un vettore in una volta e restituisce un vettore di risultati. Usa if per decidere cosa fa il tuo programma; usa ifelse() per calcolare una nuova colonna o un nuovo vettore a partire da uno esistente.
Perché R dice 'the condition has length > 1'?
Hai passato un vettore intero a if(), che ha bisogno di un singolo TRUE o FALSE. Da R 4.2 questo è un errore invece di un uso silenzioso del primo elemento. Risolvi riducendo il vettore a un solo valore, con any(x > 5) o all(x > 5), oppure passando all'ifelse() vettorizzato se volevi un risultato per ogni elemento.
Come funziona switch() in R?
switch(value, name1 = result1, name2 = result2, default) confronta un valore di tipo carattere con i nomi degli argomenti e restituisce il risultato corrispondente. Un nome con il lato destro vuoto passa al risultato successivo, così più casi possono condividere lo stesso esito. L'ultimo argomento senza nome fa da valore di default; senza di esso, un valore che non corrisponde a nulla restituisce NULL in modo invisibile.