如何将任意大小的列列表传递给dplyr::filter?

niwlg2el  于 2022-12-20  发布在  其他
关注(0)|答案(4)|浏览(131)

假设我们有这样的 Dataframe :

> data <- data.frame('a' = c('a', 'b', 'c'), 
                   'b' = c('d', 'e', 'f'), 
                   'c' = c('g', 'h', 'i'),
                   'd' = c('j', 'k', 'l'))

如果我想根据列a进行筛选以获得一些值,我可以这样做:

> library(dplyr)
> data %>% filter(a == "b")

  a b c d
1 b e h k

如果我想根据列a和列b进行筛选以获得一些值,我可以这样做:

> library(dplyr)
> data %>% filter(a == "c" & b == "f")

  a b c d
1 c f i l

如果我有一个任意长度的列的列表呢?有没有办法做这样的事情?

> data %>% filter(c(a,b) == c("c","f"))

因此,我可以将列名的任意列表以及所需值的列表传递给filter函数。

xxhby3vn

xxhby3vn1#

基本R答案:

cond <- Map(`==`, data[c("a", "b")], c("c", "f"))
data[Reduce(`&`, cond), ]

#  a b c d
#3 c f i l

这也适用于filter

library(dplyr)
data %>% 
  filter(Reduce(`&`, Map(`==`, .[c("a", "b")], c("c", "f"))))

并翻译成tidyverse函数:

library(purrr)
library(dplyr)
data %>% 
  filter(map2(.[c("a", "b")], c("c", "f"), `==`) %>% 
           reduce(`&`))
5w9g7ksd

5w9g7ksd2#

您可以使用data.frame来跟踪条件,并使用join语法代替filter():

filter_df <- data.frame(a = "c", b = "f")

data |>  
  inner_join(filter_df)

#   a b c d
# 1 c f i l
ewm0tg9j

ewm0tg9j3#

也许这对你的情况来说有点过头了,但我还是自由地允许多个值进行筛选。

library(dplyr)
library(purrr)

data <- data.frame('a' = c('a', 'b', 'c', 'd'), 
                   'b' = c('d', 'e', 'f', 'k'), 
                   'c' = c('g', 'h', 'i', 'e'),
                   'd' = c('j', 'k', 'l', 'g'))
data

#  a b c d
#1 a d g j
#2 b e h k
#3 c f i l
#4 d k e g

现在,手动完成时filter条件如下所示。

data %>% filter(a %in% c('c', 'd'), b == 'f')

#  a b c d
#1 c f i l

答案-

要以自动化的方式将条件保存在列表中,请使用自定义函数filter_data并将其与imap一起使用。

filter_args <- list(a = c('c', 'd'), b = c('f'))

filter_data <- function(dat, val, var) {
  dat %>% filter(.data[[var]] %in% val)
}

imap(filter_args, ~filter_data(data, .x, .y)) %>%
  reduce(inner_join)

#  a b c d
#1 c f i l
xmakbtuz

xmakbtuz4#

我喜欢inner_join解决方案,这是我通常在可能的情况下使用的解决方案。
但是,在比较比较比较复杂的情况下,这种方法不起作用,在这种情况下,可以使用expression splicing将筛选表达式的列表传递给filter
为此,首先必须通过将列名和值插入到比较表达式中来创建表达式列表:

cols = c('a', 'b')
values = c('c', 'f')
filters = mapply(
  \(c, v) bquote(.(as.name(c)) == .(v)),
  cols, values,
  USE.NAMES = FALSE
)

然后你可以在filter中使用这个表达式列表:

data |> filter(!!! filters)

相关问题