web-dev-qa-db-de.com

Entfernen Sie Zeilen mit allen oder einigen NAs (fehlende Werte) in data.frame

Ich möchte die Zeilen in diesem Datenrahmen entfernen, die:

a) Enthält NAs in allen Spalten. Unten ist mein Beispieldatenrahmen.

             gene hsap mmul mmus rnor cfam
1 ENSG00000208234    0   NA   NA   NA   NA
2 ENSG00000199674    0   2    2    2    2
3 ENSG00000221622    0   NA   NA   NA   NA
4 ENSG00000207604    0   NA   NA   1    2
5 ENSG00000207431    0   NA   NA   NA   NA
6 ENSG00000221312    0   1    2    3    2

Grundsätzlich hätte ich gerne einen Datenrahmen wie den folgenden.

             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
6 ENSG00000221312    0   1    2    3    2

b) enthält NAs nur in einigen Spalten, damit ich auch dieses Ergebnis erhalten kann:

             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
4 ENSG00000207604    0   NA   NA   1    2
6 ENSG00000221312    0   1    2    3    2
769
Benoit B.

Überprüfen Sie auch complete.cases :

_> final[complete.cases(final), ]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
6 ENSG00000221312    0    1    2    3    2
_

_na.omit_ ist besser geeignet, um nur alle NA zu entfernen. _complete.cases_ ermöglicht eine teilweise Auswahl, indem nur bestimmte Spalten des Datenrahmens eingeschlossen werden:

_> final[complete.cases(final[ , 5:6]),]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
4 ENSG00000207604    0   NA   NA    1    2
6 ENSG00000221312    0    1    2    3    2
_

Ihre Lösung kann nicht funktionieren. Wenn Sie darauf bestehen, _is.na_ zu verwenden, müssen Sie Folgendes tun:

_> final[rowSums(is.na(final[ , 5:6])) == 0, ]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
4 ENSG00000207604    0   NA   NA    1    2
6 ENSG00000221312    0    1    2    3    2
_

die Verwendung von _complete.cases_ ist jedoch viel klarer und schneller.

967
Joris Meys

Versuchen Sie na.omit(your.data.frame). Versuchen Sie, die zweite Frage als weitere Frage zu veröffentlichen (aus Gründen der Klarheit).

234
Roman Luštrik

tidyr hat eine neue Funktion drop_na :

_library(tidyr)
df %>% drop_na()
#              gene hsap mmul mmus rnor cfam
# 2 ENSG00000199674    0    2    2    2    2
# 6 ENSG00000221312    0    1    2    3    2
df %>% drop_na(rnor, cfam)
#              gene hsap mmul mmus rnor cfam
# 2 ENSG00000199674    0    2    2    2    2
# 4 ENSG00000207604    0   NA   NA    1    2
# 6 ENSG00000221312    0    1    2    3    2
_
88
lukeA

Ich bevorzuge folgende Methode, um zu überprüfen, ob Zeilen NAs enthalten:

row.has.na <- apply(final, 1, function(x){any(is.na(x))})

Dies gibt einen logischen Vektor mit Werten zurück, die angeben, ob eine NA in einer Zeile vorhanden ist. Sie können es verwenden, um zu sehen, wie viele Zeilen Sie löschen müssen:

sum(row.has.na)

und sie schließlich fallen lassen

final.filtered <- final[!row.has.na,]

Das Filtern von Zeilen mit bestimmten Teilen von NAs wird etwas kniffliger (Sie können beispielsweise 'final [ 5: 6]' eingeben, um 'apply' zu verwenden). Generell scheint die Lösung von Joris Meys eleganter zu sein.

86
donshikin

Eine weitere Option, wenn Sie mehr Kontrolle darüber haben möchten, wie Zeilen als ungültig eingestuft werden, ist

final <- final[!(is.na(final$rnor)) | !(is.na(rawdata$cfam)),]

Unter Verwendung des Obigen ist dies:

             gene hsap mmul mmus rnor cfam
1 ENSG00000208234    0   NA   NA   NA   2
2 ENSG00000199674    0   2    2    2    2
3 ENSG00000221622    0   NA   NA   2   NA
4 ENSG00000207604    0   NA   NA   1    2
5 ENSG00000207431    0   NA   NA   NA   NA
6 ENSG00000221312    0   1    2    3    2

Wird:

             gene hsap mmul mmus rnor cfam
1 ENSG00000208234    0   NA   NA   NA   2
2 ENSG00000199674    0   2    2    2    2
3 ENSG00000221622    0   NA   NA   2   NA
4 ENSG00000207604    0   NA   NA   1    2
6 ENSG00000221312    0   1    2    3    2

... wobei nur Zeile 5 entfernt wird, da dies die einzige Zeile ist, die NAs für rnor UND cfam enthält. Die Boolesche Logik kann dann geändert werden, um sie an bestimmte Anforderungen anzupassen.

