In R tutto è un vettore
La maggior parte dei linguaggi ha gli scalari, cioè valori singoli, e poi un tipo contenitore per tenerne diversi. R salta lo scalare. Un vettore è una raccolta ordinata di valori che condividono tutti lo stesso tipo, ed è l'unità su cui è costruito tutto il resto. Anche quello che sembra un numero isolato è un vettore di lunghezza 1:
c(), abbreviazione di combine, è il modo per costruire un vettore a mano. length() ti dice quanti elementi contiene. E 5 da solo risulta TRUE per is.vector, con lunghezza 1: R non smette mai di lavorare con i vettori, a volte lavora solo con vettori molto corti.
È per questo che il codice R ha un aspetto diverso da Python o JavaScript: le operazioni per cui altrove scriveresti un ciclo si applicano a interi vettori in un'unica espressione. Ci arriviamo più avanti.
Una regola da interiorizzare subito: tutti gli elementi di un vettore hanno lo stesso tipo. Se passi a c() un miscuglio, non si lamenta: converte tutto in silenzio nel tipo più flessibile presente:
Una stringa nel miscuglio trasforma tutto in stringhe ("1", "TRUE"). I valori logici in mezzo ai numeri diventano 1 e 0. Questa coercizione segue una gerarchia fissa, logical → integer → double → character, e avviene senza alcun avviso: ecco perché un "7" capitato per caso in una colonna di numeri può trasformare in silenzio l'intero dataset in testo. La gerarchia completa è spiegata in tipi di dati; se ti serve contenere valori davvero misti, a questo servono le liste.
Generare sequenze: :, seq() e rep()
Scrivere ogni elemento dentro c() stanca presto. R ha tre scorciatoie per gli schemi regolari:
1:10è quella di tutti i giorni: interi da un estremo all'altro, con passo 1.seq()la generalizza: scegli il passo conby =, oppure indica quanti elementi vuoi conlength.out =e lascia che R calcoli la spaziatura.seq_len(n)produce i numeri da1aned è il modo sicuro di costruire gli indici di un ciclo: a differenza di1:n, quandonè 0 dà correttamente un vettore vuoto invece di contare all'indietro1 0.rep()ripete:times =ripete l'intero vettore da capo a fondo (1 2 1 2 1 2),each =ripete ogni elemento al suo posto (1 1 1 2 2 2). È facile confonderli; esegui l'esempio e confronta.
Indicizzazione: R conta da 1
Le parentesi quadre estraggono gli elementi. Il primo elemento è [1], non [0]. Se arrivi da Python o JavaScript, questo è in assoluto l'inciampo iniziale più comune:
Tre cose su cui vale la pena fermarsi:
- Puoi indicizzare con un vettore di posizioni:
fruits[c(2, 4)]prende il secondo e il quarto in un colpo solo. - Gli indici negativi significano "tutto tranne".
fruits[-1]è il vettore senza il primo elemento. È completamente diverso da Python, dove-1indica l'ultimo elemento. Non puoi mescolare indici positivi e negativi nella stessa chiamata. fruits[0]non è un errore: restituisce uncharacter(0)vuoto. In R un errore di uno spesso produce risultati vuoti silenziosi invece di un crash, quindi conviene controllarelength()quando qualcosa più avanti sembra misteriosamente vuoto.
Gli elementi possono anche avere dei nomi, e questo ti dà un terzo modo di indicizzare, per etichetta:
I vettori con nomi funzionano come una piccola tabella di ricerca e mantengono il codice leggibile: prices["tea"] dice quello che intende, prices[2] no.
Maschere logiche e which()
Il modo più potente di indicizzare è con un vettore logico, una maschera di valori TRUE/FALSE lunga quanto i dati. Qualsiasi confronto su un vettore produce esattamente questo:
temps > 24 non dà una risposta: ne dà cinque, una per elemento. Mettendo quella maschera dentro [ ] tieni gli elementi in cui la maschera è TRUE. Combina le condizioni con & (e) e | (o), spiegati insieme al resto degli operatori.
which() traduce una maschera in posizioni: qui 2 3 5, gli indici delle temperature alte. Usalo quando ti serve sapere dove sono le corrispondenze, per riportarle o per indicizzare un altro vettore negli stessi punti. Per filtrare e basta, temps[temps > 24] è più diretto di temps[which(temps > 24)].
Questo schema del filtro con una maschera è la spina dorsale di praticamente tutto il lavoro sui dati in R, e lo ritroverai subito quando filtrerai le righe di un data frame.
Calcolo vettorizzato e regola del riciclo
In R l'aritmetica si applica elemento per elemento su interi vettori, senza bisogno di cicli:
La prima riga è la caratteristica principale: prices * 2 raddoppia ogni elemento. Dove altri linguaggi hanno bisogno di un ciclo for, a R basta un operatore, e la forma vettorizzata è sia più breve sia più veloce, perché il ciclo avviene sotto, in C ottimizzato. Scrivi un ciclo for quando ogni passo dipende dal precedente; per i calcoli elemento per elemento, vettorizza.
La terza riga mostra la regola del riciclo: quando i vettori hanno lunghezze diverse, R ripete quello più corto per pareggiarli. c(10, 20) viene riciclato in 10 20 10 20, e il risultato è 11 22 13 24. Riciclare 2 su un vettore di lunghezza 3 è esattamente ciò che ha fatto funzionare prices * 2: uno scalare è solo un vettore di lunghezza 1 che viene riciclato.
Il riciclo è pulito quando la lunghezza maggiore è un multiplo esatto di quella minore. Quando non lo è, R calcola comunque un risultato, ma emette un avviso:
Ottieni 11 22 13 più un avviso che dice che la lunghezza dell'oggetto più lungo non è un multiplo della lunghezza di quello più corto. Considera quell'avviso una segnalazione di bug: un riciclo parziale non è quasi mai quello che intendevi, e di solito significa che due vettori che dovevano avere la stessa lunghezza non ce l'hanno.
Ordinare, invertire, togliere i duplicati
Tre piccole utilità che userai di continuo:
sort() ordina i valori, rev() inverte l'ordine esistente senza ordinare e unique() elimina i duplicati tenendo le prime occorrenze. Nessuna di queste modifica x: come quasi tutto in R, restituiscono un nuovo vettore e lasciano intatto l'originale.
Cosa ti porti a casa
- Il vettore è l'unità fondamentale di R: ordinato, con un solo tipo per tutti gli elementi, e anche i valori singoli sono vettori di lunghezza 1.
- Costruiscili con
c(), genera schemi con:,seq()erep(), e ricorda chec()converte in silenzio i tipi misti. - L'indicizzazione parte da 1; gli indici negativi tolgono elementi; i nomi ti permettono di indicizzare per etichetta.
- Le maschere logiche (
x[x > 5]) sono il modo di filtrare di tutto il linguaggio;which()trasforma una maschera in posizioni. - Il calcolo è vettorizzato, i vettori più corti vengono riciclati, e un avviso di riciclo parziale significa che hai un bug.
Prossimo passo: le liste, il contenitore per quando i tuoi valori non condividono tutti lo stesso tipo.
Domande frequenti
Come si crea un vettore in R?
Con la funzione c() (abbreviazione di combine): x <- c(10, 20, 30). Per le sequenze regolari usa 1:10, seq(0, 1, by = 0.25) o rep(0, 5). Tutti gli elementi devono finire per avere lo stesso tipo: se mescoli i tipi, R li converte tutti in silenzio in quello più flessibile.
In R l'indicizzazione parte da 0 o da 1?
Da 1. x[1] è il primo elemento, x[length(x)] è l'ultimo. Questo fa inciampare chiunque arrivi da Python, JavaScript o C: non esiste un elemento x[0] (chiederlo restituisce un vettore vuoto, non un errore, e così il bug resta silenzioso).
Cosa fa la regola del riciclo in R?
Quando fai calcoli su due vettori di lunghezza diversa, R ripete quello più corto finché non raggiunge la lunghezza di quello più lungo. c(1, 2, 3, 4) + c(10, 20) dà 11 22 13 24. Se la lunghezza maggiore non è un multiplo di quella minore, R calcola comunque un risultato ma emette un avviso: considera quell'avviso un bug.
Cosa fa which() in R?
Converte un vettore logico nelle posizioni dei suoi valori TRUE. Se x > 5 dà FALSE TRUE TRUE, allora which(x > 5) dà 2 3. Usalo quando ti servono proprio le posizioni; per filtrare e basta, indicizzare direttamente con il vettore logico (x[x > 5]) è più semplice.