【问题标题】:How to remove rows with any zero value如何删除任何零值的行
【发布时间】:2023-11-06 09:35:02
【问题描述】:

我有一个问题要解决如何删除 R 中具有零值的行。另一方面,我可以使用 na.omit() 删除所有 NA 值或使用 complete.cases() 删除包含 NA 值的行。

有人知道如何删除 R 中的零值行吗?

例如:

之前

|    DateTime      | Mac1  | Mac2  | Mac3  | Mac4  |
----------------------------------------------------
| 2011-04-02 06:00 | 20    | 0     | 20    | 20    |  
| 2011-04-02 06:05 | 21    | 21    | 21    | 21    |  
| 2011-04-02 06:10 | 22    | 22    | 22    | 22    |  
| 2011-04-02 06:15 | 23    | 23    | 0     | 23    |  
| 2011-04-02 06:20 | 24    | 24    | 24    | 24    | 
| 2011-04-02 06:25 | 0     | 25    | 25    | 0     | 

之后

|    DateTime      | Mac1  | Mac2  | Mac3  | Mac4  |
----------------------------------------------------
| 2011-04-02 06:05 | 21    | 21    | 21    | 21    |  
| 2011-04-02 06:10 | 22    | 22    | 22    | 22    |  
| 2011-04-02 06:20 | 24    | 24    | 24    | 24    |  

【问题讨论】:

  • 一条路线:通过用 NA 替换零,将其简化为您已经解决的问题。
  • 感谢 Joran,您的回复。但是,我不明白,用 NA 替换零是什么意思?因为在我得到表之前我已经删除了 NAs 值。但仍有 0 的值。你能告诉我怎么做吗?
  • 好的,我想我必须使用此代码将 0 替换为 NA。data[which(data==0)] = NA
  • @YougyZ 我认为你需要data[which(data==0)] = NA_character_

标签: r dplyr rows zero


【解决方案1】:

可能的最短答案将受益于数字到逻辑的隐式强制,其中零转换为 FALSE,其他值转换为 TRUE,并使用if_any/if_all

library(dplyr)

df %>% filter(if_all(starts_with('Mac')))