43
getting-there

Wenn Sie steuern möchten, wie viele NAs für jede Zeile gültig sind, probieren Sie diese Funktion aus. Bei vielen Umfragedatensätzen können zu viele leere Antworten die Ergebnisse ruinieren. Sie werden also nach einer bestimmten Schwelle gelöscht. Mit dieser Funktion können Sie auswählen, wie viele NAs die Zeile haben darf, bevor sie gelöscht wird:

delete.na <- function(DF, n=0) {
  DF[rowSums(is.na(DF)) <= n,]
}

Standardmäßig werden alle NAs entfernt:

delete.na(final)
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
6 ENSG00000221312    0    1    2    3    2

Oder geben Sie die maximal zulässige Anzahl von NAs an:

delete.na(final, 2)
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
4 ENSG00000207604    0   NA   NA    1    2
6 ENSG00000221312    0    1    2    3    2
37

Wenn Leistung Priorität hat, verwenden Sie _data.table_ und na.omit() mit dem optionalen Parameter _cols=_.

na.omit.data.table ist der schnellste Wert in meinem Benchmark (siehe unten), ob für alle Spalten oder für ausgewählte Spalten (OP-Frage Teil 2).

Wenn Sie _data.table_ nicht verwenden möchten, verwenden Sie complete.cases().

Bei einem Vanilla _data.frame_ ist complete.cases schneller als na.omit() oder dplyr::drop_na() . Beachten Sie, dass _na.omit.data.frame_ _cols=_ nicht unterstützt.

Benchmark-Ergebnis

Hier ist ein Vergleich der Methoden base (blau), dplyr (pink) und _data.table_ (gelb) zum Löschen aller oder zum Auswählen fehlender Beobachtungen anhand eines fiktiven Datensatzes von 1 Million Beobachtungen von 20 numerischen Variablen mit unabhängige 5% ige Wahrscheinlichkeit des Fehlens und eine Teilmenge von 4 Variablen für Teil 2.

Ihre Ergebnisse können je nach Länge, Breite und Dichte Ihres bestimmten Datasets variieren.

Protokollskala auf der y-Achse notieren.

enter image description here

Benchmark-Skript

_#-------  Adjust these assumptions for your own use case  ------------
row_size   <- 1e6L 
col_size   <- 20    # not including ID column
p_missing  <- 0.05   # likelihood of missing observation (except ID col)
col_subset <- 18:21  # second part of question: filter on select columns

#-------  System info for benchmark  ----------------------------------
R.version # R version 3.4.3 (2017-11-30), platform = x86_64-w64-mingw32
library(data.table); packageVersion('data.table') # 1.10.4.3
library(dplyr);      packageVersion('dplyr')      # 0.7.4
library(tidyr);      packageVersion('tidyr')      # 0.8.0
library(microbenchmark)

#-------  Example dataset using above assumptions  --------------------
fakeData <- function(m, n, p){
  set.seed(123)
  m <-  matrix(runif(m*n), nrow=m, ncol=n)
  m[m<p] <- NA
  return(m)
}
df <- cbind( data.frame(id = paste0('ID',seq(row_size)), 
                        stringsAsFactors = FALSE),
             data.frame(fakeData(row_size, col_size, p_missing) )
             )
dt <- data.table(df)

par(las=3, mfcol=c(1,2), mar=c(22,4,1,1)+0.1)
boxplot(
  microbenchmark(
    df[complete.cases(df), ],
    na.omit(df),
    df %>% drop_na,
    dt[complete.cases(dt), ],
    na.omit(dt)
  ), xlab='', 
  main = 'Performance: Drop any NA observation',
  col=c(rep('lightblue',2),'salmon',rep('beige',2))
)
boxplot(
  microbenchmark(
    df[complete.cases(df[,col_subset]), ],
    #na.omit(df), # col subset not supported in na.omit.data.frame
    df %>% drop_na(col_subset),
    dt[complete.cases(dt[,col_subset,with=FALSE]), ],
    na.omit(dt, cols=col_subset) # see ?na.omit.data.table
  ), xlab='', 
  main = 'Performance: Drop NA obs. in select cols',
  col=c('lightblue','salmon',rep('beige',2))
)
_
34
C8H10N4O2

Mit dem Paket dplyr können wir NA wie folgt filtern:

dplyr::filter(df,  !is.na(columnname))
19
Raminsu

Dies gibt die Zeilen zurück, die mindestens EINEN Nicht-NA-Wert haben.

final[rowSums(is.na(final))<length(final),]

Dies gibt die Zeilen mit mindestens ZWEI Nicht-NA-Werten zurück.

final[rowSums(is.na(final))<(length(final)-1),]
17
Leo

Für Ihre erste Frage habe ich einen Code, mit dem ich alle NAs loswerden kann. Vielen Dank für @Gregor, um es einfacher zu machen.

