R - 从数据框中删除在数字列中仅包含零的行、基本 R 和管道友好方法?

E. *_*ore 3 r dataframe dplyr

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

我想找到一种方法来使其工作而无需创建不必要的 rowsum 列,两者都使用基本 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
Run Code Online (Sandbox Code Playgroud)

Gue*_*sBF 5

一个 dplyr 方法

您只能将 rowSums 应用于数字列,使用 dplyrsfilter()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
Run Code Online (Sandbox Code Playgroud)

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

df%>%filter(rowSums(across(where(is.numeric), ~.x!=0))>0)
Run Code Online (Sandbox Code Playgroud)

或者使用逻辑运算符 and 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), `|`))
Run Code Online (Sandbox Code Playgroud)

一个基本的R方法

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

df[rowSums(df[,sapply(df, is.numeric)]!=0)>0,]
Run Code Online (Sandbox Code Playgroud)

编辑

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

一个例子:

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

[1] TRUE

Run Code Online (Sandbox Code Playgroud)

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

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)]),]
Run Code Online (Sandbox Code Playgroud)

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

df%>%filter(if_any(where(is.numeric)))
Run Code Online (Sandbox Code Playgroud)

  • 做得好!对此类问题进行了详尽的解释,以供将来参考。 (2认同)