我有一个数据框,其中一些列包含NA值。
如何删除所有行都包含NA值的列?
试一下这个:
df <- df[,colSums(is.na(df))<nrow(df)]
df
大小相同的对象is.na(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]
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
data.frame
做同样的事情。这里没有什么真正需要 data.table
的东西。关键是 lapply
,它避免了 is.na(df)
执行整个对象的复制。+10 分为指出这一点。 - Matt Dowlebd1 <- bd[, unlist(lapply(bd, function(x) !all(is.na(x))))]
可以翻译为:将数据框bd中所有非空列提取出来,生成新的数据框bd1。具体实现是通过对bd使用lapply函数,判断每一列是否全部为空值is.na(x),再通过unlist函数将结果转换为向量形式进行子集索引。 - mnelfunction(x)
后面的 ,
—— 顺便说一句,谢谢你提供的示例。 - Thieme Hennis您现在可以使用带有where
选择器的select
。 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
dplyr
的解决方案,结果没有失望。谢谢! - Andrew Brēzaselect_if
在dplyr中已经被取代,因此最近的语法中,最后两行将是temp %>% select(where(not_all_na))
-- 尽管 select_if
仍可在dplyr 1.0.2 中使用。如果您不想在单独的行上定义函数,则也可以使用temp %>% select(where(~!all(is.na(.x))))
。 - mcstrothernot_any_na
。这是从哪里来的?我已经加载了 dplyr
..... - Sky Scraperjanitor
包。这个函数将删除所有为 NA 的列,并且可以更改为删除所有为 NA 的行。df <- janitor::remove_empty(df, which = "cols")
使用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(.)))
apply()
函数。
如果您有数据框,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
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
df[sapply(df, function(x) all(is.na(x)))] <- NULL
remove_empty
函数。library(janitor)
df %>%
remove_empty(c("rows", "cols")) #select either row or cols or both
library(dplyr)
df %>% select_if(~all(!is.na(.)))
或者
df %>% select_if(colSums(!is.na(.)) == nrow(df))
如果您只想排除/保留一定数量的缺失值列,这也是非常有用的,例如:
df %>% select_if(colSums(!is.na(.))>500)
base R
选项是 colMeans()
:df[, colMeans(is.na(df)) != 1]
df [colSums(!is.na(df))> 0]
,即使只剩下1列也会返回一个data.frame
并且仅在2个而不是3个位置上使用df
。(摘自当前已删除的帖子。) - GKidrop = FALSE
以避免丢失维度:df <- df[, colSums(is.na(df)) < nrow(df), drop = FALSE]
- ismirsehregal