Come sostituire i valori mancanti (NA) in R: na.omit e na.rm

โšก Riepilogo intelligente

La sezione "Sostituire i valori mancanti in R" illustra come individuare i valori NA, rimuovere le righe incomplete con `na.omit()` e imputarli con la media o la mediana tramite `mutate()`. Questa guida utilizza il dataset Titanic, in cui sia l'etร  che la tariffa presentano valori mancanti.

  • ๐Ÿ”Ž Rilevamento prima di tutto: colSums(is.na(df)) conta i valori mancanti per colonna prima di decidere come gestirli.
  • ๐Ÿ—‘๏ธ Rimozione delle file: La funzione na.omit() elimina tutte le righe contenenti valori NA, riducendo il numero di righe del database del Titanic da 1,309 a 1,045.
  • ๐Ÿ“ Imputazione media: Il metodo apply() con na.rm = TRUE calcola la media della colonna, mentre mutate() con ifelse() la scrive in una nuova colonna.
  • ๐Ÿ“Š Alternativa mediana: La mediana resiste ai valori anomali, il che la rende la scelta piรน sicura per variabili asimmetriche come le tariffe.
  • โšก Imputazione di massa: Le funzioni sapply() o across() applicano la stessa regola a ogni colonna numerica in un'unica istruzione.
  • โš ๏ธ Compromesso noto: L'imputazione della media riduce la varianza e indebolisce le correlazioni, quindi non dovrebbe mai essere applicata indiscriminatamente.

Sostituire i valori mancanti NA in R

Cosa sono i valori mancanti in R?

I valori mancanti compaiono quando un'osservazione non ha un valore registrato in una colonna, oppure quando un segnaposto non numerico occupa il posto di un numero. Devono essere rimossi o sostituiti prima di qualsiasi calcolo, perchรฉ la maggior parte delle funzioni di R restituisce NA non appena ne viene rilevato uno.

Questo tutorial mostra come gestire i valori mancanti con la libreria dplyr, parte dell'ecosistema tidyverse per l'analisi dei dati.

Sostituisci i valori mancanti in R

Il primo passo รจ sempre quello di scoprire quanti valori mancano e dove.

Come rilevare e contare i valori mancanti in R

Prima di decidere come gestire i valori mancanti, รจ necessario sapere quanti ce ne sono e dove si trovano. R offre quattro controlli, da una semplice risposta sรฌ/no a un conteggio completo per colonna.

# 1. Is there any NA at all?
anyNA(df_titanic)

# 2. How many in total?
sum(is.na(df_titanic))

# 3. How many per column, the most useful view
colSums(is.na(df_titanic))

# 4. Percentage missing per column
round(colMeans(is.na(df_titanic)) * 100, 1)

In pratica, la funzione da utilizzare รจ `colSums()`. Restituisce un vettore denominato con un conteggio per colonna, che mostra immediatamente se una variabile ha pochi valori mancanti o รจ perlopiรน vuota.

Conteggio delle righe complete. La funzione complete.cases() restituisce TRUE per le righe senza valori mancanti, il che indica in anticipo quanti dati verrebbero scartati dalla funzione na.omit():

sum(complete.cases(df_titanic))       # rows that would survive
sum(!complete.cases(df_titanic))      # rows that would be dropped

Un avvertimento riguardo ai segnaposto. R riconosce solo i valori NA. I set di dati spesso codificano i valori mancanti come una stringa vuota, uno spazio, "N/A", "-" o un numero sentinella come -99 o 999. Questi valori superano tutti i controlli sopra descritti senza essere rilevati. Convertili durante l'importazione in modo che il resto del flusso di lavoro funzioni correttamente:

df <- read.csv(PATH, na.strings = c("", " ", "NA", "N/A", "-", "-99"))

Dopo l'importazione, controllate sempre l'intervallo di ogni colonna numerica. Un'etร  di -99 o una tariffa di 9999 sono molto piรน pericolose di un onesto valore NA, perchรฉ nessuna funzione vi avviserร  in caso di problemi.

Tipi di dati mancanti: MCAR, MAR e MNAR