【讨论】:

    【解决方案2】:

    在遇到类似问题后,我想出了这个:

    我想在第 1 列和第 9 列中保留零,因为它们在那里有意义,但将零从其余列中删除。此代码将在数据框中的第 2 列到第 8 列中产生大于零的值,而使第 1 列和第 9 列保持不变。

    dataset %>% filter(data.frame(dataset[,2:8]>0))
    

    【讨论】:

      【解决方案3】:

      这是一个更新的方法:

      library(dplyr)
      
      df %>%
        filter(!if_any(starts_with("Mac"), ~ . == 0))
      
                DateTime Mac1 Mac2 Mac3 Mac4
      1 2011-04-02 06:05   21   21   21   21
      2 2011-04-02 06:10   22   22   22   22
      3 2011-04-02 06:20   24   24   24   24
      

      【讨论】:

        【解决方案4】:

        由于 dplyr 1.0.0 弃用了 @Feng Mai 很好地展示的范围变体,这里是新语法的更新。这可能很有用,因为在这种情况下,across() 不起作用,我花了一些时间才找出解决方案如下。

        目标是提取列中至少包含一个 0 的所有行。

        df %>% 
          rowwise() %>% 
          filter(any(c_across(everything(.)) == 0))
        

        数据

        df <- data.frame(a = 1:4, b= 1:0, c=0:3)
        df <- rbind(df, c(0,0,0))
        df <- rbind(df, c(9,9,9))
        
        # A tibble: 4 x 3
        # Rowwise: 
              a     b     c
          <dbl> <dbl> <dbl>
        1     1     1     0
        2     2     0     1
        3     4     0     3
        4     0     0     0
        

        所以它不会正确返回包含所有 9 的最后一行。

        【讨论】:

          【解决方案5】:

          使用 tidyverse/dplyr,您还可以删除变量子集中具有任何零值的行:

          # variables starting with Mac must be non-zero
          filter_at(df, vars(starts_with("Mac")), all_vars((.) != 0))
          
          # variables x, y, and z must be non-zero
          filter_at(df, vars(x, y, z), all_vars((.) != 0))
          
          # all numeric variables must be non-zero
          filter_if(df, is.numeric, all_vars((.) != 0))
          

          【讨论】:

            【解决方案6】:

            在base R中,我们可以使用grep选择我们想要测试的列,将数据与0进行比较,使用rowSums选择所有非零值的行。

            cols <- grep("^Mac", names(df))
            df[rowSums(df[cols] != 0) == length(cols), ]
            
            #          DateTime Mac1 Mac2 Mac3 Mac4
            #1 2011-04-02 06:05   21   21   21   21
            #2 2011-04-02 06:10   22   22   22   22
            #3 2011-04-02 06:20   24   24   24   24
            

            使用反转逻辑执行此操作,但输出相同

            df[rowSums(df[cols] == 0) == 0, ]
            

            dplyr中,我们可以使用filter_at测试特定的列,使用all_vars选择所有值不等于0的行。

            library(dplyr)
            df %>%  filter_at(vars(starts_with("Mac")), all_vars(. != 0))
            

            数据

            df <- structure(list(DateTime = structure(1:6, .Label = c("2011-04-02 06:00", 
            "2011-04-02 06:05", "2011-04-02 06:10", "2011-04-02 06:15", "2011-04-02 06:20", 
            "2011-04-02 06:25"), class = "factor"), Mac1 = c(20L, 21L, 22L, 
            23L, 24L, 0L), Mac2 = c(0L, 21L, 22L, 23L, 24L, 25L), Mac3 = c(20L, 
            21L, 22L, 0L, 24L, 25L), Mac4 = c(20L, 21L, 22L, 23L, 24L, 0L
            )), class = "data.frame", row.names = c(NA, -6L))
            

            【讨论】:

              【解决方案7】:

              您可以使用 dplyr 包中的过滤器。

              我们称你的数据框为 df

              library(dplyr) df1 <- filter(df, Mac1 > 0, Mac2 > 0, Mac3 > 0, Mac4 > 0)

              df1 将只有条目大于零的行。希望这可以帮助。

              【讨论】:

                【解决方案8】:

                我会做以下事情。

                将零设置为 NA。

                 data[data==0] <- NA
                 data
                

                删除与 NA 关联的行。

                 data2<-data[complete.cases(data),]
                

                【讨论】:

                  【解决方案9】:

                  我更喜欢对 csgillespie 方法的简单改编,无需定义函数:

                  d[apply(d!=0, 1, all),]
                  

                  d 是您的数据框。

                  【讨论】:

                  • 这对我不起作用,但这有效:d[!apply(d==0, 1, all),]
                  【解决方案10】:

                  好吧,您可以将 0 换成 NA,然后使用其中一种解决方案,但为了区别,您可能会注意到,如果一个数字大于0,这样log 中的rowSums 只有在连续没有零的情况下才会是有限的。

                  dfr[is.finite(rowSums(log(dfr[-1]))),]
                  

                  【讨论】:

                  • +1 表示聪明,但在实践中我当然更喜欢all/any 解决方案...
                  【解决方案11】:

                  我可能会同意 Joran 的建议,即用 NA 替换 0,然后使用您提到的内置函数。如果您不能/不想这样做,一种方法是使用 any() 查找包含 0 的行并将其子集:

                  set.seed(42)
                  #Fake data
                  x <- data.frame(a = sample(0:2, 5, TRUE), b = sample(0:2, 5, TRUE))
                  > x
                    a b
                  1 2 1
                  2 2 2
                  3 0 0
                  4 2 1
                  5 1 2
                  #Subset out any rows with a 0 in them
                  #Note the negation with ! around the apply function
                  x[!(apply(x, 1, function(y) any(y == 0))),]
                    a b
                  1 2 1
                  2 2 2
                  4 2 1
                  5 1 2
                  

                  要实现 Joran 的方法,你应该开始这样的事情:

                  x[x==0] <- NA
                  

                  【讨论】:

                  • 无论如何,谢谢,但我已经用 csgillespie 解决方案做到了.. ;)
                  【解决方案12】:

                  有几种不同的方法可以做到这一点。我更喜欢使用apply,因为它很容易扩展:

                  ##Generate some data
                  dd = data.frame(a = 1:4, b= 1:0, c=0:3)
                  
                  ##Go through each row and determine if a value is zero
                  row_sub = apply(dd, 1, function(row) all(row !=0 ))
                  ##Subset as usual
                  dd[row_sub,]
                  

                  【讨论】: