Kako zamijeniti nedostajuće vrijednosti (NA) u R: na.omit & na.rm

⚡ Pametni sažetak

Zamjena nedostajućih vrijednosti u R-u obuhvaća otkrivanje NA vrijednosti, uklanjanje nepotpunih redaka pomoću na.omit() i njihovo imputiranje sa srednjom vrijednošću ili medijanom pomoću mutate(). Ovaj vodič koristi skup podataka Titanic, gdje i age i fare nose nedostajuća opažanja.

  • 🔎 Prvo otkrivanje: colSums(is.na(df)) broji nedostajuće vrijednosti po stupcu prije donošenja bilo kakve odluke o tome kako ih obraditi.
  • 🗑️ Uklanjanje retka: na.omit() izbacuje svaki redak koji sadrži NA, smanjujući podatke Titanica s 1,309 redaka na 1,045.
  • 📐 Prosječna imputacija: apply() s na.rm = TRUE izračunava srednju vrijednost stupca, a mutate() s ifelse() zapisuje je u novi stupac.
  • 📊 Srednja alternativa: Medijan se opire ekstremnim vrijednostima, što ga čini sigurnijim izborom za iskrivljene varijable poput cijene prijevoza.
  • ⚡ Skupna imputacija: sapply() ili across() primjenjuje isto pravilo na svaki numerički stupac u jednoj naredbi.
  • ⚠️ Poznati kompromis: Imputacija srednje vrijednosti smanjuje varijancu i slabi korelacije, stoga se nikada ne smije primjenjivati ​​slijepo.

Zamijenite nedostajuće vrijednosti NA u R-u

Što su nedostajuće vrijednosti u R-u?

Nedostajuće vrijednosti pojavljuju se kada opažanje nema zabilježenu vrijednost u stupcu ili kada se nenumeričko rezervirano mjesto nalazi tamo gdje bi trebao biti broj. Moraju se ukloniti ili zamijeniti prije bilo kakvog izračuna jer većina R funkcija vraća NA u trenutku kada je jedan prisutan.

Ovaj tutorial pokazuje kako se nositi s nedostajućim vrijednostima pomoću biblioteke dplyr, dijela ekosustava tidyverse za analizu podataka.

Zamijenite nedostajuće vrijednosti u R

Prvi korak je uvijek saznati koliko vrijednosti nedostaje i gdje.

Kako otkriti i prebrojati nedostajuće vrijednosti u R-u

Prije nego što odlučite što učiniti s nedostajućim vrijednostima, morate znati koliko ih ima i gdje se nalaze. R nudi četiri provjere, od jednog odgovora da ili ne do potpunog zbrajanja po stupcu.

# 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)

U praksi se preporučuje colSums(). Vraća imenovani vektor s jednim brojem po stupcu, što odmah pokazuje nedostaje li varijabli nekoliko vrijednosti ili je uglavnom prazna.

Brojanje punih redaka. complete.cases() vraća TRUE za retke bez nedostajućih vrijednosti nigdje, što vam unaprijed govori koliko podataka bi na.omit() odbacio:

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

Upozorenje o rezerviranim mjestima. R prepoznaje samo NA. Skupovi podataka često kodiraju nedostajuće vrijednosti kao prazan niz, razmak, "N/A", "-" ili kontrolni broj kao što je -99 ili 999. Oni prolaze svaku gornju provjeru nezapaženo. Pretvorite ih pri uvozu kako bi se ostatak tijeka rada ponašao ispravno:

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

Uvijek skenirajte raspon svakog numeričkog stupca nakon uvoza. Starost od -99 ili cijena od 9999 daleko su opasniji od poštenog NA, jer vas nijedna funkcija neće upozoriti na to.

Vrste nedostajućih podataka: MCAR, MAR i MNAR

Zašto vrijednost nedostaje određuje je li njezino unošenje sigurno. Statističari prepoznaju tri mehanizma.

  • MCAR, potpuno nasumično nedostaje. Vjerojatnost da nedostaje nije povezana ni s čim, promatranim ili ne, na primjer sa senzorom koji zakaže u nasumičnim trenucima.ping ili imputiranje ovih redaka ne uvodi pristranost, već samo gubitak preciznosti.
  • MAR, nasumično nedostaje. Vjerojatnost ovisi o drugim promatranim varijablama, ali ne o samoj nedostajućoj vrijednosti. Ako je manja vjerojatnost da će stariji putnici imati zabilježenu dob, dob je MAR s obzirom na ostale stupce. Imputacija koja koristi te stupce dobro se nosi s tim.
  • MNAR, ne nedostaje slučajno. Vjerojatnost ovisi o samoj nepromatranoj vrijednosti, na primjer, osobe s visokim primanjima odbijaju navesti svoj prihod. Nijedna metoda imputacije ne može to popraviti samo iz podataka, a sam nedostatak sadrži informacije vrijedne bilježenja u stupcu s oznakama.