Il motivo per cui un valore รจ mancante determina se รจ sicuro imputarlo. Gli statistici riconoscono tre meccanismi.

  • MCAR, mancante completamente in modo casuale. La probabilitร  di essere mancante non รจ correlata a nulla, osservato o meno, ad esempio un sensore che si guasta in momenti casuali. Cadutaping oppure l'imputazione di queste righe non introduce alcuna distorsione, solo una perdita di precisione.
  • MAR, mancante in modo casuale. La probabilitร  dipende da altre variabili osservate, ma non dal valore mancante in sรฉ. Se fosse meno probabile che l'etร  dei passeggeri piรน anziani venisse registrata, l'etร  sarebbe MAR (Missing At Random) date le altre colonne. L'imputazione che utilizza tali colonne gestisce bene questo caso.
  • MNAR, mancante non a caso. La probabilitร  dipende dal valore non osservato stesso, ad esempio i percettori di redditi elevati che si rifiutano di dichiarare il proprio reddito. Nessun metodo di imputazione puรฒ risolvere questo problema basandosi solo sui dati, e la mancanza di dati in sรฉ contiene informazioni che vale la pena registrare in una colonna di flag.

L'imputazione della media, come utilizzata in questo tutorial, รจ giustificabile solo in presenza di MCAR e MAR semplice. Anche in questi casi, presenta un costo noto: riempire ogni lacuna con lo stesso numero riduce la varianza della colonna e ne indebolisce la correlazione con tutto il resto. Per lavori seri, รจ consigliabile utilizzare un metodo basato su modelli, come il pacchetto mice, e mantenere sempre una colonna di flag che indichi quali valori sono stati imputati.

mutare()

mutate() รจ il dplyr Verbo che crea una nuova variabile o sovrascrive una variabile esistente, il che lo rende lo strumento ideale per costruire una copia pulita di una colonna.

Procederemo in due parti. Impareremo come:

  • escludere i valori mancanti da un frame di dati
  • imputare i valori mancanti alla media e alla mediana

Il verbo mutate() รจ molto facile da usare. Possiamo creare una nuova variabile seguendo questa sintassi:

mutate(df, name_variable_1 = condition, ...)
arguments:
-df: Data frame used to create a new variable
-name_variable_1: Name and the formula to create the new variable
-...: No limit constraint. Possibility to create more than one variable inside mutate()

Escludi i valori mancanti (NA)

`na.omit()` รจ una funzione base di R, non un verbo di dplyr, ma funziona comunque correttamente. Elimina tutte le righe che contengono almeno un valore NA. Questa รจ l'opzione piรน veloce, ma raramente la migliore, perchรฉ un singolo valore mancante comporta l'eliminazione dell'intera osservazione.

Per affrontare il problema delle osservazioni mancanti, utilizzeremo il set di dati Titanic. In questo set di dati abbiamo accesso alle informazioni dei passeggeri a bordo durante la tragedia. Questo set di dati ha molte NA di cui occuparsi.

Scarica il file CSV da internet, quindi elenca le colonne che contengono NA:

PATH <- "https://raw.githubusercontent.com/guru99-edu/R-Programming/master/test.csv"
df_titanic <- read.csv(PATH, sep = ",")
# Return the column names containing missing observations
list_na <- colnames(df_titanic)[ apply(df_titanic, 2, anyNA) ]
list_na

Produzione:

## [1] "age"  "fare"

Qui,

colnames(df_titanic)[apply(df_titanic, 2, anyNA)]

restituisce i nomi delle colonne che contengono almeno un valore mancante.

Nelle colonne etร  e tariffa mancano valori.

Possiamo eliminarli con na.omit().

library(dplyr)
# Exclude the missing observations
df_titanic_drop <-df_titanic %>%
na.omit()		
dim(df_titanic_drop)

Produzione:

## [1] 1045   13

Il nuovo set di dati contiene 1045 righe rispetto alle 1309 del set di dati originale.

Escludi valori mancanti

Imputare i dati mancanti con la media e la mediana

รˆ anche possibile imputare, ovvero riempire, i valori mancanti con la media o la mediana. Una buona pratica consiste nel creare due variabili separate per la media e la mediana. Una volta create, possiamo sostituire i valori mancanti con le variabili appena formate.

Utilizzeremo il metodo apply per calcolare la media della colonna con NA. Vediamo un esempio

Passo 1) In precedenza nel tutorial, abbiamo archiviato il nome delle colonne con i valori mancanti nell'elenco chiamato list_na. Utilizzeremo questo elenco

Passo 2) Calcola la media con l'argomento na.rm = TRUE. Questo argomento รจ obbligatorio perchรฉ le colonne contengono dati mancanti e indica a R di ignorarli.

# Create mean
average_missing <- apply(df_titanic[,colnames(df_titanic) %in% list_na],
      2,
      mean,
      na.rm =  TRUE)
average_missing

Code Spiegazione:

Passiamo 4 argomenti nel metodo apply.

  • df: df_titanic[,colnames(df_titanic) %in% list_na]. Questo codice restituirร  il nome delle colonne dall'oggetto list_na (ad esempio "age" e "fare")
  • 2: Calcola la funzione sulle colonne
  • media: calcola la media
  • na.rm = TRUE: ignora i valori mancanti

Produzione:

##      age     fare 
## 29.88113 33.29548

Abbiamo creato con successo la media delle colonne contenenti le osservazioni mancanti. Questi due valori verranno utilizzati per sostituire le osservazioni mancanti.

Passo 3) Sostituisci i valori NA

Il verbo mutate dalla libreria dplyr รจ utile per creare una nuova variabile. Non vogliamo necessariamente modificare la colonna originale, quindi possiamo creare una nuova variabile senza NA. mutate รจ facile da usare, basta scegliere un nome di variabile e definire come creare questa variabile. Ecco il codice completo

# Create a new variable with the mean and median
df_titanic_replace <- df_titanic %>%
   mutate(replace_mean_age  = ifelse(is.na(age), average_missing[1], age),
   replace_mean_fare = ifelse(is.na(fare), average_missing[2], fare))

Code Spiegazione:

Creiamo due variabili, replace_mean_age e replace_mean_fare come segue:

  • replace_mean_age = ifelse(is.na(age), Average_missing[1], age)
  • replace_mean_fare = ifelse(is.na(tariffa), Average_missing[2],tariffa)

Se la colonna etร  presenta valori mancanti, sostituiscili con il primo elemento di Average_missing (media dell'etร ), altrimenti mantieni i valori originali. Stessa logica per la tariffa

sum(is.na(df_titanic_replace$age))

Produzione:

## [1] 263

Dopo la sostituzione, la nuova colonna non contiene alcun valore mancante:

sum(is.na(df_titanic_replace$replace_mean_age))

Produzione:

## [1] 0

La colonna "etร " originale contiene 263 valori mancanti, mentre la nuova colonna "replace_mean_age" li ha riempiti tutti con l'etร  media.

Passo 4) Possiamo anche sostituire le osservazioni mancanti con la mediana.

median_missing <- apply(df_titanic[,colnames(df_titanic) %in% list_na],
      2,
      median,
      na.rm =  TRUE)
df_titanic_replace <- df_titanic %>%
            mutate(replace_median_age  = ifelse(is.na(age), median_missing[1], age), 
            replace_median_fare = ifelse(is.na(fare), median_missing[2], fare))
head(df_titanic_replace)

Produzione:

Assegnare i dati mancanti alla media e alla mediana

Passo 5) Su un set di dati ampio, il metodo passo passo diventa tedioso. La funzione sapply() riduce l'intera procedura a un'unica istruzione, a costo di non visualizzare mai i valori imputati.

sapply non crea un file Frame dati, quindi possiamo racchiudere la funzione sapply() all'interno di data.frame() per creare un oggetto frame di dati.

# Quick code to replace missing values with the mean
df_titanic_impute_mean <- data.frame(
    sapply(
        df_titanic,
        function(x) ifelse(is.na(x),
            mean(x, na.rm = TRUE),
            x)))

Imputazione moderna con replace_na() e across()

Gli approcci apply() e sapply() descritti sopra funzionano ancora, ma tidyr e dplyr ora esprimono le stesse operazioni in modo piรน sicuro e leggibile.

replace_na() per valori fissi. tidyr::replace_na() accetta un elenco denominato di colonne e le relative sostituzioni:

library(tidyr)

df_titanic %>%
    replace_na(list(age = 29.88, fare = 33.30))

