[英]R: how to drop rows from a data frame if the rows contain a certain value (for many columns)
我有一个包含许多变量(列)和行(观察值)的数据框。 我想从我的 dataframe 中删除包含值1
的行。
我知道我可以,鉴于
X Y
1 1 1
2 2 1
3 3 1
4 4 4
5 5 5
做这个:
test <- data.frame("X"=1:5, "Y"=c(1,1,1,4,5))
test[test$X>1 & test$Y>1, ]
并得到:
X Y
4 4 4
5 5 5
但我不想为 20 或 50 个变量写出data$var1 > 1 & data$var2 > 1...
来做如此简单的事情。
我怎样才能得到相同的结果而不必做大量的写作?
编辑: Big oof :这里建议的三种方法都没有产生相同数量的观察结果。 这是一个错误吗? 也许这是与NA交互的一些影响?
方法1)
df[!apply(df[, myCols], 1, function(x) any(x == 1)),]
> any(df == 1)
[1] TRUE
方法2)
removeRowsWithOnes <- function(df) {
rowsToRemoveIndices <- rowSums(df == 1) > 0
return(df[!rowsToRemoveIndices,])
}
> any(df == 1)
[1] NA
方法 3)(删除的行数与方法 2) 不同)
require(tidyverse)
df %>%
filter(
across(everything(), ~ . != 1)
)
> any(df == 1)
[1] NA
编辑 2:将 NA 添加到 df 后:
df <- data.frame("x"=c(1,NA,2,2,3,NA), "y"=c(NA,1,1,4,NA,NA))
x y
1 1 NA
2 NA 1
3 2 1
4 2 4
5 3 NA
6 NA NA
只有方法 3) 产生预期结果:
x y
1 2 4
2 3 NA
3 NA NA
编辑2:
请参阅@Jonas 的评论:
要使这两种方法起作用,您可以将 na.rm=TRUE 添加到 rowSums 和 any 的调用中。 此选项默认设置为 na.rm=FALSE(请参阅文档)
另一个可能的答案是使用tidyverse
require(tidyverse)
df %>%
filter(
across(everything(), ~ . != 1)
)
在数据框中的所有变量中,它保留了不同于 1 的行。
注意:如果您的数据中有NA
,此方法也会删除这些索引。 因此,我建议进行以下扩展;
df %>%
filter(
across(everything(), ~ . != 1 | is.na(.))
)
然后它保留所有不同于 1 的值而不删除NA
行。 否则,您可能会删除您打算保留的行(取决于您在做什么)。
比较运行时间
按照 Jonas 的例子,我尝试对所有解决方案进行基准测试;
# Using rowSums
removeRowsWithOnes <- function(df) {
rowsToRemoveIndices <- rowSums(df == 1) > 0
return(df[!rowsToRemoveIndices,])
}
# Using apply
removeRowsWithOnes2 <- function(df) {
df[!apply(df, 1, function(x) any(x == 1)),]
}
# Using tidyversr
removeRowsWithOnes3 <- function(df) {df %>%
filter(
across(everything(), ~ . != 1 | is.na(.))
)}
基准
n <- 1e5
set.seed(5555)
bigSampleData <- do.call("cbind",lapply(LETTERS, function(nam) setNames(data.frame(sample(1:1000,n,replace = TRUE)),nam)))
microbenchmark::microbenchmark(removeRowsWithOnes(bigSampleData),removeRowsWithOnes2(bigSampleData),removeRowsWithOnes3(bigSampleData),times=10)
结果
Unit: milliseconds
expr min lq mean median uq max neval cld
removeRowsWithOnes(bigSampleData) 35.57471 40.54827 77.64570 41.06107 60.34422 217.3363 10 b
removeRowsWithOnes2(bigSampleData) 217.34171 222.35136 227.90565 227.05570 229.02625 240.9274 10 c
removeRowsWithOnes3(bigSampleData) 17.42338 22.24363 23.34607 22.88563 23.72934 32.0293 10 a
您可以使用apply
:
df[!apply(df[, myCols], 1, function(x) any(x[!is.na(x)] == 1)),]
其中myCols
是列名的向量,如果它们包含 1,则要为其丢弃行。如果应考虑所有列df[, myCols]
替换为df
。
我建议使用以下 function:
removeRowsWithOnes <- function(df) {
rowsToRemoveIndices <- rowSums(df == 1) > 0
return(df[!rowsToRemoveIndices,])
}
这个 function 在处理数据帧时使用 Rs 高度优化的rowSums
函数和一个很好的比较特性。
为了给出一些基准,让我们把 det 的答案放在另一个 function 中:
removeRowsWithOnes2 <- function(df) {
df[!apply(df, 1, function(x) any(x == 1)),]
}
现在对大样本数据运行基准测试。
n <- 1e5
set.seed(5555)
bigSampleData <- do.call("cbind",lapply(LETTERS, function(nam) setNames(data.frame(sample(1:1000,n,replace = TRUE)),nam)))
microbenchmark::microbenchmark(removeRowsWithOnes(bigSampleData),removeRowsWithOnes2(bigSampleData),times=10)
在我的机器上,它给出了以下结果:
Unit: milliseconds
expr min lq mean median uq max neval
removeRowsWithOnes(bigSampleData) 59.6315 60.9947 78.8833 72.17005 88.4460 132.6954 10
removeRowsWithOnes2(bigSampleData) 352.3655 370.9296 416.3392 425.06935 442.7583 493.9709 10
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.