Visualizzazione post con etichetta tabelle. Mostra tutti i post
Visualizzazione post con etichetta tabelle. Mostra tutti i post

domenica 5 marzo 2023

Gestione dei dati mancanti

Per illustrare il tema, che è strettamente collegato all'ordinamento dei dati [1], vediamo tre cose:
→ come individuare i dati mancanti;
→ come i dati mancanti - identificati in R con la sigla NA (Not Available) - possono bloccare l'esecuzione di calcoli sulle variabili numeriche che li contengono e come in questo caso sia possibile omettere selettivamente dai calcoli i dati NA
→ come al bisogno sia possibile eliminare per intero da un vettore o matrice o tabella [2] i casi con dati NA.

Per proseguire dovete, seguendo le istruzioni fornite alla pagina Dati:
effettuare il download del file importa_csv.csv
salvare il file nella cartella C:\Rdati\

In alternativa copiate le otto righe riportate qui sotto, incollatele in un editor di file di testo aggiungendo un ↵ Invio al termine dell'ultima riga, e salvate il tutto in C:\Rdati\ in un file di testo denominato importa_csv.csv (attenzione: assicuratevi che il file sia effettivamente salvato con l'estensione .csv):

id;sesso;anni;peso_kg;altezza_m
MT;M;69;76;1,78
GF;F;56;63;
MC;F;53;71;1,60
SB;M;28;73;1,78
FE;F;61;54;1,54
AB;M;46;92;1,84
RF;F;31;81;1,56

Vediamo come individuare i dati mancanti. Copiate e incollate nella Console di R questo script e premete ↵ Invio:

# IDENTIFICA E CONTEGGIA I DATI MANCANTI 
#
# importa i dati, notare / invece di \ su windows
mydata <- read.table("C:/Rdati/importa_csv.csv", header=TRUE, sep=";", dec=",", row.names="id")
#
mydata # mostra i dati importati
#
is.na(mydata) # identifica i dati mancanti
#
colSums(is.na(mydata)) # conteggia i dati mancanti per colonna/variabile
#
which(is.na(mydata$altezza_m)) # identifica la posizione dei dati mancanti
#

Dopo avere importato i dati, richiamando il nome dell'oggetto mydata che li contiene questi sono mostrati con la sigla NA riportata automaticamente da R in corrispondenza di ciascuno dei dati mancanti.

> mydata # mostra i dati importati
   sesso anni peso_kg altezza_m
MT     M   69      76      1.78
GF     F   56      63        NA
MC     F   53      71      1.60
SB     M   28      73      1.78
FE     F   61      54      1.54
AB     M   46      92      1.84
RF     F   31      81      1.56

Nel caso di piccole tabelle come questa non serve altro: ma nel caso di estesi database ci servono funzioni che forniscano qualche soluzione più pratica e immediata. 

Impieghiamo quindi la funzione is.na() che identifica nella tabella mydata i dati mancanti riportando TRUE in corrispondenza di ciascuno di essi 

> is.na(mydata) # identifica i dati mancanti
   sesso  anni peso_kg altezza_m
MT FALSE FALSE   FALSE     FALSE
GF FALSE FALSE   FALSE      TRUE
MC FALSE FALSE   FALSE     FALSE
SB FALSE FALSE   FALSE     FALSE
FE FALSE FALSE   FALSE     FALSE
AB FALSE FALSE   FALSE     FALSE
RF FALSE FALSE   FALSE     FALSE

I dati della tabella mydata trasformati nei corrispettivi valori logici con la funzione is.na() possono allora essere impiegati come argomento della funzione colSums() che effettua il conteggio dei dati mancanti per ciascuna colonna/variabile

colSums(is.na(mydata)) # conteggia i dati mancanti per colonna/variabile
    sesso      anni   peso_kg altezza_m 
        0         0         0         1 

A questo punto abbiamo individuato in altezza_m la variabile con i dati mancanti e abbiamo conteggiato il loro numero

Non ci resta quindi che impiegare i dati della variabile mydata$altezza_m trasformati nei corrispettivi valori logici con is.na() come argomento della funzione which() per identificare la posizione dei  dati mancanti