across() per ogni colonna numerica. Questa รจ la sostituzione diretta e sicura dal punto di vista dei tipi per la riga singola sapply(), e a differenza di sapply() non modifica mai le colonne di tipo carattere o fattore:

library(dplyr)

# Mean-impute every numeric column
df_titanic %>%
    mutate(across(where(is.numeric),
                  ~ ifelse(is.na(.x), mean(.x, na.rm = TRUE), .x)))

# Median-impute instead
df_titanic %>%
    mutate(across(where(is.numeric),
                  ~ ifelse(is.na(.x), median(.x, na.rm = TRUE), .x)))

โš ๏ธ Perchรฉ la scorciatoia sapply() รจ rischiosa: l'esempio sapply() su una sola riga viene eseguito ogni colonna, comprese quelle di tipo carattere e fattore. Chiamare mean() su queste restituisce NA con un avviso, e sapply() converte quindi l'intero risultato in carattere. La versione across(where(is.numeric), โ€ฆ) sopra evita completamente questo problema.

coalesce() per le colonne di fallback. Quando una seconda colonna puรฒ fornire il valore mancante, coalesce() restituisce la prima voce non mancante tra i suoi argomenti:

df %>% mutate(age_final = coalesce(age, age_estimated, 0))

Tieni traccia di ciรฒ che hai modificato. Aggiungi un flag prima dell'imputazione, in modo che qualsiasi modello successivo possa apprendere dal fatto che un valore era mancante:

df_titanic %>%
    mutate(age_was_missing = is.na(age),
           age = ifelse(is.na(age), mean(age, na.rm = TRUE), age))

Gestione dei valori mancanti in R: Riferimento al metodo

In questo tutorial vengono illustrati tre approcci:

  • Escludere tutte le osservazioni mancanti
  • Imputare con la media
  • Imputare con la mediana

La tabella seguente riassume le procedure di rilevamento e rimozione:

Biblioteca Obiettivo Code
base Elenca le osservazioni mancanti
colnames(df)[apply(df, 2, anyNA)]
base Rimuovi tutte le righe contenenti NA
na.omit(df)

L'imputazione con media o mediana puรฒ essere effettuata in due modi

  • Usando applica
  • Utilizzando sapply
Metodo Dettagli Vantaggi Svantaggi
Passo dopo passo con l'applicazione Controlla le colonne mancanti, calcola la media/mediana, memorizza il valore, sostituisci con mutate() รˆ possibile visualizzare la media o la mediana imputata Piรน tempo di esecuzione. Puรฒ essere lento con set di dati di grandi dimensioni
Modo rapido con Sapply Utilizza sapply() e data.frame() per cercare e sostituire automaticamente i valori mancanti con media/mediana Codice breve e veloce I valori imputati non vengono mai mostrati

DOMANDE FREQUENTI

NA indica un valore mancante all'interno di un vettore. NULL rappresenta l'assenza di un oggetto e ha lunghezza zero. NaN รจ il risultato di un'operazione numerica non definita, come la divisione di zero per zero.

Per variabili con distribuzione asimmetrica, come tariffe o redditi, si utilizza la mediana, poichรฉ i valori anomali tendono a far salire la media. La media va utilizzata solo quando la colonna รจ approssimativamente simmetrica e priva di valori estremi.

Riempire ogni spazio vuoto con lo stesso valore riduce la varianza della colonna e ne indebolisce la correlazione con le altre variabili. I metodi basati su modelli, come il pacchetto mice, preservano queste relazioni in modo molto piรน efficace.

La maggior parte degli algoritmi non accetta valori mancanti (NA) e genera un errore o ignora silenziosamente le righe. Un'imputazione eseguita in modo approssimativo puรฒ causare la perdita di informazioni tra i set di addestramento e di test, pertanto รจ fondamentale calcolare sempre i valori di imputazione esclusivamente sul set di addestramento.

Sรฌ. Gli assistenti IA possono suggerire metodi basati sul modello di dati mancanti e generare il codice dplyr. Verifica la scelta confrontandola con l'output di colSums(is.na()) e con la tua conoscenza del dominio relativa al motivo per cui i dati sono mancanti.

Riassumi questo post con: