Làm cách nào để xóa các cột CHỈ chứa NA?


83

Tôi có data.frame chứa một số cột có tất cả các giá trị NA, làm cách nào để xóa chúng khỏi data.frame.

Tôi có thể sử dụng chức năng không

na.omit(...) 

chỉ rõ một số luận cứ bổ sung?


1
Xin chào! Hãy làm cho bài viết của bạn có thể tái tạo. Đọc bài đăng cách tạo một ví dụ có thể tái tạo tuyệt vời về cách thực hiện điều này. Cảm ơn bạn.
Arun

Bài viết này có giúp ích gì không? stackoverflow.com/questions/4862178/…
Arun

bạn có thể đăng head(data)? Bạn có muốn xóa các cột hoặc hàng tương ứng không?
Nishanth

@ e4e5f4 Tôi muốn xóa các cột tương ứng (tất cả các giá trị của các cột tôi muốn xóa là NA)
Lorenzo Rigamonti

Câu trả lời:


123

Một cách để làm điều đó:

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

Nếu số NA trong một cột bằng số hàng thì nó phải hoàn toàn là NA.

Hoặc tương tự

df[colSums(!is.na(df)) > 0]

1
Làm cách nào để xóa các cột có nhiều hơn ngưỡng NA? hay bằng Tỷ lệ phần trăm (giả sử trên 50%)?
discipulus

2
@lovedyosystem Có lẽ tốt nhất bạn nên gửi một câu hỏi riêng biệt, giả sử rằng bạn chưa gửi câu hỏi này kể từ khi đăng nhận xét của mình. Nhưng dù sao đi nữa, bạn luôn có thể làm điều gì đó chẳng hạn như df[, colSums(is.na(df)) < nrow(df) * 0.5]chỉ giữ các cột có ít nhất 50% ô trống.
MadScone

2
Những người làm việc với một ma trận tương quan phải sử dụng df[, colSums(is.na(df)) != nrow(df) - 1]kể từ khi đường chéo luôn là1
Boern

9
Có thể sử dụng điều này với hàm select_if dplyr (phiên bản 0.5.0). df %>% select_if(colSums(!is.na(.)) > 0)
Stefan Avey

@MadScone nó đang cho tôi lỗi cú pháp tại "," cho df [, colSums (is.na (df))! = Nrow (df)] và lỗi cú pháp tại "!" trong df [colSums (! is.na (df))> 0]. Tôi có thiếu cái gì không
Aravind S

52

Đây là một giải pháp dplyr:

df %>% select_if(~sum(!is.na(.)) > 0)

4
Với ~ 15 nghìn hàng và ~ 5 nghìn cột, điều này thực sự diễn ra mãi mãi.
EngrStudent

@EngrStudent Có nhanh hơn với giải pháp của câu trả lời được chấp nhận không?
johnny

Đó là một số năm. Tôi không nhớ. DJV có một bài viết về thời điểm tốt bên dưới.
EngrStudent


24

Có vẻ như bạn muốn loại bỏ CHỈ các cột có TẤT CẢ NA các cột, để lại các cột có một số hàng có NAs. Tôi sẽ làm điều này (nhưng tôi chắc chắn có một cảnh báo vectorised hiệu quả:

#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

Nếu bạn thấy mình trong tình huống muốn xóa các cột có bất kỳ NAgiá trị nào, bạn có thể chỉ cần thay đổi alllệnh ở trên thành any.


Các data.frame có hai loại cột: một trong whohc tất cả các giá trị là số và khác trong đó tất cả các giá trị là NA
Lorenzo Rigamonti

Vì vậy, điều này sẽ hoạt động sau đó. Nó chỉ loại bỏ các cột là TẤT CẢ các giá trị NA.
Simon O'Hanlon

1
Giải pháp tốt. Tôi sẽ làm apply(is.na(df), 1, all)mặc dù chỉ vì nó hơi gọn gàng hơn và is.na()được sử dụng trên tất cả dfhơn là một hàng tại một thời điểm (hiển thị nhanh hơn một chút).
MadScone

@MadScone mẹo hay - trông gọn gàng hơn. Tuy nhiên, bạn nên áp dụng trên các cột chứ không phải hàng.
Simon O'Hanlon

@MadScone Các chỉnh sửa bị khóa sau 5 phút về nhận xét. Tôi không nên lo lắng, không có vấn đề gì đâu !! :-)
Simon O'Hanlon

19

Một kịch bản trực quan: dplyr::select_if(~!all(is.na(.))). Theo nghĩa đen, nó chỉ giữ các cột không thiếu tất cả các phần tử. (để xóa tất cả các cột thiếu phần tử).

> 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

17

Một lựa chọn khác với Filter

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

LƯU Ý: Dữ liệu từ bài đăng của @Simon O'Hanlon.


5

Vì hiệu suất thực sự quan trọng đối với tôi, tôi đã đánh giá tất cả các chức năng ở trên.

LƯU Ý: Dữ liệu từ bài đăng của @Simon O'Hanlon. Chỉ với kích thước 15000 thay vì 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

Các kết quả:

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)

nhập mô tả hình ảnh ở đây

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

nhập mô tả hình ảnh ở đây


Đôi khi lần lặp đầu tiên là một JIT được biên dịch, vì vậy nó có thời gian rất kém, và không đặc trưng. Tôi nghĩ rằng thật thú vị khi kích thước mẫu lớn hơn tác động đến phần đuôi bên phải của phân phối. Đây là công việc tốt.
EngrStudent

Tôi chạy lại một lần nữa, không chắc mình đã thay đổi cốt truyện. Về việc phân phối, thực sự. Tôi có lẽ nên so sánh các kích thước mẫu khác nhau khi tôi có thời gian.
DJV

1
nếu bạn qqplot ( ggplot2.tidyverse.org/reference/geom_qq.html ) một trong các xu hướng, chẳng hạn như "akrun" thì tôi cá là có một điểm rất khác so với phân phối của phần còn lại. Phần còn lại thể hiện thời gian mất bao lâu nếu bạn chạy nó nhiều lần, nhưng điều đó thể hiện điều gì xảy ra nếu bạn chạy nó một lần. Có một câu nói cũ: bạn có thể có 20 năm kinh nghiệm hoặc bạn chỉ có thể có một năm kinh nghiệm đáng giá 20 lần.
EngrStudent

rất đẹp! Tôi ngạc nhiên bởi một số mẫu ở đuôi cực kỳ. Tôi tự hỏi tại sao nó lại đắt hơn nhiều như vậy. JIT có thể là 1 hoặc 2 nhưng không phải là 20. Điều kiện? Gián đoạn? Khác? Cảm ơn một lần nữa cho bản cập nhật.
EngrStudent

Bạn được chào đón, cảm ơn bạn vì những suy nghĩ. Không biết, tôi thực sự đã cho phép nó chạy "tự do".
DJV
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.