> which(is.na(mydata$altezza_m)) # identifica la posizione dei dati mancanti
[1] 2

che nel nostro caso risultano essere il dato della riga numero 2 della variabile mydata$altezza_m.

Per l'effetto che i dati mancanti possono determinare sui calcoli copiate e incollate nella Console di R questo script e premete ↵ Invio:

# ESEMPIO DI MEDIA ARITMETICA NON COMPUTABILE A CAUSA DI DATI MANCANTI 
#
# importa i dati, notare / invece di \ su windows
mydata <- read.table("C:/Rdati/importa_csv.csv", header=TRUE, sep=";", dec=",", row.names="id")
#
mydata # mostra i dati importati
#
mean(mydata$peso_kg) # calcola la media aritmetica dei valori di peso
mean(mydata$altezza_m) # calcola la media aritmetica dei valori di altezza
#

Il risultato che compare nella Console di R è il seguente:

> mydata # mostra i dati importati 
   sesso anni peso_kg altezza_m
MT     M   69      76      1.78
GF     F   56      63        NA
MC     F   53      71      1.60
SB     M   28      73      1.78
FE     F   61      54      1.54
AB     M   46      92      1.84
RF     F   31      81      1.56
> #
> mean(mydata$peso_kg) # calcola la media aritmetica dei valori di peso 
[1] 72.85714
> mean(mydata$altezza_m) # calcola la media aritmetica dei valori di altezza 
[1] NA

Come potete vedere il calcolo della media effettuato con la funzione mean() ha avuto successo solamente per il peso. Per l'altezza in luogo del valore della media è stata riportata la sigla NA, evidente conseguenza del valore mancante dell'altezza nel caso GF.

Copiate e incollate nella Console di R queste due ulteriori righe di codice e premete ↵ Invio:

# la corretta gestione dei dati mancanti consente di calcolare la media dei valori di altezza 
#
mean(mydata$peso_kg) # ricalcola la media aritmetica dei valori di peso
mean(mydata$altezza_m, na.rm=TRUE) # ricalcola la media aritmetica dei valori di altezza
#

Il risultato dei calcoli della media che compare nella Console di R ora è questo:

> mean(mydata$peso_kg) # ricalcola la media aritmetica dei valori di peso
[1] 72.85714
> mean(mydata$altezza_m, na.rm=TRUE) # ricalcola la media aritmetica dei valori di altezza
[1] 1.683333

Quindi con l'aggiunta nella funzione mean() dell'argomento na.rm=TRUE, che rimuove dal calcolo i dati mancanti (in questo caso uno solo, ma potrebbero essere anche molti), è stato reso possibile il calcolo della media anche per l'altezza.

Con la funzione na.omit() è infine possibile eliminare definitivamente i casi con dati mancanti: anche un solo dato mancante/campo vuoto comporta la cancellazione dell'intera riga/caso. Copiate e incollate nella Console di R questo script e premete ↵ Invio:

# ELIMINA I CASI CON DATI MANCANTI 
#
# importa i dati, notare / invece di \ su windows
mydata <- read.table("C:/Rdati/importa_csv.csv", header=TRUE, sep=";", dec=",", row.names="id")
#
mydata # mostra i dati importati
#
newdata <- na.omit(mydata) # elimina da mydata i casi con dati mancanti
newdata # mostra i dati dopo eliminazione dei casi con dati mancanti
#
mean(newdata$peso_kg) # calcola la media aritmetica dei valori di peso
mean(newdata$altezza_m) # calcola la media aritmetica dei valori di altezza
#

Dai dati importati nella tabella mydata mediante la funzione na.omit() viene generata una nuova tabella denominata newdata dalla quale sono esclusi i casi con dati mancanti. Con l'eliminazione dalla tabella del caso GF i calcoli vanno subito a buon fine sia per il peso sia per l'altezza.

> mydata # mostra i dati importati 
   sesso anni peso_kg altezza_m
