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

Gna*_*ark 127 r apply dataframe

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

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

teu*_*cer 141

试试这个:

df <- df[,colSums(is.na(df))<nrow(df)]
Run Code Online (Sandbox Code Playgroud)

  • 这会创建一个与旧对象大小相对应的对象,这是大对象上的内存问题.最好使用函数来减小大小.使用Filter或使用data.table的答案将有助于您的内存使用. (3认同)
  • 这似乎不适用于非数字列. (3认同)
  • 另一种选择可能是“df[colSums(!is.na(df)) &gt; 0]”,如果只剩下 1 列,它也会返回一个“data.frame”,并且仅在 2 个而不是 3 个位置上使用“df” 。(取自当前已删除的帖子。) (2认同)

mne*_*nel 88

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

以下两种方法具有更高的内存和时间效率

使用的方法 Filter

Filter(function(x)!all(is.na(x)), df)
Run Code Online (Sandbox Code Playgroud)

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

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

使用大数据的示例(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 
Run Code Online (Sandbox Code Playgroud)

  • @s_a,`bd1 < - bd [,unlist(lapply(bd,function(x),!all(is.na(x))))]` (6认同)
  • @mnel I think you need to remove the `,` after `function(x)` - thanks for the example btw (6认同)
  • 非常好.不过,你可以对`data.frame`做同样的事情.这里没有什么需要`data.table`.关键是`lapply`,它避免了`is.na(df)`完成的整个对象的复制.+10用于指出. (4认同)

zac*_*ack 37

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
Run Code Online (Sandbox Code Playgroud)

  • 来到这里寻找“dplyr”解决方案。没有失望。谢谢! (3认同)
  • `select_if` 现在在 dplyr 中被取代,所以最后两行在最新的语法中将是 `temp %&gt;% select(where(not_all_na))` ——尽管 `select_if` 从 dplyr 1.0.2 开始仍然有效。如果您不想在单独的行上定义函数,那么 `temp %&gt;% select(where(~!all(is.na(.x))))` 也可以使用。 (2认同)

Sve*_*enB 20

套餐的另一个选项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(.)))
Run Code Online (Sandbox Code Playgroud)


mro*_*opa 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)
                  )
Run Code Online (Sandbox Code Playgroud)

那么你可以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
Run Code Online (Sandbox Code Playgroud)

  • 我希望这会更快,因为colSum()解决方案似乎做得更多.但是在我的测试集上(之前的1614个变量中的213个,之后是1377个变量),它需要的时间要长3倍.(但+1是一个有趣的方法.) (3认同)

Gra*_*ant 8

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

DT[, .SD, .SDcols = \(x) !all(is.na(x))]
Run Code Online (Sandbox Code Playgroud)

(我正在使用 R>=4.1 中可用的新\(x)lambda 函数语法,但实际上关键是将逻辑子集传递给.SDcols.

速度相当。

DT[, .SD, .SDcols = \(x) !all(is.na(x))]
Run Code Online (Sandbox Code Playgroud)


And*_*é.B 6

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

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


jpm*_*ris 5

df[sapply(df, function(x) all(is.na(x)))] <- NULL
Run Code Online (Sandbox Code Playgroud)