Wie lösche ich Spalten, die NUR NAs enthalten?

83

Ich habe einen data.frame, der einige Spalten mit allen NA-Werten enthält. Wie kann ich sie aus dem data.frame löschen?

Kann ich die Funktion nutzen?

na.omit(...) 

einige zusätzliche Argumente angeben?

Lorenzo Rigamonti
quelle
1
Hallo! Bitte machen Sie Ihren Beitrag reproduzierbar. Lesen Sie den Beitrag, wie Sie ein gutes reproduzierbares Beispiel dafür erstellen. Danke dir.
Arun
Hilft dieser Beitrag? stackoverflow.com/questions/4862178/…
Arun
Sie schreiben head(data)? Möchten Sie entsprechende Spalten oder Zeilen entfernen?
Nishanth
@ e4e5f4 Ich möchte entsprechende Spalten entfernen (alle Werte der Spalten, die ich entfernen möchte, sind NA)
Lorenzo Rigamonti

Antworten:

122

Eine Möglichkeit, dies zu tun:

df[, colSums(is.na(df)) != nrow(df)]

Wenn die Anzahl der NAs in einer Spalte der Anzahl der Zeilen entspricht, muss sie vollständig NA sein.

Oder ähnlich

df[colSums(!is.na(df)) > 0]
MadScone
quelle
1
Wie kann ich Spalten mit mehr als einem NA-Schwellenwert löschen? oder in Prozent (sagen wir über 50%)?
Discipulus
2
@lovedynasty Am besten senden Sie eine separate Frage, vorausgesetzt, Sie haben Ihren Kommentar noch nicht veröffentlicht. Sie können jedoch immer so etwas tun, df[, colSums(is.na(df)) < nrow(df) * 0.5]dh nur Spalten mit mindestens 50% Nicht-Leerzeichen behalten.
MadScone
2
Leute, die mit einer Korrelationsmatrix arbeiten, müssen verwenden, df[, colSums(is.na(df)) != nrow(df) - 1]da die Diagonale immer ist1
Boern
9
Kann dies auch mit der Funktion select_if von dplyr (Version 0.5.0) verwenden. df %>% select_if(colSums(!is.na(.)) > 0)
Stefan Avey
@MadScone gibt mir einen Syntaxfehler bei "," für df [, colSums (is.na (df))! = Nrow (df)] und einen Syntaxfehler bei "!" in df [colSums (! is.na (df))> 0]. Vermisse ich etwas
Aravind S
52

Hier ist eine dplyr-Lösung:

df %>% select_if(~sum(!is.na(.)) > 0)
Brad Cannell
quelle
4
Bei ~ 15.000 Zeilen und ~ 5.000 Spalten dauert dies wirklich ewig.
EngrStudent
@EngrStudent War es mit der Lösung der akzeptierten Antwort schneller?
Johnny
Es ist einige Jahre her. Ich erinnere mich nicht. DJV hat unten einen schönen Timing-Beitrag.
EngrStudent
24

Es sieht so aus, als ob Sie NUR Spalten mit ALLEN NA s entfernen möchten , wobei Spalten mit einigen Zeilen mit s übrig bleiben NA. Ich würde dies tun (aber ich bin sicher, dass es eine effiziente vektorisierte Lösung gibt:

#set seed for reproducibility
set.seed <- 103
df <- data.frame( id = 1:10 , nas = rep( NA , 10 ) , vals = sample( c( 1:3 , NA ) , 10 , repl = TRUE ) )
df
#      id nas vals
#   1   1  NA   NA
#   2   2  NA    2
#   3   3  NA    1
#   4   4  NA    2
#   5   5  NA    2
#   6   6  NA    3
#   7   7  NA    2
#   8   8  NA    3
#   9   9  NA    3
#   10 10  NA    2

#Use this command to remove columns that are entirely NA values, it will elave columns where only some vlaues are NA
df[ , ! apply( df , 2 , function(x) all(is.na(x)) ) ]
#      id vals
#   1   1   NA
#   2   2    2
#   3   3    1
#   4   4    2
#   5   5    2
#   6   6    3
#   7   7    2
#   8   8    3
#   9   9    3
#   10 10    2

Wenn Sie sich in einer Situation befinden, in der Sie Spalten mit beliebigen NAWerten entfernen möchten, können Sie den allobigen Befehl einfach in ändern any.

Simon O'Hanlon
quelle
Der data.frame hat zwei Arten von Spalten: eine in whohc alle Werte sind Zahlen und die andere, in der alle Werte NA sind
Lorenzo Rigamonti
Das wird dann also funktionieren. Es werden nur Spalten entfernt, in denen ALLE Werte vorhanden sind NA.
Simon O'Hanlon
1
Gute Lösung. Ich würde es apply(is.na(df), 1, all)allerdings nur tun, weil es etwas ordentlicher is.na()ist und für alle dfund nicht nur für eine Reihe gleichzeitig verwendet wird (Show sei etwas schneller).
MadScone
@ MadScone guter Tipp - sieht ordentlicher aus. Sie sollten jedoch über Spalten und nicht über Zeilen hinweg anwenden.
Simon O'Hanlon
@ MadScone-Änderungen werden nach 5 Minuten für Kommentare gesperrt. Ich sollte mir keine Sorgen machen, es ist kein Problem !! :-)
Simon O'Hanlon
24

Eine weitere Option ist das janitorPaket:

df <- remove_empty_cols(df)

https://github.com/sfirke/janitor

jsta
quelle
10
janitor::remove_empty_cols()ist veraltet - Verwendungdf <- janitor::remove_empty(df, which = "cols")
André.B
19

Ein intuitives Skript : dplyr::select_if(~!all(is.na(.))). Es werden buchstäblich nur Spalten gespeichert, in denen nicht alle Elemente fehlen. (um Spalten zu löschen, bei denen alle Elemente fehlen).

> df <- data.frame( id = 1:10 , nas = rep( NA , 10 ) , vals = sample( c( 1:3 , NA ) , 10 , repl = TRUE ) )

> df %>% glimpse()
Observations: 10
Variables: 3
$ id   <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
$ nas  <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA
$ vals <int> NA, 1, 1, NA, 1, 1, 1, 2, 3, NA

> df %>% select_if(~!all(is.na(.))) 
   id vals
1   1   NA
2   2    1
3   3    1
4   4   NA
5   5    1
6   6    1
7   7    1
8   8    2
9   9    3
10 10   NA
Sibo Jiang
quelle
16

Eine weitere Option mit Filter

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

HINWEIS: Daten aus dem Beitrag von @Simon O'Hanlon.

akrun
quelle
4

Da die Leistung für mich sehr wichtig war, habe ich alle oben genannten Funktionen verglichen.

HINWEIS: Daten aus dem Beitrag von @Simon O'Hanlon. Nur mit Größe 15000 statt 10.

library(tidyverse)
library(microbenchmark)

set.seed(123)
df <- data.frame(id = 1:15000,
                 nas = rep(NA, 15000), 
                 vals = sample(c(1:3, NA), 15000,
                               repl = TRUE))
df

MadSconeF1 <- function(x) x[, colSums(is.na(x)) != nrow(x)]

MadSconeF2 <- function(x) x[colSums(!is.na(x)) > 0]

BradCannell <- function(x) x %>% select_if(~sum(!is.na(.)) > 0)

SimonOHanlon <- function(x) x[ , !apply(x, 2 ,function(y) all(is.na(y)))]

jsta <- function(x) janitor::remove_empty(x)

SiboJiang <- function(x) x %>% dplyr::select_if(~!all(is.na(.)))

akrun <- function(x) Filter(function(y) !all(is.na(y)), x)

mbm <- microbenchmark(
  "MadSconeF1" = {MadSconeF1(df)},
  "MadSconeF2" = {MadSconeF2(df)},
  "BradCannell" = {BradCannell(df)},
  "SimonOHanlon" = {SimonOHanlon(df)},
  "SiboJiang" = {SiboJiang(df)},
  "jsta" = {jsta(df)}, 
  "akrun" = {akrun(df)},
  times = 1000)

mbm

Ergebnisse:

Unit: microseconds
         expr    min      lq      mean  median      uq      max neval  cld
   MadSconeF1  154.5  178.35  257.9396  196.05  219.25   5001.0  1000 a   
   MadSconeF2  180.4  209.75  281.2541  226.40  251.05   6322.1  1000 a   
  BradCannell 2579.4 2884.90 3330.3700 3059.45 3379.30  33667.3  1000    d
 SimonOHanlon  511.0  565.00  943.3089  586.45  623.65 210338.4  1000  b  
    SiboJiang 2558.1 2853.05 3377.6702 3010.30 3310.00  89718.0  1000    d
         jsta 1544.8 1652.45 2031.5065 1706.05 1872.65  11594.9  1000   c 
        akrun   93.8  111.60  139.9482  121.90  135.45   3851.2  1000 a


autoplot(mbm)

Geben Sie hier die Bildbeschreibung ein

mbm %>% 
  tbl_df() %>%
  ggplot(aes(sample = time)) + 
  stat_qq() + 
  stat_qq_line() +
  facet_wrap(~expr, scales = "free")

Geben Sie hier die Bildbeschreibung ein

DJV
quelle
Manchmal ist die erste Iteration eine kompilierte JIT, daher hat sie sehr schlechte und nicht sehr charakteristische Zeiten. Ich finde es interessant, was die größere Stichprobe mit den richtigen Schwänzen der Verteilung macht. Das ist gute Arbeit.
EngrStudent
Ich habe es noch einmal ausgeführt und war mir nicht sicher, ob ich die Handlung geändert habe. In Bezug auf die Verteilung in der Tat. Ich sollte wahrscheinlich verschiedene Stichprobengrößen vergleichen, wenn ich Zeit habe.
DJV
1
Wenn Sie einen der Trends wie "akrun" qqplot ( ggplot2.tidyverse.org/reference/geom_qq.html ), dann wette ich, dass es einen Punkt gibt, der sich sehr von der Verteilung der anderen unterscheidet. Der Rest gibt an, wie lange es dauert, wenn Sie es wiederholt ausführen, aber das gibt an, was passiert, wenn Sie es einmal ausführen. Es gibt ein altes Sprichwort: Sie können 20 Jahre Erfahrung haben oder Sie können nur 20 Jahre Erfahrung 20 Mal haben.
EngrStudent
Sehr schön! Ich bin überrascht, dass sich mehrere Samples im extremen Schwanz befinden. Ich frage mich, warum diese so viel teurer sind. JIT kann 1 oder 2 sein, aber nicht 20. Bedingung? Interrupts? Andere? Nochmals vielen Dank für das Update.
EngrStudent
Gern geschehen, danke für die Gedanken. Weiß nicht, ich habe es tatsächlich "frei" laufen lassen.
DJV