MT     M   69      76      1.78
GF     F   56      63        NA
MC     F   53      71      1.60
SB     M   28      73      1.78
FE     F   61      54      1.54
AB     M   46      92      1.84
RF     F   31      81      1.56
> #
> newdata <- na.omit(mydata) # elimina da mydata i casi con dati mancanti
> newdata # mostra i dati dopo eliminazione dei casi con dati mancanti
   sesso anni peso_kg altezza_m
MT     M   69      76      1.78
MC     F   53      71      1.60
SB     M   28      73      1.78
FE     F   61      54      1.54
AB     M   46      92      1.84
RF     F   31      81      1.56
> #  
> mean(newdata$peso_kg) # calcola la media aritmetica dei valori di peso 
[1] 74.5
> mean(newdata$altezza_m) # calcola la media aritmetica dei valori di altezza 
[1] 1.683333

Da notare che impiegando la funzione na.omit() la media dei valori di peso, che con lo script precedente era 72.85714 kg, ora è cambiata ed è diventata 74.5 kg in quanto in seguito all'eliminazione dell'intero caso GF è stato eliminato anche il suo valore di peso 63 kg che contribuiva a determinare la media di quest'ultimo.


----------

[1] Vedere il post Ordinamento dei dati.

[2] Parliamo di array o vettore nel caso di dati numerici monodimensionali, disposti su una sola riga, 

8
6
11
7

di matrice nel caso di dati numerici disposti su più righe e più colonne

8
9
15
14
6
7
18
12
11
8
17
13
7
4
19
17

e di tabella nei casi in cui il contenuto, disposto su più righe e più colonne, è rappresentato oltre che da dati numerici, anche da testo e/o operatori logici

M
7
9
VERO
F
3
12
VERO
F
5
10
FALSO


sabato 18 febbraio 2023

Ordinamento dei dati

Ordinare i dati è una attività semplice e sovente necessaria per la quale R offre molte soluzioni.

Qui vediamo le funzioni di ordinamento contenute nel pacchetto base di R - il pacchetto di funzioni statistiche e grafiche che viene installato con il programma R - e le funzioni contenute nei due pacchetti aggiuntivi dplyr e data.table, che sono quelle più frequentemente utilizzate. I due pacchetti aggiuntivi devono ovviamente essere scaricati e installati dal CRAN.

Per proseguire è necessario:
→ effettuare il download del file di dati peso_altezza.csv
→ salvare il file nella cartella C:\Rdati\

Per il file di dati trovate link e modalità di download alla pagina Dati, ma potete anche semplicemente copiare i dati riportati qui sotto aggiungendo un ↵ Invio al termine dell'ultima riga e salvarli in C:\Rdati\ in un file di testo denominato peso_altezza.csv (assicuratevi che il file sia effettivamente salvato in formato testo e con l'estensione .csv).

id;sesso;anni;peso_kg;altezza_m
MT;M;69;76;1,78
GF;F;56;63;
MC;F;53;71;1,60
SB;M;28;73;1,78
FE;F;61;54;1,54
AB;M;46;92;1,84
RF;F;31;81;1,56

Iniziamo con la funzione order() contenuta nel pacchetto base precisando subito che con questa funzione la tabella originaria viene sì mostrata nell'ordine desiderato ma non viene modificata, e il nuovo ordine si applica salvando i dati ordinati in una tabella che:
→  può avere lo stesso nome della tabella originaria, nel qual caso questa viene sovrascritta e quindi risulterà effettivamente modificata;
→ può avere un nuovo nome, nel qual caso la tabella originaria resta immodificata, come facciamo qui (noi che vogliamo essere prudenti). 

Vediamo la cosa con questo semplice script, incollatelo nella Console di R e premete ↵ Invio:

# ORDINAMENTO DATI 
#
mydata <- read.table("c:/Rdati/peso_altezza.csv", header=TRUE, sep=";", dec=",") # importa i dati 
mydata # mostra la tabella originaria
#
mydata[order(mydata$id),] # ordina e mostra la tabella ordinata
#
mydata # la tabella originaria resta immodificata
#
newdata <- mydata[order(mydata$id),] # ordina e salva in una nuova tabella
#
newdata # mostra la nuova tabella
#

Ecco quindi cosa accade, questa è la tabella originaria mydata importata:

> mydata # mostra la tabella originaria
  id sesso anni peso_kg altezza_m
1 MT     M   69      76      1.78
2 GF     F   56      63        NA
3 MC     F   53      71      1.60
4 SB     M   28      73      1.78
5 FE     F   61      54      1.54
6 AB     M   46      92      1.84
7 RF     F   31      81      1.56

Con la funzione order() la tabella mydata viene ordinata in ordine crescente in base alla variabile id e viene mostrata nella Console di R:

> mydata[order(mydata$id),] # ordina e mostra la tabella ordinata
  id sesso anni peso_kg altezza_m
6 AB     M   46      92      1.84
5 FE     F   61      54      1.54
2 GF     F   56      63        NA
3 MC     F   53      71      1.60
1 MT     M   69      76      1.78
7 RF     F   31      81      1.56
4 SB     M   28      73      1.78

Tuttavia se verifichiamo la tabella originaria mydata richiamandone il nome vediamo che non è stata modificata:

> mydata # la tabella originaria resta immodificata
  id sesso anni peso_kg altezza_m
1 MT     M   69      76      1.78
2 GF     F   56      63        NA
3 MC     F   53      71      1.60
4 SB     M   28      73      1.78
5 FE     F   61      54      1.54
6 AB     M   46      92      1.84
7 RF     F   31      81      1.56

Per ottenere una tabella con i dati ordinati si ordina nuovamente la tabella mydata salvando il risultato (<-) in una nuova tabella che denominiamo newdata:

> newdata <- mydata[order(mydata$id),] # ordina e salva in una nuova tabella

Se ora verifichiamo il contenuto della nuova tabella newdata richiamandone il nome, vediamo che contiene i dati ordinati:

> newdata # mostra la nuova tabella
  id sesso anni peso_kg altezza_m
6 AB     M   46      92      1.84
5 FE     F   61      54      1.54
2 GF     F   56      63        NA
3 MC     F   53      71      1.60
1 MT     M   69      76      1.78
7 RF     F   31      81      1.56
4 SB     M   28      73      1.78

Una volta chiarito questo punto, molto importante dal punto di vista pratico, possiamo precedere con gli script. Copiate il primo, incollatelo nella Console di R e premete ↵ Invio:

# ORDINAMENTO ordina per nome della tabella$variabile
#
mydata <- read.table("c:/Rdati/peso_altezza.csv", header=TRUE, sep=";", dec=",") # importa i dati 
mydata # mostra la tabella originaria
#
mydata[order(mydata$peso_kg),] # ordina il peso in ordine crescente
#
mydata[order(-mydata$peso_kg),] # ordina il peso in ordine decrescente
#
mydata[order(mydata$sesso, -mydata$peso_kg),] # ordina il sesso in ordine crescente, il peso in ordine decrescente
#
mydata[order(mydata$id),] # ordina id in ordine crescente
#
mydata[order(-mydata$id),] # ordina id in ordine decrescente
#
mydata[order(-xtfrm(mydata$id)),] # ordina id in ordine decrescente
#
mydata[order(-xtfrm(mydata$sesso), mydata$peso_kg),] # ordina il sesso in ordine decrescente, il peso in ordine crescente
#
mydata[order(mydata$altezza_m, na.last=TRUE),] # ordina l'altezza in ordine crescente, NA in coda
#
mydata[order(mydata$altezza_m, na.last=FALSE),] # ordina l'altezza in ordine crescente, NA in testa
#   
mydata[order(-mydata$altezza_m, na.last=NA),] # ordina l'altezza in ordine decrescente, esclude NA
#  
mydata # la tabella originaria resta immodificata
#

I commenti riportati in ciascuna riga di codice sono di per sè esplicativi.

La cosa interessante accade quando si tenta di ordinare la variabile mydata$id in ordine decrescente con mydata[order(-mydata$id),], questo infatti è il risultato:

> mydata[order(-mydata$id),] # ordina id in ordine decrescente
Errore in -mydata$id : argomento non valido per l'operatore unario

