Entfernen Sie Spalten aus dem Datenrahmen, in denen ALLE Werte NA sind

149

Ich habe Probleme mit einem Datenrahmen und konnte dieses Problem nicht wirklich selbst lösen:
Der Datenrahmen hat beliebige Eigenschaften als Spalten und jede Zeile repräsentiert einen Datensatz .

Die Frage ist:
Wie werden Spalten entfernt, bei denen für ALLE Zeilen der Wert NA ist ?

Gnark
quelle

Antworten:

155

Versuche dies:

df <- df[,colSums(is.na(df))<nrow(df)]
teucer
quelle
3
Dadurch wird ein Objekt mit der Größe des alten Objekts erstellt, was ein Problem mit dem Speicher großer Objekte darstellt. Verwenden Sie besser eine Funktion, um die Größe zu reduzieren. Die unten stehende Antwort mit Filter oder data.table hilft Ihnen bei der Speichernutzung.
Mtelesha
3
Dies scheint bei nicht numerischen Spalten nicht zu funktionieren.
Verbamour
Es ändert Spaltennamen, wenn sie dupliziert werden
Peter.k
97

Die beiden bisher angebotenen Ansätze scheitern bei großen Datenmengen, da sie (neben anderen Speicherproblemen) is.na(df)ein Objekt mit der gleichen Größe wie erstellen df.

Hier sind zwei Ansätze, die speicher- und zeiteffizienter sind

Ein Ansatz mit Filter

Filter(function(x)!all(is.na(x)), df)

und ein Ansatz unter Verwendung von data.table (für allgemeine Zeit- und Speichereffizienz)

library(data.table)
DT <- as.data.table(df)
DT[,which(unlist(lapply(DT, function(x)!all(is.na(x))))),with=F]

Beispiele mit großen Datenmengen (30 Spalten, 1e6 Zeilen)

big_data <- replicate(10, data.frame(rep(NA, 1e6), sample(c(1:8,NA),1e6,T), sample(250,1e6,T)),simplify=F)
bd <- do.call(data.frame,big_data)
names(bd) <- paste0('X',seq_len(30))
DT <- as.data.table(bd)

system.time({df1 <- bd[,colSums(is.na(bd) < nrow(bd))]})
# error -- can't allocate vector of size ...
system.time({df2 <- bd[, !apply(is.na(bd), 2, all)]})
# error -- can't allocate vector of size ...
system.time({df3 <- Filter(function(x)!all(is.na(x)), bd)})
## user  system elapsed 
## 0.26    0.03    0.29 
system.time({DT1 <- DT[,which(unlist(lapply(DT, function(x)!all(is.na(x))))),with=F]})
## user  system elapsed 
## 0.14    0.03    0.18 
mnel
quelle
6
Sehr schön. Sie könnten jedoch das Gleiche tun data.frame. Hier gibt es nichts, was wirklich gebraucht wird data.table. Der Schlüssel ist der lapply, der das Kopieren des gesamten Objekts durch vermeidet is.na(df). +10 für den Hinweis.
Matt Dowle
1
Wie würden Sie es mit einem data.frame machen? @ Matt-Dowle
s_a
8
@s_a, bd1 <- bd[, unlist(lapply(bd, function(x), !all(is.na(x))))]
mnel
6
@mnel Ich glaube , Sie entfernen müssen ,nach function(x)- Danke für das Beispiel btw
Thieme Hennis
1
Kannst du es schneller machen mit: = oder mit einem set ()?
Skan
49

dplyrhat jetzt ein select_ifVerb, das hier hilfreich sein kann:

library(dplyr)
temp <- data.frame(x = 1:5, y = c(1,2,NA,4, 5), z = rep(NA, 5))
not_all_na <- function(x) any(!is.na(x))
not_any_na <- function(x) all(!is.na(x))

> temp
  x  y  z
1 1  1 NA
2 2  2 NA
3 3 NA NA
4 4  4 NA
5 5  5 NA

> temp %>% select_if(not_all_na)
  x  y
1 1  1
2 2  2
3 3 NA
4 4  4
5 5  5

> temp %>% select_if(not_any_na)
  x
