【问题标题】:R - Remove rows from dataframe that contain only zeros in numeric columns, base R and pipe-friendly methods?R - 从数据框中删除仅在数字列中包含零的行、基本 R 和管道友好方法?
【发布时间】:2021-08-17 14:26:55
【问题描述】:

我想删除总和为 0 的所有行,但我在前 2 列中有因子列。我想出了一个 dplyr 解决方案,创建一个中间行和列,过滤掉总和为 0 的行,然后删除该行和列。

我想找到一种方法可以在不创建不必要的行和列的情况下使用基本 R 和 dplyr/tidyverse 管道友好方法。肯定有一段简单的单行代码可以实现这一点吗?

library(tidyverse)

df <- data.frame(person = rep(c("Ed", "Sue"), 6),
                id = paste0("plot",1:12),
                a = c(2, 0, 0, 0, 0, 1, 0, 0, 4, 0, 0, 0),
                b = c(0, 0, 6, 4, 0, 8, 1, 0, 0, 0, 1, 1),
                c = c(4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 8),
                d = c(0, 0, 0, 3, 0, 1, 0, 0, 9, 0, 1, 5),
                e = c(7, 0, 5, 0, 0, 1, 0, 0, 0, 0, 7, 0))


##create intermediate 'row.sum' column, filter rows that have all 0's, then remove row.sum column
df1 <- df %>% 
  dplyr::mutate(row.sum = a+b+c+d+e) %>% 
  dplyr::filter(row.sum != 0) %>% 
  dplyr::select(-row.sum)


#end result:
#  person     id a b c d e
#1     Ed  plot1 2 0 4 0 7
#2     Ed  plot3 0 6 0 0 5
#3    Sue  plot4 0 4 0 3 0
#4    Sue  plot6 1 8 0 1 1
#5     Ed  plot7 0 1 0 0 0
#6     Ed  plot9 4 0 0 9 0
#7     Ed plot11 0 1 3 1 7
#8    Sue plot12 0 1 8 5 0

