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

问题描述 投票:0回答:15

我有一个数据框,其中一些列包含 NA 值。

如何删除all行包含 NA 值的列?

r apply dataframe
15个回答
213
投票

试试这个:

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

134
投票

迄今为止提供的两种方法在处理大型数据集时失败,因为(除了其他内存问题)它们创建了

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 

107
投票

更新

您现在可以将

select
where
选择助手一起使用。
select_if
已被取代,但从 dplyr 1.0.2 起仍然有效。 (感谢@mcstrother 引起人们的注意)。

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(where(not_all_na))
  x  y
1 1  1
2 2  2
3 3 NA
4 4  4
5 5  5

> temp %>% select(where(not_any_na))
  x
1 1
2 2
3 3
4 4
5 5

旧答案

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

> 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

36
投票

游戏较晚,但您也可以使用

janitor
包。此函数将删除全部 NA 的列,并且可以更改为删除全部 NA 的行。

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

22
投票

带有

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

17
投票

另一种方法是使用

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()
来查看哪些列满足您的条件,因此您可以简单地执行与 Musa 答案中相同的子集设置,仅使用
apply
方法。

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

8
投票
df[sapply(df, function(x) all(is.na(x)))] <- NULL

8
投票

一个老问题,但我认为我们可以用更简单的 data.table 解决方案更新 @mnel 的好答案:

DT[, .SD, .SDcols = \(x) !all(is.na(x))]

(我正在使用 R>=4.1 中可用的新

\(x)
lambda 函数语法,但实际上关键是通过
.SDcols
传递逻辑子集。

速度相当。

microbenchmark::microbenchmark(
  which_unlist = DT[, which(unlist(lapply(DT, \(x) !all(is.na(x))))), with=FALSE],
  sdcols       = DT[, .SD, .SDcols = \(x) !all(is.na(x))],
  times = 2
)
#> Unit: milliseconds
#>          expr      min       lq     mean   median       uq      max neval cld
#>  which_unlist 51.32227 51.32227 56.78501 56.78501 62.24776 62.24776     2   a
#>        sdcols 43.14361 43.14361 49.33491 49.33491 55.52621 55.52621     2   a

4
投票

您可以使用Janitor包

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)

1
投票

一个方便的

base R
选项可以是
colMeans()

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

1
投票

根据我在应用以前的答案时遇到困难的经验,我发现我需要修改他们的方法才能实现这里的问题:

如何删除所有行的值为 NA 的列?

首先请注意,我的解决方案仅在没有重复列的情况下才有效(该问题已在此处处理(在堆栈溢出时)

其次,它使用

dplyr

而不是

df <- df %>% select_if(~all(!is.na(.)))
我发现有效的是

df <- df %>% select_if(~!all(is.na(.)))
重点是“非”符号“!”需要位于全称量词之外。 IE。 

select_if

 运算符作用于列。在这种情况下,它仅选择那些
满足标准的

每个元素都等于“NA”


1
投票
library(dplyr) # create a sample data frame df <- data.frame(x = c(1, 2, NA, 4), y = c(NA, NA, NA, NA), z = c(6, 7, NA, 9)) # remove columns with all NAs df <- df %>% select_if(~!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 将允许将验证限制在表的一部分(如果您愿意),但它会将整个表视为


0
投票
janitor::remove_constant()
做得很好。


0
投票
您可以简单地删除所有只有“NA”值的列:

df.dropna(axis=1, how='all', inplace=True)
事情就是这样

axis = 1 -> 删除列(0 表示行),

how = 'all' -> 删除所有值为 'NA' 的列(其他选项为 'any' ,这意味着删除所有具有 1 个或多个 'NA' 值的列),

inplace = True -> 不需要创建副本,在该副本中进行更改并返回它,此选项告诉 python 在同一个数据帧对象中执行此操作(内存高效)。

希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.