Hapus kolom dari kerangka data di mana SEMUA nilai adalah NA

149

Saya mengalami masalah dengan kerangka data dan saya sendiri tidak bisa menyelesaikan masalah itu:
Kerangka data memiliki sifat sewenang-wenang sebagai kolom dan setiap baris mewakili satu set data .

Pertanyaannya adalah:
Bagaimana cara menyingkirkan kolom di mana untuk SEMUA baris nilainya NA ?

Gnark
sumber

Jawaban:

155

Coba ini:

df <- df[,colSums(is.na(df))<nrow(df)]
penggoda
sumber
3
Ini menciptakan objek ukuran objek lama yang merupakan masalah dengan memori pada objek besar. Lebih baik menggunakan fungsi untuk mengurangi ukuran. Jawabannya di bawah ini menggunakan Filter atau menggunakan data.tabel akan membantu penggunaan memori Anda.
mtelesha
3
Tampaknya ini tidak berfungsi dengan kolom non-numerik.
verbamour
Itu mengubah nama kolom jika mereka digandakan
Peter.k
97

Dua pendekatan yang ditawarkan sejauh ini gagal dengan set data besar sebagai (di antara masalah memori lainnya) yang mereka buat is.na(df), yang akan menjadi objek dengan ukuran yang sama df.

Berikut adalah dua pendekatan yang lebih hemat memori dan waktu

Pendekatan menggunakan Filter

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

dan pendekatan menggunakan data.table (untuk efisiensi waktu dan memori umum)

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

contoh menggunakan data besar (30 kolom, 1e6 baris)

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
sumber
6
Sangat bagus. Anda bisa melakukan hal yang sama dengannya data.frame. Tidak ada yang perlu di sini data.table. Kuncinya adalah lapply, yang menghindari salinan seluruh objek yang dilakukan oleh is.na(df). +10 untuk menunjukkannya.
Matt Dowle
1
Bagaimana Anda melakukannya dengan data.frame? @ matt-dowle
s_a
8
@s_a, bd1 <- bd[, unlist(lapply(bd, function(x), !all(is.na(x))))]
mnel
6
@mnel Saya pikir Anda perlu menghapus ,setelah function(x)- terima kasih untuk contoh btw
Thieme Hennis
1
Bisakah Anda melakukannya lebih cepat dengan: = atau dengan set ()?
skan
49

dplyrsekarang memiliki select_ifkata kerja yang mungkin berguna di sini:

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
sumber
Datang ke sini mencari dplyrsolusinya. Tidak kecewa. Terima kasih!
Andrew Brēza
Saya menemukan ini memiliki masalah yang juga akan menghapus variabel dengan sebagian besar tetapi tidak semua nilai hilang
MBorg
15

Cara lain adalah dengan menggunakan apply()fungsi tersebut.

Jika Anda memiliki data.frame

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

maka Anda dapat menggunakan apply()untuk melihat kolom mana yang memenuhi kondisi Anda dan sehingga Anda dapat melakukan subsetting yang sama seperti dalam jawaban oleh Musa, hanya dengan applypendekatan.

> !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
sumber
3
Saya berharap ini lebih cepat, karena solusi colSum () tampaknya melakukan lebih banyak pekerjaan. Tetapi pada set pengujian saya (213 obs. 1614 variabel sebelumnya, vs 1377 variabel sesudahnya) dibutuhkan tepat 3 kali lebih lama. (Tapi +1 untuk pendekatan yang menarik.)
Darren Cook
10

Terlambat ke permainan tetapi Anda juga dapat menggunakan janitorpaket. Fungsi ini akan menghapus kolom yang semuanya NA, dan dapat diubah untuk menghapus baris yang semuanya NA juga.

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

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

Jawaban yang diterima tidak berfungsi dengan kolom non-numerik. Dari jawaban ini , berikut ini berfungsi dengan kolom yang berisi tipe data berbeda

Filter(function(x) !all(is.na(x)), df)
jeromeResearch
sumber
Orang lain telah memposting jawaban yang sama di utas ini 4 tahun sebelum Anda ... Lihat jawaban mnel di bawah ini.
André.B
2

Opsi lain dengan 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
sumber
1

Saya harap ini juga dapat membantu. Itu bisa dibuat menjadi satu perintah, tetapi saya merasa lebih mudah bagi saya untuk membaca dengan membaginya dalam dua perintah. Saya membuat fungsi dengan instruksi berikut dan bekerja dengan cepat.

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

.SD akan memungkinkan untuk membatasi verifikasi menjadi bagian dari tabel, jika Anda mau, tetapi akan menjadikan seluruh tabel sebagai

Luis M. Nieves
sumber
1

Pilihan praktis base Rbisa berupa colMeans():

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

Anda dapat menggunakan paket Petugas Kebersihan remove_empty

library(janitor)

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

Juga, pendekatan dplyr lain

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

ATAU

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

ini juga berguna jika Anda hanya ingin mengecualikan / menyimpan kolom dengan sejumlah nilai yang hilang misalnya

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