L'errore è legato al fatto che R non ammette davanti a una variabile di tipo testo il segno meno (-) e viene corretto nella riga successiva con la funzione xtfrm() che nell'help è definita come "A generic auxiliary function that produces a numeric vector which will sort in the same order as x".

Questo è il risultato della nuova sintassi che corregge l'errore che impediva l'ordinamento in senso decrescente della variabile testo:

> mydata[order(-xtfrm(mydata$id)),] # ordina id in ordine decrescente
  id sesso anni peso_kg altezza_m
4 SB     M   28      73      1.78
7 RF     F   31      81      1.56
1 MT     M   69      76      1.78
3 MC     F   53      71      1.60
2 GF     F   56      63        NA
5 FE     F   61      54      1.54
6 AB     M   46      92      1.84

Come potete constatare dai risultati che vedete nella Console di R le ultime tre righe di codice illustrano come impiegare l'argomento na.last per controllare il trattamento dei dati mancanti (NA):
 se na.last=TRUE i dati mancanti sono identificati con NA e messi in coda ai dati;
 se na.last=FALSE i dati mancanti sono identificati con NA e posti in testa ai dati;
 se na.last=NA i dati mancanti sono rimossi;

Copiate questo secondo script, incollatelo nella Console di R e premete ↵ Invio:

# ORDINAMENTO ordina per nome della variabile
#
mydata <- read.table("c:/Rdati/peso_altezza.csv", header=TRUE, sep=";", dec=",") # importa i dati 
mydata # mostra la tabella originaria
attach(mydata) # consente di impiegare direttamente i nomi delle variabili di mydata
#
mydata[order(id),] # ordina id in ordine crescente
#
mydata[order(-xtfrm(id)),] # ordina id in ordine decrescente
#
mydata[order(-anni),] # ordina gli anni in ordine decrescente
#   
mydata[order(sesso, -altezza_m),] # ordina il sesso in ordine crescente, l'altezza in ordine crescente
#
mydata[order(-xtfrm(sesso), altezza_m),] # ordina il sesso in ordine decrescente, l'altezza in ordine decrescente
#
detach(mydata) # termina l'impiego diretto dei nomi delle variabili
#
mydata # la tabella originaria resta immodificata
#

Impieghiamo sempre la funzione order() contenuta nel pacchetto {base} ma risulta immediatamente evidente la differenza: nello script precedente le variabili erano indicate con nomedellatabella$nomedellavariabile, mentre ora dopo avere riportato nella seconda riga di codice

attach(mydata) 

possiamo fare riferimento alle variabili riportando semplicemente il nomedellavariabileMentre le regole di ordinamento rimangono ovviamente le stesse, alla fine dello script viene riportato

detach(mydata) 

per ripristinare la condizione di default [2].

Concludiamo quanto riguarda la funzione order() contenuta nel pacchetto {base} con questo terzo script, incollatelo nella Console di R e premete ↵ Invio:

# ORDINAMENTO ordina per numero della colonna
#
mydata <- read.table("c:/Rdati/peso_altezza.csv", header=TRUE, sep=";", dec=",") # importa i dati 
mydata # mostra la tabella originaria
#
mydata[order(mydata[,4]),] # ordina la colonna 4 in ordine crescente
#
mydata[order(-mydata[,4]),] # ordina la colonna 4 in ordine decrescente
#
mydata[order(mydata[,1]),] # ordina la colonna 1 in ordine crescente
#
mydata[order(-xtfrm(mydata[,1])),] # ordina la colonna 1 in ordine decrescente
#
mydata[order(mydata[,2], mydata[,5]),] # ordina la colonna 2 in ordine crescente, la colonna 5 in ordine crescente 
#
mydata[order(-xtfrm(mydata[,2]), -mydata[,5]),] # ordina la colonna 2 in ordine decrescente, la colonna 5 in ordine decrescente 
#
mydata # la tabella originaria resta immodificata
#

La differenza rispetto ai due primi script consiste, questa volta, semplicemente nell'ordinare la tabella impiegando il numero della colonna. 

Vediamo ora le regole per l'ordinamento previste dalla funzione arrange() del pacchetto dplyr.

Copiate questo script, incollatelo nella Console di R e premete ↵ Invio:

