如何使用 dplyr 管道删除所有列为零的行
How to remove rows where all columns are zero using dplyr pipe
我有以下数据框:
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 的管道样式来实现呢?
我们可以使用 purrr
中的 reduce
来获取行的总和和 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
这是一个 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
这是第三个选项,它使用 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。
这是使用 dplyr 的 row-wise 操作的另一个选项(col1,col2,col3
定义了三个示例列,计算了按行求和):
library(tidyverse)
df <- df %>%
rowwise() %>%
filter(sum(c(col1,col2,col3)) != 0)
或者,如果您有大量变量(列)到 select,您还可以通过以下方式使用 tidyverse selection 语法:
df <- df %>%
rowwise() %>%
filter(sum(c_across(col1:col3)) != 0)
添加@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()
。这对于 select
或 mutate
是不同的。
重要提示:
选项A中,写across(everything(.)) != 0
、
很关键
并不是
across(everything(.) != 0))
!
原因:
across
需要一个 tidyselect 变量(此处为 everything()
),而不是布尔值(即 everything(.) != 0)
)
您可以使用新的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 日创建
我有以下数据框:
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 的管道样式来实现呢?
我们可以使用 purrr
中的 reduce
来获取行的总和和 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
这是一个 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
这是第三个选项,它使用 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。
这是使用 dplyr 的 row-wise 操作的另一个选项(col1,col2,col3
定义了三个示例列,计算了按行求和):
library(tidyverse)
df <- df %>%
rowwise() %>%
filter(sum(c(col1,col2,col3)) != 0)
或者,如果您有大量变量(列)到 select,您还可以通过以下方式使用 tidyverse selection 语法:
df <- df %>%
rowwise() %>%
filter(sum(c_across(col1:col3)) != 0)
添加@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()
。这对于 select
或 mutate
是不同的。
重要提示:
选项A中,写across(everything(.)) != 0
、
很关键
并不是
across(everything(.) != 0))
!
原因:
across
需要一个 tidyselect 变量(此处为 everything()
),而不是布尔值(即 everything(.) != 0)
)
您可以使用新的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 日创建