Elegancki sposób zgłaszania brakujących wartości w data.frame

81

Oto mały fragment kodu, który napisałem, aby zgłosić zmienne z brakującymi wartościami z ramki danych. Próbuję wymyślić bardziej elegancki sposób, aby to zrobić, taki, który być może zwraca ramkę danych, ale utknąłem:

for (Var in names(airquality)) {
    missing <- sum(is.na(airquality[,Var]))
    if (missing > 0) {
        print(c(Var,missing))
    }
}

Edycja: mam do czynienia z ramkami data.frames zawierającymi dziesiątki lub setki zmiennych, więc kluczowe jest, abyśmy raportowali tylko zmienne z brakami danych.

Zach
źródło
@kohske: to była moja pierwsza myśl, ale wyniki są tablez postaci i musiałbyś przeanalizować liczbę NA.
Joshua Ulrich
Cofam twoje pytanie, ponieważ opublikowałeś odpowiedź. Jeśli chcesz skomentować odpowiedź, zrób to jako komentarz do tej odpowiedzi. Jeśli pytania zawierają również odpowiedzi, staje się to bardzo zagmatwane.
Andrie
@Andrie: Nie zgadzam się z twoją zmianą, ponieważ kluczowym problemem, z którym się spotykam, jest zgłaszanie tylko zmiennych z brakami danych. Co więcej, twoje wycofanie usunęło zmianę, którą wprowadziłem w kodzie. Zmodyfikowałem moje pytanie, aby uwzględnić te informacje, i dodałem zmodyfikowaną wersję kodu Josha do komentarza.
Zach
@Zach Twoja nowa edycja wygląda mi dobrze. Nawiasem mówiąc, nie mam nic przeciwko dodawaniu dodatkowych danych / żądań w pytaniu, gdy jest ono aktywne, jeśli to wyjaśnia pytanie.
Andrie
Jest na to pół miliona sposobów, zobacz Widok zadań CRAN
MissingData

Odpowiedzi:

157

Po prostu użyj sapply

> sapply(airquality, function(x) sum(is.na(x)))
  Ozone Solar.R    Wind    Temp   Month     Day 
     37       7       0       0       0       0

Możesz także użyć applylub colSumsna macierzy utworzonej przezis.na()

> apply(is.na(airquality),2,sum)
  Ozone Solar.R    Wind    Temp   Month     Day 
     37       7       0       0       0       0
> colSums(is.na(airquality))
  Ozone Solar.R    Wind    Temp   Month     Day 
     37       7       0       0       0       0 
Joshua Ulrich
źródło
12
Zmodyfikowałem twój kod nieznacznie, aby zgłosić tylko brakującą wartość:M <- sapply(airquality, function(x) sum(is.na(x))); M[M>0]
Zach
Dzięki! Nauczyłem się wiele.
Bombyx mori
Cześć @Joshua Ulrich, bardzo dziękuję za zwięzły kod. Chciałbym dodać kolumnę w ramce danych, która pokazywałaby procent wartości na. Czy możesz pomóc w tej sprawie?
DukeLover
2
@Zach Korzystam z wersji Twojej sugestii, aby sprawdzić, czy wymagane pola mają wartość:M <- colSums(is.na(airquality)); M[M <= 0]
Anthony Simon Mielniczuk
@Joshua dodanie opcji dla% s też byłoby asem!
radek
8

Możemy użyć map_dfz mruczeniem.

library(mice)
library(purrr)