# ORDINAMENTO con il pacchetto "dplyr"
#
library(dplyr) # carica il pacchetto
mydata <- read.table("c:/Rdati/peso_altezza.csv", header=TRUE, sep=";", dec=",") # importa i dati 
mydata # mostra la tabella originaria
#
arrange(mydata, altezza_m) # ordina l'altezza in ordine crescente
#
arrange(mydata, desc(altezza_m)) # ordina l'altezza in ordine decrescente
#
arrange(mydata, sesso, desc(id)) # ordina il sesso in ordine crescente, id in ordine decrescente
#
mydata # la tabella originaria resta immodificata
#

Le cose da notare in merito alla funzione arrange() sono:
 viene specificato ogni volta il nome della tabella da ordinare, per cui il nome della variabile risulta abbreviato;
 i dati mancanti sono identificati con NA e messi in coda ai dati;
 l'ordinamento di una variabile testo in ordine discendente con l'opzione desc() non incontra il problema che avevamo riscontrato con la funzione order();
 la tabella originaria resta immodificata.

Passiamo infine a vedere le regole per l'ordinamento previste dalla funzione setorder() del pacchetto read.table.

Copiate questo script, incollatelo nella Console di R e premete ↵ Invio:

# ORDINAMENTO con il pacchetto "data.table"
#
library(data.table) # carica il pacchetto
mydata <- read.table("c:/Rdati/peso_altezza.csv", header=TRUE, sep=";", dec=",") # importa i dati 
mydata # mostra la tabella originaria
#
setorder(mydata, altezza_m) # ordina l'altezza in ordine crescente
mydata # mostra la tabella ordinata
#
setorder(mydata, -altezza_m) # ordina l'altezza in ordine decrescente
mydata # mostra la tabella ordinata
#
setorder(mydata, sesso, -id) # ordina il sesso in ordine crescente, id in ordine decrescente
mydata # mostra la tabella ordinata
#

Qui le cose da notare sono:
 contrariamente a quanto visto finora, con la funzione setorder() la tabella originaria viene realmente ordinata e inoltre:
 se si impiega l'argomento na.last=TRUE i dati mancanti sono identificati con NA e messi in coda ai dati;
 se si impiega l'argomento na.last=FALSE i dati mancanti sono identificati con NA e posti in testa ai dati;
 la funzione accetta per l'argomento na.last solamente i valori TRUE e FALSE con valore di default FALSE (che è quello qui esemplificato).

Conclusione: se volete evitare l'impiego di un pacchetto aggiuntivo, trovate nella funzione order() del pacchetto {base} che viene installato con il programma R tutte le modalità di ordinamento che si possono desiderare, anche se con qualche arzigogolo. Se invece vi va bene installare pacchetti aggiuntivi, la funzione arrange() e la funzione setorder(), con differenze veramente minime tra loro, offrono il vantaggio di impiegare una sintassi semplificata e più immediata. Il punto delicato è il fatto che  setorder() ordina realmente la tabella originaria, mentre potrebbe essere più prudente e anche più ordinato lasciare, come accade con arrange(), la tabella originaria immodificata e al bisogno salvare i dati ordinati in una tabella con un nome differente. Ma ovviamente si tratta di scelte personali e insindacabili.
   

----------

[1] Parliamo di array o vettore nel caso di dati numerici monodimensionali, disposti su una sola riga, 

8
6
11
7

di matrice nel caso di dati numerici disposti su più righe e più colonne

8
9
15
14
6
7
18
12
11
8
17
13
7
4
19
17

e di tabella nei casi in cui il contenuto, disposto su più righe e più colonne, è rappresentato oltre che da dati numerici, anche da testo e/o operatori logici

M
7
9
VERO
F
3
12
VERO
F
5
10
FALSO

[2] In termini un poco più tecnici: con la funzione attach(database) il database è collegato al percorso di ricerca di R, questo significa che quando si opera su una variabile R è informato del database che la contiene, e pertanto è possibile accedere alla variabile semplicemente fornendone il nome. Con la funzione detach(database) il database viene rimosso dal percorso di ricerca di R.