繁体   English   中英

R:如果行包含某个值(对于许多列),如何从数据框中删除行

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM