Rimuovere le colonne dal frame di dati in cui TUTTI i valori sono NA


149

Sto riscontrando problemi con un frame di dati e non riesco davvero a risolvere il problema da solo:
il dataframe ha proprietà arbitrarie come colonne e ogni riga rappresenta un set di dati .

La domanda è:
come sbarazzarsi delle colonne in cui per TUTTE le righe il valore è NA ?

Risposte:


155

Prova questo:

df <- df[,colSums(is.na(df))<nrow(df)]

3
Questo crea un oggetto delle dimensioni del vecchio oggetto che è un problema con la memoria su oggetti di grandi dimensioni. Meglio usare una funzione per ridurre le dimensioni. La risposta sotto usando Filter o usando data.table ti aiuterà a usare la memoria.
mtelesha,

3
Ciò non sembra funzionare con colonne non numeriche.
verbamour

Cambia il nome della colonna se sono duplicati
Peter.k

97

I due approcci offerti finora falliscono con set di dati di grandi dimensioni come (tra gli altri problemi di memoria) che creano is.na(df), che sarà un oggetto delle stesse dimensioni di df.

Ecco due approcci che sono più efficienti in termini di memoria e tempo

Un approccio che utilizza Filter

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

e un approccio che utilizza data.table (per tempo generale ed efficienza della memoria)

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

esempi utilizzando dati di grandi dimensioni (30 colonne, 1e6 righe)

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 

6
Molto bella. Potresti fare lo stesso con data.frame, comunque. Non c'è niente qui che ha davvero bisogno data.table. La chiave è la lapply, che evita la copia dell'intero oggetto fatto da is.na(df). +10 per evidenziarlo.
Matt Dowle,

1
Come lo faresti con un data.frame? @ matt-dowle
s_a

8
@s_a, bd1 <- bd[, unlist(lapply(bd, function(x), !all(is.na(x))))]
mnel

6
@mnel Penso che sia necessario rimuovere il ,dopo function(x)- grazie per l'esempio tra l'altro
Thieme Hennis,

1
Puoi farlo più velocemente con: = o con un set ()?
skan

49

dplyrora ha un select_ifverbo che può essere utile qui:

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

Sono venuto qui alla ricerca della dplyrsoluzione. Non sono rimasto deluso. Grazie!
Andrew Brēza,

Ho scoperto che questo aveva il problema che avrebbe eliminato anche le variabili con la maggior parte ma non tutti i valori mancanti
MBorg

15

Un altro modo sarebbe usare la apply()funzione.

Se si dispone di data.frame

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

quindi puoi usare apply()per vedere quali colonne soddisfano la tua condizione e quindi puoi semplicemente fare lo stesso sottoinsieme della risposta di Musa, solo con un applyapproccio.

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

3
Mi aspettavo che questo fosse più veloce, poiché la soluzione colSum () sembrava fare più lavoro. Ma sul mio set di test (213 oss. Di 1614 variabili prima, contro 1377 variabili dopo) ci vogliono esattamente 3 volte di più. (Ma +1 per un approccio interessante.)
Darren Cook,

10

In ritardo al gioco, ma puoi anche usare il janitorpacchetto. Questa funzione rimuoverà le colonne che sono tutte NA e può essere cambiata per rimuovere anche le righe che sono tutte NA.

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



4

La risposta accettata non funziona con colonne non numeriche. Da questa risposta , quanto segue funziona con colonne contenenti diversi tipi di dati

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

Qualcun altro ha già pubblicato la stessa risposta in questa discussione 4 anni prima che tu ... Vedi la risposta di mnel di seguito.
André.B,

2

Altre opzioni con purrrpacchetto:

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

1

Spero che questo possa anche aiutare. Potrebbe essere trasformato in un singolo comando, ma ho trovato più facile da leggere dividendolo in due comandi. Ho fatto una funzione con le seguenti istruzioni e ho lavorato velocemente.

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

.SD consentirà di limitare la verifica a parte della tabella, se lo si desidera, ma prenderà l'intera tabella come


1

base RUn'opzione utile potrebbe essere colMeans():

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

0

Puoi usare il pacchetto Janitor remove_empty

library(janitor)

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

Inoltre, un altro approccio dplyr

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

O

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

questo è utile anche se si desidera escludere / mantenere la colonna con un determinato numero di valori mancanti, ad es

 df %>% select_if(colSums(!is.na(.))>500)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.