从所有值为NA的数据框中删除列


149

我有一个数据帧麻烦,不能真正解决这个问题我自己:
数据帧具有任意的性质列每一行代表一个数据集

问题是:
如何摆脱所有行的值为NA的列

Answers:


155

试试这个:

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

3
这将创建一个与旧对象大小相同的对象,这是大对象上的内存问题。最好使用一个函数来减小大小。使用Filter或使用data.table的答案将有助于您的内存使用。
mtelesha 2015年

3
这似乎不适用于非数字列。
verbamour '02

如果它们重复,它将更改列名称
Peter.k,

97

到目前为止,提供的这两种方法在创建大数据集时(以及其他内存问题中)失败is.na(df),它们将成为与相同大小的对象df

这是两种更节省内存和节省时间的方法

一种使用方法 Filter

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

和使用data.table的方法(用于一般时间和内存效率)

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

使用大数据的示例(30列1e6行)

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
非常好。不过,您也可以这样做data.frame。这里没有什么真正需要的data.table。关键是lapply,可以避免使用复制整个对象is.na(df)。指出+10。
Matt Dowle 2012年

1
您将如何处理data.frame?@ matt-
dowle

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

6
@mnel我认为你需要删除,function(x)-顺便说一句感谢的例子
THIEME Hennis

1
您可以使用:=或set()更快地完成它吗?
skan

49

dplyr现在有一个select_if动词,在这里可能会有所帮助:

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

来到这里寻找dplyr解决方案。并不失望。谢谢!
安德鲁·布雷萨(AndrewBrēza)

我发现这有一个问题,即它也会删除大多数但不是全部缺失的变量
MBorg

15

另一种方法是使用该apply()功能。

如果您有data.frame

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

那么您可以apply()用来查看哪些列满足您的条件,因此只需使用一种apply方法,就可以简单地进行与Musa的答案相同的子集。

> !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
我希望这样做会更快,因为colSum()解决方案似乎正在做更多的工作。但是在我的测试集上(之前是1614个变量的213个变量,而之后是1377个变量),它花费的时间正好是原来的3倍。(但是+1是一种有趣的方法。)
Darren Cook

10

游戏晚了,但您也可以使用该janitor程序包。此函数将删除全部为NA的列,并且可以更改为也删除全部为NA的行。

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



4

接受的答案不适用于非数字列。根据此答案,以下内容适用于包含不同数据类型的列

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

在您问世4年之前,其他人已经在此主题中发布了相同的答案。请参阅下面的mnel答案。
André.B

2

purrr套件的另一种选择:

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

我希望这也会有所帮助。它可以做成一个命令,但是我发现将它分成两个命令对我来说更容易阅读。我按照以下说明进行了操作,并迅速实现了闪电。

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

如果您愿意,.SD允许将验证范围限制在表的一部分,但是它将整个表作为


1

一个方便的base R选择可能是colMeans()

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

0

您可以使用看门人包 remove_empty

library(janitor)

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

另外,另一种dplyr方法

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

要么

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

如果您只想排除/保留具有一定数量的缺失值的列,这也很有用,例如

 df %>% select_if(colSums(!is.na(.))>500)
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.