final[!(rowSums(is.na(final))),]

Bei der zweiten Frage ist der Code nur eine Abwechslung zur vorherigen Lösung.

final[as.logical((rowSums(is.na(final))-5)),]

Beachten Sie, dass -5 die Anzahl der Spalten in Ihren Daten ist. Dies eliminiert Zeilen mit allen NAs, da die Zeilensummen sich zu 5 addieren und nach der Subtraktion zu Nullen werden. Diesmal ist as.logical notwendig.

15
LegitMe

Wir können dafür auch die Subset-Funktion verwenden.

finalData<-subset(data,!(is.na(data["mmul"]) | is.na(data["rnor"])))

Dies ergibt nur die Zeilen, die weder in mmul noch in rnor NA haben

14
Ramya Ural

Ich bin ein Synthesizer :). Hier habe ich die Antworten zu einer Funktion zusammengefasst:

#' keep rows that have a certain number (range) of NAs anywhere/somewhere and delete others
#' @param df a data frame
#' @param col restrict to the columns where you would like to search for NA; eg, 3, c(3), 2:5, "place", c("place","age")
#' \cr default is NULL, search for all columns
#' @param n integer or vector, 0, c(3,5), number/range of NAs allowed.
#' \cr If a number, the exact number of NAs kept
#' \cr Range includes both ends 3<=n<=5
#' \cr Range could be -Inf, Inf
#' @return returns a new df with rows that have NA(s) removed
#' @export
ez.na.keep = function(df, col=NULL, n=0){
    if (!is.null(col)) {
        # R converts a single row/col to a vector if the parameter col has only one col
        # see https://radfordneal.wordpress.com/2008/08/20/design-flaws-in-r-2-%E2%80%94-dropped-dimensions/#comments
        df.temp = df[,col,drop=FALSE]
    } else {
        df.temp = df
    }

    if (length(n)==1){
        if (n==0) {
            # simply call complete.cases which might be faster
            result = df[complete.cases(df.temp),]
        } else {
            # credit: http://stackoverflow.com/a/30461945/2292993
            log <- apply(df.temp, 2, is.na)
            logindex <- apply(log, 1, function(x) sum(x) == n)
            result = df[logindex, ]
        }
    }

    if (length(n)==2){
        min = n[1]; max = n[2]
        log <- apply(df.temp, 2, is.na)
        logindex <- apply(log, 1, function(x) {sum(x) >= min && sum(x) <= max})
        result = df[logindex, ]
    }

    return(result)
}
9
Jerry T

Angenommen, dat ist Ihr Datenrahmen, kann die erwartete Ausgabe mit erreicht werden

1 .rowSums

> dat[!rowSums((is.na(dat))),]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
6 ENSG00000221312    0   1    2    3    2

2 .lapply

> dat[!Reduce('|',lapply(dat,is.na)),]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
6 ENSG00000221312    0   1    2    3    2
8
Prradep
delete.dirt <- function(DF, Dart=c('NA')) {
  dirty_rows <- apply(DF, 1, function(r) !any(r %in% Dart))
  DF <- DF[dirty_rows, ]
}

mydata <- delete.dirt(mydata)

Die obige Funktion löscht alle Zeilen aus dem Datenrahmen, der in einer Spalte 'NA' enthält, und gibt die resultierenden Daten zurück. Wenn Sie nach mehreren Werten wie NA und ? suchen möchten, ändern Sie Dart=c('NA') in function param in Dart=c('NA', '?')

4
sapy

Ich vermute, dass dies auf diese Weise eleganter gelöst werden könnte

  m <- matrix(1:25, ncol = 5)
  m[c(1, 6, 13, 25)] <- NA
  df <- data.frame(m)
  library(dplyr) 
  df %>%
  filter_all(any_vars(is.na(.)))
  #>   X1 X2 X3 X4 X5
  #> 1 NA NA 11 16 21
  #> 2  3  8 NA 18 23
  #> 3  5 10 15 20 NA
3
Joni Hoppen

Ein Ansatz, der sowohl allgemein als auch gut lesbar ist, ist die Verwendung der Funktion filter und ihrer Varianten im Paket dplyr (filter_all, filter_at, filter_if):

library(dplyr)

vars_to_check <- c("rnor", "cfam")

# Filter a specific list of columns to keep only non-missing entries
df %>% 
  filter_at(.vars = vars(one_of(vars_to_check)),
            ~ !is.na(.))

# Filter all the columns to exclude NA
df %>% 
  filter_all(~ !is.na(.))

# Filter only numeric columns
df %>%
  filter_if(is.numeric,
            ~ !is.na(.))
2
bschneidr

na.omit wird in diesem Fall tun. Anschließend können Sie Ihren Datensatz mithilfe eines Diagramms mit fehlenden Werten visualisieren.

0
Isabelle