如何使用 dplyr 管道删除所有列均为零的行

How to remove rows where all columns are zero using dplyr pipe

提问人:littleworth 提问时间:3/15/2018 更新时间:11/11/2022 访问量:15307

问:

我有以下数据框:

dat <- structure(list(`A-XXX` = c(1.51653275922944, 0.077037240321129, 
0), `fBM-XXX` = c(2.22875185527511, 0, 0), `P-XXX` = c(1.73356698481106, 
0, 0), `vBM-XXX` = c(3.00397859609183, 0, 0)), .Names = c("A-XXX", 
"fBM-XXX", "P-XXX", "vBM-XXX"), row.names = c("BATF::JUN_AHR", 
"BATF::JUN_CCR9", "BATF::JUN_IL10"), class = "data.frame")

dat 
#>                     A-XXX  fBM-XXX    P-XXX  vBM-XXX
#> BATF::JUN_AHR  1.51653276 2.228752 1.733567 3.003979
#> BATF::JUN_CCR9 0.07703724 0.000000 0.000000 0.000000
#> BATF::JUN_IL10 0.00000000 0.000000 0.000000 0.000000

我可以使用以下命令删除包含所有列零的行:

> dat <- dat[ rowSums(dat)!=0, ]
> dat
                    A-XXX  fBM-XXX    P-XXX  vBM-XXX
BATF::JUN_AHR  1.51653276 2.228752 1.733567 3.003979
BATF::JUN_CCR9 0.07703724 0.000000 0.000000 0.000000

但是我怎样才能用 dplyr 的管道风格做到这一点呢?

r dplyr tidyverse

评论

0赞 akrun 3/15/2018
您说要保留行名称。这就是我的代码正在做的事情

答:

3赞 akrun 3/15/2018 #1

我们可以使用 from 根据逻辑向量获取行和数据集的总和reducepurrrfilter

library(tidyverse)
dat %>%
    reduce(`+`) %>%
    {. != 0} %>% 
   filter(dat, .)
#       A-XXX  fBM-XXX    P-XXX  vBM-XXX
#1 1.51653276 2.228752 1.733567 3.003979
#2 0.07703724 0.000000 0.000000 0.000000

注意:在 中,row.names 被剥离。最好稍后创建新列或分配 row.names%>%


如果我们还需要行名,请尽早创建一个行名列,然后使用它来更改末尾的行名

dat %>%
  rownames_to_column('rn') %>%
  filter(rowSums(.[-1]) != 0) %>% 
  `row.names<-`(., .[['rn']]) %>% select(-rn)
#                   A-XXX  fBM-XXX    P-XXX  vBM-XXX
#BATF::JUN_AHR  1.51653276 2.228752 1.733567 3.003979
#BATF::JUN_CCR9 0.07703724 0.000000 0.000000 0.000000

评论

0赞 littleworth 3/15/2018
谢谢。实际上,我的首字母是一长串的管道。我怎样才能修改你的代码,使其不兼容。datfilter(dat,.)
0赞 littleworth 3/15/2018
更正你的包含,因为我的 dat 是长系列的管道。filterdat
0赞 littleworth 3/15/2018
谢谢,但这样做会删除行名。我想保留行名。
0赞 akrun 3/15/2018
@scamander 这一步之后你还有别的操作吗?因为,即使我们保留了 row.names,它也会在下一个链中被剥离
12赞 talat 3/15/2018 #2

下面是一个 dplyr 选项:

library(dplyr)
filter_all(dat, any_vars(. != 0))

#       A-XXX  fBM-XXX    P-XXX  vBM-XXX
#1 1.51653276 2.228752 1.733567 3.003979
#2 0.07703724 0.000000 0.000000 0.000000

在这里,我们利用这样的逻辑:如果任何变量不等于零,我们将保留它。这与删除所有变量都等于零的行相同。

关于 row.names:

library(tidyverse)
dat %>% rownames_to_column() %>% filter_at(vars(-rowname), any_vars(. != 0))
#         rowname      A-XXX  fBM-XXX    P-XXX  vBM-XXX
#1  BATF::JUN_AHR 1.51653276 2.228752 1.733567 3.003979
#2 BATF::JUN_CCR9 0.07703724 0.000000 0.000000 0.000000

评论

0赞 littleworth 3/15/2018
需要保留行名。如何修改您的代码?
1赞 talat 3/15/2018
@scamander,请看我的更新。通常,在使用 dplyr(或 data.table)时,您不希望使用 row.names。相反,请始终将该信息存储在单独的列中。
0赞 Art 1/7/2021
如何使用 dplyr 和 across() 而不是 _all 做到这一点?
1赞 hnagaty 4/12/2021
@Art,你可以使用,我在帮助页面找到了一个例子。我定制了这个例子并发布了一个新的答案。if_anyacross()
1赞 Calum You 3/15/2018 #3

这是第三个选项,用于生成所有行是否为零的索引。绝对不如 ,但使用 !purrr::pmapfilter_atpmap

dat <- structure(list(`A-XXX` = c(1.51653275922944, 0.077037240321129, 
                                  0), `fBM-XXX` = c(2.22875185527511, 0, 0), `P-XXX` = c(1.73356698481106, 
                                                                                         0, 0), `vBM-XXX` = c(3.00397859609183, 0, 0)), .Names = c("A-XXX", 
                                                                                                                                                   "fBM-XXX", "P-XXX", "vBM-XXX"), row.names = c("BATF::JUN_AHR", 
                                                                                                                                                                                                 "BATF::JUN_CCR9", "BATF::JUN_IL10"), class = "data.frame")

library(tidyverse)
dat %>%
  rownames_to_column() %>%
  bind_cols(all_zero = pmap_lgl(., function(rowname, ...) all(list(...) == 0))) %>%
  filter(all_zero == FALSE) %>%
  `rownames<-`(.$rowname) %>%
  select(-rowname, -all_zero)
#>                     A-XXX  fBM-XXX    P-XXX  vBM-XXX
#> BATF::JUN_AHR  1.51653276 2.228752 1.733567 3.003979
#> BATF::JUN_CCR9 0.07703724 0.000000 0.000000 0.000000

reprex 软件包 (v0.2.0) 于 2018-03-14 创建。

1赞 mgrund 10/23/2020 #4

这是使用 dplyr 的逐行运算的另一个选项(定义三个示例列,计算按行总和):col1,col2,col3

library(tidyverse)

df <- df %>% 
    rowwise() %>% 
    filter(sum(c(col1,col2,col3)) != 0)

或者,如果您有大量变量(列)可供选择,您也可以通过以下方式使用 tidyverse 选择语法:

df <- df %>% 
    rowwise() %>% 
    filter(sum(c_across(col1:col3)) != 0)

有关详细信息,请参阅:https://dplyr.tidyverse.org/articles/rowwise.html

4赞 Agile Bean 1/7/2021 #5

更新 2022-11-11

使用最新的 tidyverse 软件包,.现在更新的解决方案是:across() in filter() is deprecated

data %>% filter(if_all(everything(.), ~. != 0))

旧解决方案(已终止)

通过@mgrund补充答案, DPLYR 1.0.0 的较短替代方案是:

# Option A:
data %>% filter(across(everything(.)) != 0))

# Option B:
data %>% filter(across(everything(.), ~. != 0))

说明:
检查每个tidy_select变量,该变量表示每一列。在选项 A 中,如果每列不为零,则检查每一列,这加起来就是每列中一整行零。在选项 B 中,对每一列都应用公式 (~),用于检查当前列是否为零。
across()everything()

编辑:
由于已经按行检查,因此您不需要.这与 或 不同。
filterrowwise()selectmutate

重要提示:
在选项 A 中,关键是要写 ,
而不是 !
across(everything(.)) != 0across(everything(.) != 0))

原因:
需要一个 tidyselect 变量(这里),而不是布尔值(这将是
acrosseverything()everything(.) != 0))

评论

0赞 Art 1/7/2021
错误:输入 有问题。x 必须使用有效的下标向量对列进行子集化。x 下标的类型错误。i 它必须是数字或字符。i 输入为 。i 错误发生在第 1 行。filter()..1logical..1across(everything() != 0)
1赞 Agile Bean 1/8/2021
你是对的,我的版本只适用于没有.纠正!rowwise
4赞 hnagaty 4/12/2021 #6

您可以使用新的 .我定制了一个在文档中找到的示例if_any()if_any()

library(dplyr)
library(tibble)
dat <- structure(list(`A-XXX` = c(1.51653275922944, 0.077037240321129, 
                                  0), `fBM-XXX` = c(2.22875185527511, 0, 0), `P-XXX` = c(1.73356698481106, 
                                                                                         0, 0), `vBM-XXX` = c(3.00397859609183, 0, 0)), .Names = c("A-XXX", 
                                                                                                                                                   "fBM-XXX", "P-XXX", "vBM-XXX"), row.names = c("BATF::JUN_AHR", 
                                                                                                                                                                                                 "BATF::JUN_CCR9", "BATF::JUN_IL10"), class = "data.frame")
dat
#>                     A-XXX  fBM-XXX    P-XXX  vBM-XXX
#> BATF::JUN_AHR  1.51653276 2.228752 1.733567 3.003979
#> BATF::JUN_CCR9 0.07703724 0.000000 0.000000 0.000000
#> BATF::JUN_IL10 0.00000000 0.000000 0.000000 0.000000

dat %>% 
  rownames_to_column("ID") %>% 
  filter(if_any(!matches("ID"), ~ . != 0)) %>% 
  column_to_rownames("ID")
#>                     A-XXX  fBM-XXX    P-XXX  vBM-XXX
#> BATF::JUN_AHR  1.51653276 2.228752 1.733567 3.003979
#> BATF::JUN_CCR9 0.07703724 0.000000 0.000000 0.000000

创建于 2021-04-12 由 reprex 软件包 (v1.0.0)