1 1
2 2
3 3
4 4
5 5
Zack
quelle
Kam hier auf der Suche nach der dplyrLösung. Wurde nicht enttäuscht. Vielen Dank!
Andrew Brēza
Ich fand, dass dies das Problem hatte, dass es auch Variablen mit den meisten, aber nicht allen Werten als fehlend löschen würde
MBorg
15

Eine andere Möglichkeit wäre, die apply()Funktion zu verwenden.

Wenn Sie den data.frame haben

df <- data.frame (var1 = c(1:7,NA),
                  var2 = c(1,2,1,3,4,NA,NA,9),
                  var3 = c(NA)
                  )

Dann können Sie verwenden, um apply()zu sehen, welche Spalten Ihre Bedingung erfüllen, und so können Sie einfach dieselbe Teilmenge wie in der Antwort von Musa nur mit einem applyAnsatz ausführen .

> !apply (is.na(df), 2, all)
 var1  var2  var3 
 TRUE  TRUE FALSE 

> df[, !apply(is.na(df), 2, all)]
  var1 var2
1    1    1
2    2    2
3    3    1
4    4    3
5    5    4
6    6   NA
7    7   NA
8   NA    9
Mropa
quelle
3
Ich hatte erwartet, dass dies schneller gehen würde, da die colSum () -Lösung mehr Arbeit zu leisten schien. Bei meinem Testsatz (213 Beobachtungen von 1614 Variablen zuvor gegenüber 1377 Variablen danach) dauert es jedoch genau dreimal länger. (Aber +1 für einen interessanten Ansatz.)
Darren Cook
10

Spät zum Spiel, aber Sie können auch das janitorPaket verwenden. Diese Funktion entfernt Spalten, die alle NA sind, und kann geändert werden, um auch Zeilen zu entfernen, die alle NA sind.

df <- janitor::remove_empty(df, which = "cols")

André.B
quelle
5
df[sapply(df, function(x) all(is.na(x)))] <- NULL
jpmorris
quelle
4

Die akzeptierte Antwort funktioniert nicht mit nicht numerischen Spalten. Ausgehend von dieser Antwort funktioniert das Folgende mit Spalten, die verschiedene Datentypen enthalten

Filter(function(x) !all(is.na(x)), df)
jeromeResearch
quelle
Jemand anderes hat die gleiche Antwort bereits 4 Jahre vor Ihnen in diesem Thread gepostet ... Siehe die Antwort von mnel unten.
André.B
2

Weitere Optionen mit purrrPaket:

library(dplyr)

df <- data.frame(a = NA,
                 b = seq(1:5), 
                 c = c(rep(1, 4), NA))

df %>% purrr::discard(~all(is.na(.)))
df %>% purrr::keep(~!all(is.na(.)))
AlexB
quelle
1

Ich hoffe das kann auch helfen. Es könnte zu einem einzigen Befehl gemacht werden, aber ich fand es einfacher zu lesen, indem ich es in zwei Befehle aufteilte. Ich machte eine Funktion mit der folgenden Anweisung und arbeitete blitzschnell.

naColsRemoval = function (DataTable) { na.cols = DataTable [ , .( which ( apply ( is.na ( .SD ) , 2 , all ) ) )] DataTable [ , unlist (na.cols) := NULL , with = F] }

Mit .SD können Sie die Überprüfung auf einen Teil der Tabelle beschränken, wenn Sie dies wünschen. Es wird jedoch die gesamte Tabelle als verwendet

Luis M. Nieves
quelle
1

Eine praktische base ROption könnte sein colMeans():

df[, colMeans(is.na(df)) != 1]
tmfmnk
quelle
0

Sie können das Janitor-Paket verwenden remove_empty

library(janitor)

df %>%
  remove_empty(c("rows", "cols")) #select either row or cols or both

Auch ein anderer dplyr-Ansatz

 library(dplyr) 
 df %>% select_if(~all(!is.na(.)))

ODER

df %>% select_if(colSums(!is.na(.)) == nrow(df))

Dies ist auch nützlich, wenn Sie nur Spalten mit einer bestimmten Anzahl fehlender Werte ausschließen / behalten möchten, z

 df %>% select_if(colSums(!is.na(.))>500)
Ashish Singhal
quelle