Imputacija srednje vrijednosti, kako se koristi u ovom vodiču, obranjiva je samo pod MCAR-om i jednostavnim MAR-om. Čak i tada ima poznatu cijenu: popunjavanje svake praznine istim brojem smanjuje varijancu stupca i slabi njegovu korelaciju sa svime ostalim. Za ozbiljan rad koristite metodu temeljenu na modelu kao što je paket mices i uvijek zadržite stupac s oznakom koji označava koje su vrijednosti imputirane.

mutirati()

mutate() je dplyr glagol koji stvara novu varijablu ili prepisuje postojeću, što ga čini prirodnim alatom za izgradnju očišćene kopije stupca.

Nastavit ćemo u dva dijela. Naučit ćemo kako:

  • isključi nedostajuće vrijednosti iz podatkovnog okvira
  • imputirati nedostajuće vrijednosti sa srednjom i medijanom

Glagol mutate() vrlo je jednostavan za korištenje. Možemo stvoriti novu varijablu slijedeći ovu sintaksu:

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()

Isključi nedostajuće vrijednosti (NA)

na.omit() je osnovna R funkcija, a ne dplyr glagol, ali svejedno čisto prosljeđuje. Izbacuje svaki redak koji sadrži barem jednu NA. To je najbrža opcija i rijetko najbolja, jer jedna nedostajuća vrijednost odbacuje cijelo opažanje.

Kako bismo riješili problem nedostajućih opažanja, koristit ćemo titanski skup podataka. U ovom skupu podataka imamo pristup informacijama o putnicima u zrakoplovu tijekom tragedije. Ovaj skup podataka ima mnogo NA o kojima treba voditi računa.

Učitajte CSV datoteku s interneta, a zatim navedite stupce koji sadrže 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

Izlaz:

## [1] "age"  "fare"

Ovdje,

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

vraća nazive stupaca koji sadrže barem jednu nedostajuću vrijednost.

U stupcima starost i cijena karte nedostaju vrijednosti.

Možemo ih ispustiti pomoću na.omit().

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

Izlaz:

## [1] 1045   13

Novi skup podataka sadrži 1045 redaka u usporedbi s 1309 s izvornim skupom podataka.

Isključi nedostajuće vrijednosti

Imputirajte nedostajuće podatke sa srednjom vrijednosti i medijanom

Također možete imputirati, odnosno popuniti nedostajuće vrijednosti srednjom vrijednošću ili medijanom. Dobra je praksa stvoriti dvije odvojene varijable za srednju vrijednost i medijan. Nakon što ih stvorimo, možemo zamijeniti nedostajuće vrijednosti novostvorenim varijablama.

Koristit ćemo metodu primjene za izračunavanje srednje vrijednosti stupca s NA. Pogledajmo primjer

Korak 1) Ranije u vodiču, pohranili smo naziv stupca s nedostajućim vrijednostima na popis pod nazivom list_na. Koristit ćemo se ovim popisom

Korak 2) Izračunajte srednju vrijednost s argumentom na.rm = TRUE. Ovaj argument je obavezan jer stupci imaju nedostajuće podatke, a to govori R-u da ih ignorira.

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

Code Objašnjenje:

Prosljeđujemo 4 argumenta u metodi primjene.

  • df: df_titanic[,colnames(df_titanic) %in% list_na]. Ovaj kod će vratiti naziv stupca iz objekta list_na (tj. “age” i “fare”)
  • 2: Izračunajte funkciju na stupcima
  • srednja vrijednost: Izračunajte srednju vrijednost
  • na.rm = TRUE: Zanemarite vrijednosti koje nedostaju

Izlaz:

##      age     fare 
## 29.88113 33.29548

Uspješno smo stvorili srednju vrijednost stupaca koji sadrže opažanja koja nedostaju. Ove dvije vrijednosti će se koristiti za zamjenu opažanja koja nedostaju.

Korak 3) Zamijenite NA vrijednosti

Glagol mutate iz biblioteke dplyr koristan je u stvaranju nove varijable. Ne želimo nužno mijenjati izvorni stupac kako bismo mogli stvoriti novu varijablu bez NA. mutate je jednostavan za korištenje, samo odabiremo ime varijable i definiramo kako stvoriti tu varijablu. Ovdje je kompletan kod

# 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 Objašnjenje:

Stvaramo dvije varijable, replace_mean_age i replace_mean_fare na sljedeći način:

  • zamijeni srednju_dob = ifelse(is.na(dob), prosjek_nedostaje[1], dob)
  • zamjena_srednje_karte = ifelse(je.na(karta), prosjek_nedostaje[2],karta)

Ako stupac age ima nedostajuće vrijednosti, zamijenite ga prvim elementom prosječne_nedostajuće (srednja vrijednost starosti), inače zadržite izvorne vrijednosti. Ista logika za cijenu

sum(is.na(df_titanic_replace$age))

Izlaz:

## [1] 263

Nakon zamjene, novi stupac uopće ne sadrži nedostajuće vrijednosti:

sum(is.na(df_titanic_replace$replace_mean_age))

Izlaz:

## [1] 0

Izvorni stupac s dobi sadrži 263 nedostajuće vrijednosti, dok je novi stupac replace_mean_age svaku od njih popunio prosječnom dobi.

Korak 4) Opažanja koja nedostaju također možemo zamijeniti medijanom.

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)

Izlaz:

Imputirajte podatke koji nedostaju pomoću srednje vrijednosti i medijana

Korak 5) Na širokom skupu podataka, metoda korak po korak postaje zamorna. sapply() sažima cijeli postupak u jednu naredbu, po cijenu da se imputirane vrijednosti nikada ne vide.

sapply ne stvara a podatkovni okvir, tako da možemo omotati funkciju sapply() unutar data.frame() da bismo stvorili objekt okvira podataka.

# 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)))

Moderna imputacija s replace_na() i across()

Gore navedeni pristupi apply() i sapply() i dalje rade, ali tidyr i dplyr sada izražavaju iste operacije sigurnije i čitljivije.

replace_na() za fiksne vrijednosti. tidyr::replace_na() uzima imenovani popis stupaca i njihovih zamjena:

library(tidyr)

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

across() za svaki numerički stupac. Ovo je izravna, tipski sigurna zamjena za sapply() jednolinijski izraz i za razliku od sapply() nikada ne dira znakovne ili faktorske stupce:

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)))

⚠️ Zašto je prečac sapply() rizičan: Primjer sapply() s jednom linijom izvršava se preko svaki stupac, uključujući znakovne i faktorske. Pozivanje mean() na te vraća NA s upozorenjem, a sapply() zatim prisiljava cijeli rezultat na znakovni. Gornja verzija across(where(is.numeric), ...) to u potpunosti izbjegava.

coalesce() za rezervne stupce. Kada drugi stupac može dati nedostajuću vrijednost, coalesce() vraća prvi unos koji ne nedostaje među svojim argumentima:

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

Vodite evidenciju o tome što ste promijenili. Dodajte zastavicu prije imputiranja, tako da bilo koji kasniji model može učiti iz činjenice da je vrijednost nedostajala:

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

Obrada nedostajućih vrijednosti u R-u: Referenca metode

U ovom tutorijalu su obrađena tri pristupa:

  • Isključite sva zapažanja koja nedostaju
  • Pripisati srednjoj vrijednosti
  • Pripisati medijanu

Donja tablica sažima otkrivanje i uklanjanje:

Knjižnica Cilj Code
baza Navedite zapažanja koja nedostaju
colnames(df)[apply(df, 2, anyNA)]
baza Ukloni svaki redak koji sadrži NA
na.omit(df)

Imputiranje sa srednjom ili medijanom može se izvršiti na dva načina

  • Korištenje primijeniti
  • Korištenje sapply
način Detaljnije Prednosti Nedostaci
Korak po korak uz prijavu Provjerite stupce u kojima nedostaju, izračunajte srednju vrijednost/medijan, pohranite vrijednost, zamijenite s mutate() Možete vidjeti imputiranu srednju vrijednost ili medijan Više vremena izvršenja. Može biti spor s velikim skupom podataka
Brzi način s dodatkom Upotrijebite sapply() i data.frame() za automatsko pretraživanje i zamjenu vrijednosti koje nedostaju srednjom/medijanom Kratak kod i brzo Imputirane vrijednosti se nikada ne prikazuju

Pitanja i odgovori

NA označava nedostajuću vrijednost unutar vektora. NULL predstavlja odsutnost objekta i ima nultu duljinu. NaN je rezultat nedefinirane numeričke operacije kao što je dijeljenje nule s nulom.

Koristite medijan za asimetrične varijable poput cijene prijevoza ili prihoda, jer outlieri povlače srednju vrijednost prema gore. Koristite srednju vrijednost samo kada je stupac otprilike simetričan i bez ekstremnih vrijednosti.

Popunjavanje svake praznine istom vrijednošću smanjuje varijancu stupca i slabi njegovu korelaciju s drugim varijablama. Metode temeljene na modelu, poput paketa mices, puno bolje čuvaju te odnose.

Većina algoritama ne može prihvatiti NA i pogriješit će ili tiho odbaciti retke. Nepažljiva imputacija uzrokuje curenje informacija između skupova za učenje i testnih skupova, stoga uvijek izračunavajte vrijednosti imputacije samo na podjeli za učenje.

Da. AI asistenti mogu predložiti metode na temelju obrasca nedostajućih podataka i izraditi dplyr kod. Provjerite izbor u odnosu na vlastiti izlaz colSums(is.na()) i poznavanje domene o tome zašto podaci nedostaju.

Sažmite ovu objavu uz: