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

时间:2018-03-15 06:29:47

标签: r dplyr tidyverse

我有以下数据框:

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的管道风格呢?

6 个答案:

答案 0 :(得分:5)

这是一个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

答案 1 :(得分:2)

我们可以使用reduce中的purrr来获取行的总和,并根据逻辑向量获取数据集filter

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

答案 2 :(得分:2)

添加到@mgrund 的答案中, dplyr 1.0.0 更短的替代方案是:

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

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

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

编辑:
由于 filter 已经按行检查,因此您不需要 rowwise()selectmutate 的情况有所不同。

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

原因:
across 需要一个 tidyselect 变量(此处为 everything()),而不是布尔值(即 everything(.) != 0)

答案 3 :(得分:2)

您可以使用新的 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

reprex package (v1.0.0) 于 2021 年 4 月 12 日创建

答案 4 :(得分:1)

这是第三个选项,它使用purrr::pmap生成所有行是否为零的索引。绝对不如filter_at那么紧凑,但使用pmap打开了有趣和复杂条件的选项!

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 package(v0.2.0)创建于2018-03-14。

答案 5 :(得分:1)

这里是使用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