【问题讨论】:

    标签: r dataframe dplyr


    【解决方案1】:

    这行得通吗:

    subset(df, rowSums(df[3:7]) != 0)
       person     id a b c d e
    1      Ed  plot1 2 0 4 0 7
    3      Ed  plot3 0 6 0 0 5
    4     Sue  plot4 0 4 0 3 0
    6     Sue  plot6 1 8 0 1 1
    7      Ed  plot7 0 1 0 0 0
    9      Ed  plot9 4 0 0 9 0
    11     Ed plot11 0 1 3 1 7
    12    Sue plot12 0 1 8 5 0
    

    【讨论】:

    • 应该可以不用!= 0,因为在 R 中 0 是 FALSE。但是需要强制为逻辑。
    • 您的代码可能最适合基本 R 情况。我还测试了!!rowSums(df[3:7]),它成功了。它可能会在 tidyversians 的头脑中造成混乱,因为 `!!在“rlang”语法中有不同的解释。
    • @IRTFM 确实如此,但在调用已保存的字符串对象时不是使用 bang bang 运算符吗? rland 使用不多,但现在!! 不会被{{...}} 取代。
    • 可能是这样,但是我刚才在filter内部尝试时仍然会抛出错误。
    【解决方案2】:

    dplyr 方法

    您只能将 rowSums 应用于数字列,使用 dplyrs filter()across(),以及帮助程序 where(is.numeric)

    library(dplyr)
    
    df%>%filter(rowSums(across(where(is.numeric)))!=0)
    
      person     id a b c d e
    1     Ed  plot1 2 0 4 0 7
    2     Ed  plot3 0 6 0 0 5
    3    Sue  plot4 0 4 0 3 0
    4    Sue  plot6 1 8 0 1 1
    5     Ed  plot7 0 1 0 0 0
    6     Ed  plot9 4 0 0 9 0
    7     Ed plot11 0 1 3 1 7
    8    Sue plot12 0 1 8 5 0
    

    如果您的数字列也有负值,则此方法(以及一些依赖于rowSums() 的方法)可能会失败。 在这种情况下,我们必须确保只保留至少包含any()非零值的行。这可以通过修改rowSums() 以包含条件.x!=0inside across() 来完成:

    df%>%filter(rowSums(across(where(is.numeric), ~.x!=0))>0)
    

    或者用逻辑运算符和Reduce()/reduce(),用下面的代码:

    library(dplyr)
    library(purrr)
    
    df%>%filter(pmap_lgl(select(., where(is.numeric)), ~any(c(...)!=0)))
    
    #or with purrr:reduce()#
    
    df%>%filter(across(where(is.numeric), ~.x!=0)%>%reduce(`|`))
    #or simply
    df%>%filter(reduce(across(where(is.numeric), ~.x!=0), `|`))
    

    基础 R 方法

    您可以使用带有[sapply(f, is.numeric) 的基本子集来创建逻辑索引以仅选择数字列以提供给不等式运算符!=,然后取最终逻辑矩阵的rowSums(),即创建并仅选择 rowSums >0 的行:

    df[rowSums(df[,sapply(df, is.numeric)]!=0)>0,]
    

    编辑

    我们可以从对数字向量调用逻辑函数所产生的强制中受益。 as.logical() 会将零评估为 FALSE,并将任何非零数字评估为 TRUE。 x|x 和嵌套的爆炸符号 !(!) 也会这样做。这与将元素与零进行比较的其他解决方案一致,因此比rowSumssolution 更加一致。

    一个例子:

    vector<-c(0,1,2,-1)
    identical(as.logical(vector), vector|vector, vector!=0, !(!vector))
    
    [1] TRUE
    
    

    考虑到这一点,有一些巧妙的方法可以解决这个问题:

    df%>%filter(reduce(across(where(is.numeric), as.logical), `|`))
    #or simply
    df%>%filter(reduce(across(where(is.numeric)), `|`))
    #and with base R:
    df[Reduce(`|`, df[sapply(df, is.numeric)]),]
    

    迄今为止最干净的,新的if_any()

    df%>%filter(if_any(where(is.numeric)))
    

    【讨论】:

    • 干得好!这是对此类问题的详尽解释,以供将来参考。
    • 谢谢,亲爱的@Anoushiravan R。我也总是从你的贡献中学到很多东西。
    • 非常感谢。这对我来说是一个很大的乐趣,你真是太好了。事实上,我们都在这里互相学习。
    【解决方案3】:

    这是基本逻辑和整洁语法的混合体。无可否认,绕过 rlang 的替代语法有点折磨。

    df1 <- df %>% filter(!(!rowSums(`[`(.,,3:7))))
    > df1
      person     id a b c d e
    1     Ed  plot1 2 0 4 0 7
    2     Ed  plot3 0 6 0 0 5
    3    Sue  plot4 0 4 0 3 0
    4    Sue  plot6 1 8 0 1 1
    5     Ed  plot7 0 1 0 0 0
    6     Ed  plot9 4 0 0 9 0
    7     Ed plot11 0 1 3 1 7
    8    Sue plot12 0 1 8 5 0
    

    需要用圆括号分隔两个感叹号,因为!!filter的逻辑环境“rlang”中的不同操作。 (请注意,如果可能存在 NA,则 rwoSums 确实有一个 na.rm 参数,并且应该忽略这些参数。

    这是一个基本解决方案。 !!!= 0 的替代品吗

    df1 <- df[ as.logical(rowSums(df[3:7]) ), ]
    

    (我认为坚持!= 0 可能会更好)

    【讨论】:

    • 我喜欢这种!(! 方法,但想知道as.logical 是否不会做同样的事情,但具有卓越的可读性
    • 想了想,我认为!= 0 大概是清楚的。我仍然需要!as.logical(.),这似乎几乎是折磨。
    • 我其实在想df %&gt;% filter(as.logical(rowSums([(.,,3:7))))
    • 查看我的更新答案以了解我的意思。感谢您的讨论,@IRTFM
    • 删除了我的评论。
    【解决方案4】:

    不使用rowSums的替代方案

    • 刚刚在rowwise filter 中使用all 将所需的值转换为逻辑测试== 0cur_data()
    
    library(dplyr)
    
    df %>% rowwise() %>%
      filter(!all(cur_data()[-c(1:2)] == 0))
    
    #> # A tibble: 8 x 7
    #> # Rowwise: 
    #>   person id         a     b     c     d     e
    #>   <chr>  <chr>  <dbl> <dbl> <dbl> <dbl> <dbl>
    #> 1 Ed     plot1      2     0     4     0     7
    #> 2 Ed     plot3      0     6     0     0     5
    #> 3 Sue    plot4      0     4     0     3     0
    #> 4 Sue    plot6      1     8     0     1     1
    #> 5 Ed     plot7      0     1     0     0     0
    #> 6 Ed     plot9      4     0     0     9     0
    #> 7 Ed     plot11     0     1     3     1     7
    #> 8 Sue    plot12     0     1     8     5     0
    

    reprex package (v2.0.0) 于 2021 年 5 月 30 日创建

    【讨论】:

      【解决方案5】:

      我们可以逐行计算行总和并使用slice

      library(dplyr)
      df %>%
        rowwise() %>% 
        slice(unique(c(which(sum(c_across(where(is.numeric))) != 0))))
      

      输出:

        person id         a     b     c     d     e
        <chr>  <chr>  <dbl> <dbl> <dbl> <dbl> <dbl>
      1 Ed     plot1      2     0     4     0     7
      2 Ed     plot3      0     6     0     0     5
      3 Sue    plot4      0     4     0     3     0
      4 Sue    plot6      1     8     0     1     1
      5 Ed     plot7      0     1     0     0     0
      6 Ed     plot9      4     0     0     9     0
      7 Ed     plot11     0     1     3     1     7
      8 Sue    plot12     0     1     8     5     0
      

      【讨论】:

        猜你喜欢
        • 2018-05-16
        • 1970-01-01
        • 2015-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-13
        • 2011-12-16
        相关资源
        最近更新 更多