# map_df with purrr
map_df(airquality, function(x) sum(is.na(x)))
# A tibble: 1 × 6
# Ozone Solar.R  Wind  Temp Month   Day
# <int>   <int> <int> <int> <int> <int>
# 1    37       7     0     0     0     0
Keiku
źródło
1
Jaka jest przewaga map_dfnad sapply?
Zach
1
@Zach Myślę, że nie ma dużej różnicy, ale Hadley powiedział, aby nie używać sapply () wewnątrz funkcji. Zobacz Wyjątki i debugowanie · Zaawansowane R. adv-r.had.co.nz/Exceptions-Debugging.html .
Keiku
dla leniwych ludzi takich jak ja możesz napisać powyższy kod w krótszej składni mruczenia dla funkcji (~), więc wygląda to tak:map_df( air quality, ~sum(is.na(.) )
Agile Bean
1
@Zach zaletą map_dfover sapplyjest tylko wtedy, gdy wynik ma wiele wierszy, ponieważ format wyjściowy map_df jest zawsze tibble.
Agile Bean
1
@Zach: lepiej jest używać vapplyvs sapplyw funkcjach, ponieważ vapplydaje znaną strukturę wyników (którą określasz). sapplymoże zwrócić tablicę lub listę, w zależności od wyniku funkcji. Wadą programu map_dfjest to, że jako dane wejściowe nadajesz mu data.frame, która zwraca podklasę data.frame, a nie data.frame. Nie ma gwarancji, że tibbles będą zachowywać się tak samo, jak data.frames we wszystkich niezbędnych przypadkach w przyszłości.
Joshua Ulrich
8

Moim nowym ulubionym (niezbyt szerokim) danymi są metody z doskonałego pakietu naniar . Otrzymujesz nie tylko częstotliwości, ale także wzorce braków:

library(naniar)
library(UpSetR)

riskfactors %>%
  as_shadow_upset() %>%
  upset()

wprowadź opis obrazu tutaj

Często warto zobaczyć, gdzie są chybienia w stosunku do braków, które można osiągnąć, wykreślając wykres punktowy z brakami:

ggplot(airquality,
       aes(x = Ozone,
           y = Solar.R)) +
 geom_miss_point()

wprowadź opis obrazu tutaj

Lub dla zmiennych kategorialnych:

gg_miss_fct(x = riskfactors, fct = marital)

wprowadź opis obrazu tutaj

Te przykłady pochodzą z winiety pakietu, która zawiera listę innych interesujących wizualizacji.

radek
źródło
2
Dzięki za opublikowanie tego! W gg_miss_upset()najnowszej wersji dostępna jest teraz dedykowana funkcja, która zostanie przesłana do CRAN po powrocie z wakacji. naniar.njtierney.com/reference/gg_miss_upset.html
Nick Tierney
6
summary(airquality)

już podaje te informacje

W VIM pakiety oferuje również kilka przyjemnych brakującą działkę danych dla data.frame

library("VIM")
aggr(airquality)

wprowadź opis obrazu tutaj

Steffen Moritz
źródło
Czy pakiet VIM może zgłosić, które konkretne obserwacje mają brakujące dane?
Anthony Simon Mielniczuk
nie sądzę ... ale możesz to całkiem łatwo (musiałbyś zastąpić airquality własną ramką danych): res <- airquality [rowSums (is.na (airquality))> 0,]
Steffen Moritz
4

Bardziej zwięzłe-: sum(is.na(x[1]))

To jest

  1. x[1] Spójrz na pierwszą kolumnę

  2. is.na() prawda, jeśli tak NA

  3. sum() TRUEjest 1, FALSEjest0

Keith Whittingham
źródło
to nie odpowiada na pierwotne pytanie, którym jest znalezienie liczby NAs dla wszystkich kolumn w danych
Ben Bolker
4

Kolejna alternatywa graficzna - plot_missingfunkcja z doskonałego DataExplorerpakietu:

wprowadź opis obrazu tutaj

Docs zwraca również uwagę na fakt, że możesz zapisać te wyniki do dodatkowej analizy za pomocą missing_data <- plot_missing(data).

radek
źródło
plot_missing()Funkcja w DataExplorerpakiecie jest teraz PlotMissing().
coip
1
@coip PlotMissing()jest przestarzałe. Użyj plot_missing()zamiast tego. Zobacz nr 49, aby uzyskać więcej informacji.
Boxuan
2

Inną funkcją, która pomogłaby w przejrzeniu brakujących danych, byłaby df_status z biblioteki funModeling

library(funModeling)

iris.2 to zbiór danych tęczówki z kilkoma dodanymi NA. możesz go zastąpić swoim zbiorem danych.

df_status(iris.2)

To da ci liczbę i procent NA w każdej kolumnie.

Shahan Degamwala
źródło
1

Jeszcze jedno rozwiązanie graficzne, oferty visdat pakietowevis_miss .

library(visdat)
vis_miss(airquality)

wprowadź opis obrazu tutaj

Bardzo podobne do Ameliawyników z niewielką różnicą polegającą na podawaniu% s braków po wyjęciu z pudełka.

radek
źródło
1

Myślę, że biblioteka Amelia dobrze radzi sobie z obsługą brakujących danych, zawiera również mapę do wizualizacji brakujących wierszy.

install.packages("Amelia")
library(Amelia)
missmap(airquality)

wprowadź opis obrazu tutaj

Możesz również uruchomić następujący kod, który zwróci wartości logiczne na

row.has.na <- apply(training, 1, function(x){any(is.na(x))})
drexxx
źródło
1

Innym graficznym i interaktywnym sposobem jest użycie is.na10funkcji z heatmaplybiblioteki:

library(heatmaply)

heatmaply(is.na10(airquality), grid_gap = 1, 
          showticklabels = c(T,F),
            k_col =3, k_row = 3,
            margins = c(55, 30), 
            colors = c("grey80", "grey20"))

wprowadź opis obrazu tutaj

Prawdopodobnie nie będzie działać dobrze z dużymi zbiorami danych.

radek
źródło
0

Jeśli chcesz to zrobić dla określonej kolumny, możesz również tego użyć

length(which(is.na(airquality[1])==T))
Chintak Chhapia
źródło
4
Nie musisz porównywać wektora logicznego z T. Możesz również policzyć liczbę elementów TRUE w wektorze logicznym, sumując go.
Houshalter
0

dplyrRozwiązaniem uzyskać zliczania mogą być:

summarise_all(df, ~sum(is.na(.)))

Lub aby otrzymać procent:

summarise_all(df, ~(sum(is_missing(.) / nrow(df))))

Może warto również zauważyć, że brakujące dane mogą być brzydkie, niespójne i nie zawsze zakodowane w NAzależności od źródła lub sposobu obsługi podczas importowania. Następującą funkcję można dostosować w zależności od danych i tego, czego brakuje:

is_missing <- function(x){
  missing_strs <- c('', 'null', 'na', 'nan', 'inf', '-inf', '-9', 'unknown', 'missing')
  ifelse((is.na(x) | is.nan(x) | is.infinite(x)), TRUE,
         ifelse(trimws(tolower(x)) %in% missing_strs, TRUE, FALSE))
}

# sample ugly data
df <- data.frame(a = c(NA, '1', '  ', 'missing'),
                 b = c(0, 2, NaN, 4),
                 c = c('NA', 'b', '-9', 'null'),
                 d = 1:4,
                 e = c(1, Inf, -Inf, 0))

# counts:
> summarise_all(df, ~sum(is_missing(.)))
  a b c d e
1 3 1 3 0 2

# percentage:
> summarise_all(df, ~(sum(is_missing(.) / nrow(df))))
     a    b    c d   e
1 0.75 0.25 0.75 0 0.5
sbha
źródło