如何删除仅包含NA的列?


83

我有一个data.frame,其中包含一些具有所有NA值的列,如何从data.frame中删除它们。

我可以使用该功能吗

na.omit(...) 

指定一些其他参数?


1
嗨,您好!请使您的帖子可重复。阅读有关如何制作一个很好的可复制示例的文章。谢谢。
阿伦(Arun)


你可以发表head(data)吗?您要删除相应的列或行吗?
Nishanth 2013年

@ e4e5f4我想删除相应的列(我要删除的所有列的值都是NA)
Lorenzo Rigamonti 2013年

Answers:


123

一种方法是:

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

如果一列中NA的数量等于行数,则它必须完全是NA。

或类似

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

1
如何删除NA阈值以上的列?或百分比(假设高于50%)?
徒弟

2
@lovedynasty可能最好是提交一个单独的问题,前提是您自发布评论以来尚未提出此问题。但是无论如何,您总是可以做类似的事情,df[, colSums(is.na(df)) < nrow(df) * 0.5]即只保留至少50%非空白的列。
MadScone 2015年

2
df[, colSums(is.na(df)) != nrow(df) - 1]由于对角线始终是对角线,因此使用相关矩阵的人必须使用1
Boern 2015年

9
也可以将其与dplyr(0.5.0版)select_if函数一起使用。 df %>% select_if(colSums(!is.na(.)) > 0)
Stefan Avey's

@MadScone,它在df [,colSums(is.na(df))!= nrow(df)]处给我语法错误,而在“!”处给我语法错误!在df [colSums(!is.na(df))> 0]中。我错过了什么吗
Aravind S

52

这是dplyr解决方案:

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

4
在约1.5万行和约5000列的情况下,这确实是永远的事。
EngrStudent

@EngrStudent接受的答案的解决方案是否更快?
约翰尼

已经好几年了 我不记得了 DJV在下面有一个不错的时间发布。
EngrStudent


24

好像您要删除包含ALL NA的列,而使某些列包含具有NAs的行。我会这样做(但是我敢肯定有一种有效的矢量化状态:

#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

如果发现自己要删除具有任何NA值的列,则只需将all上面的命令更改为即可any


data.frame具有两种类型的列:一种在whohc中所有值都是数字,而另一种其中所有值都是NA
Lorenzo Rigamonti 2013年

因此,这将起作用。仅删除所有值为的列NA
西蒙·奥汉隆

1
好的解决方案。我会这样做,apply(is.na(df), 1, all)只是因为它稍微整洁并且一次is.na()用于所有df行,而不是一次(显示速度更快)。
MadScone

@MadScone好提示-看起来更整洁。您应该跨列而不是行应用。
西蒙·奥汉隆

@MadScone对评论的评论5分钟后被锁定。我不用担心,这没什么大不了的!!:-)
Simon O'Hanlon

19

直观的脚本:dplyr::select_if(~!all(is.na(.)))。从字面上看,它仅保留不丢失所有元素的列。(删除所有缺少元素的列)。

> 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

另一种选择 Filter

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

注意:数据来自@Simon O'Hanlon的帖子。


5

因为性能对我来说真的很重要,所以我对以上所有功能进行了基准测试。

注意:数据来自@Simon O'Hanlon的帖子。仅尺寸为15000,而不是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

结果:

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)

在此处输入图片说明

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

在此处输入图片说明


有时,第一次迭代是JIT编译的,因此它的时间很差,而且不是很典型。我认为有趣的是,较大的样本量对分布的右尾有何影响。这是一项很好的工作。
EngrStudent

我再次运行它,不确定是否更改了情节。关于分配,的确如此。有时间的时候,我可能应该比较不同的样本数量。
DJV

1
如果您qqplot(ggplot2.tidyverse.org/reference/geom_qq.html)是其中一种趋势,例如“ akrun”,那么我敢打赌,有一点与其余分布有很大不同。其余的代表重复运行将花费多长时间,但这代表了一次运行将发生什么。有句老话:您可以拥有20年的经验,或者只有20年的经验值。
EngrStudent

非常好!令我惊讶的是,有几个样本处于极端状态。我不知道为什么这些东西要贵得多。JIT可能是1或2,但不是20。条件?会打扰吗 其他?再次感谢您的更新。
EngrStudent

不客气,谢谢您的想法。不知道,我实际上允许它“自由”运行。
DJV
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.