我在使用数据框时遇到了问题,我自己无法真正解决该问题:数据框
具有任意属性作为列,每一行代表一个数据集。
问题是:
如何摆脱所有行的值为 NA的列?
试试这个:
df <- df[,colSums(is.na(df))<nrow(df)]
迄今为止提供的两种方法都失败了,因为它们创建的大型数据集(以及其他内存问题)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]
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
您现在可以使用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
游戏迟到了,但您也可以使用该janitor
包。此函数将删除全部为 NA 的列,并且可以更改为删除全部为 NA 的行。
df <- janitor::remove_empty(df, which = "cols")
另一种方法是使用该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
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(.)))
df[sapply(df, function(x) all(is.na(x)))] <- NULL
您可以使用 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)
一个老问题,但我认为我们可以用更简单的 data.table 解决方案更新@mnel 的好答案:
DT[, .SD, .SDcols = \(x) !all(is.na(x))]
(我正在使用\(x)
R>=4.1 中可用的新 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
我希望这也可能有所帮助。它可以做成一个命令,但我发现把它分成两个命令更容易阅读。我按照以下说明制作了一个函数,并且工作得很快。
naColsRemoval = function (DataTable) {
na.cols = DataTable [ , .( which ( apply ( is.na ( .SD ) , 2 , all ) ) )]
DataTable [ , unlist (na.cols) := NULL , with = F]
}
如果您愿意,.SD 将允许将验证限制在表格的一部分,但它会将整个表格作为
一个方便的base R
选择可能是colMeans()
:
df[, colMeans(is.na(df)) != 1]
根据我在应用以前的答案时遇到问题的经验,我发现我需要修改他们的方法才能实现这里的问题:
如何摆脱所有行的值为NA的列?
首先请注意,我的解决方案仅在您没有重复列时才有效(该问题在此处处理(在堆栈溢出时)
其次,它使用dplyr
.
代替
df <- df %>% select_if(~all(!is.na(.)))
我发现有效的是
df <- df %>% select_if(~!all(is.na(.)))
关键是“非”符号“!” 需要在全称量词之外。即select_if
运算符作用于列。在这种情况下,它只选择那些不满足标准的
每个元素都等于“NA”
janitor::remove_constant() 很好地做到了这一点。