创建一个函数,该函数通过运算符传递多个参数,用于使用 dplyr 过滤数据集

Posted

技术标签:

【中文标题】创建一个函数,该函数通过运算符传递多个参数,用于使用 dplyr 过滤数据集【英文标题】:creating a function that passes multiple args with operators for filtering dataset using dplyr 【发布时间】:2021-08-08 04:08:00 【问题描述】:
library(dplyr)

create_filter <- function(dat, ...)
    args <- enquos(...)
    ex_args <- unname(imap(args, function(expr, name) quo(!!sym(name)==!!expr)))
    return(dat %>% filter(!!!ex_args))

我正在创建一个函数来过滤我传递多个参数的数据集。我当前的代码只有在我有带等于运算符的参数并且每列一个参数时才有效。我也尝试过我的运气 as.list(match.call()) 不成功。有没有办法完成以下场景?

这是有效的: create_cohort(dat = iris, Species = 'setosa', Sepal.Length = 5.1)

  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          5.1         3.5          1.4         0.3  setosa
3          5.1         3.8          1.5         0.3  setosa.....

这不起作用: create_cohort(dat = iris,Species = 'setosa', Sepal.Length >= 5)

预期输出:

Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1            5.1         3.5          1.4         0.2     setosa
2            5.0         3.6          1.4         0.2     setosa
3            5.4         3.9          1.7         0.4     setosa...

我还想尝试复杂的过滤,我可以使用逻辑运算符 (OR) 并在传递 NA 参数时忽略。例如在下面的情况下,如果选择了 setosa 并且没有给出 Sepal.Length(NA),则显示 'Sepal.Length' =>5 并且对于 viginica 显示忽略 Sepal.Length 标准并显示所有行。

create_cohort(dat = iris, Species = 'setosa'|'virginica', Sepal.Length = NA)

预期:

Sepal.Length Sepal.Width Petal.Length Petal.Width Species
          4.8         3.0          1.4         0.3    setosa
          5.1         3.8          1.6         0.2    setosa
          4.6         3.2          1.4         0.2    setosa
          5.3         3.7          1.5         0.2    setosa
          5.0         3.3          1.4         0.2    setosa
          6.3         3.3          6.0         2.5 virginica
          5.8         2.7          5.1         1.9 virginica
          7.1         3.0          5.9         2.1 virginica

编辑:

现在这就是我所拥有的。虽然它不起作用,但我正在做的是当 Species = 'setosa' 和 Sepal.Width = NA 时的特殊情况,我想这样做 - dat %>% filter(Species == 'setosa' & Sepal.Width > 5)。对于某些情况,当 Species 是其他东西或指定了 Sepal.Width 时,我希望该函数能够正常运行,如显示提到的种类和 sepal.width。除了这些参数之外,还可以传递其他参数,所以我使用了 '...'

我面临的另一个问题是必须将参数名称分配给另一个变量。看看我是如何分配 ln , >=, 3'。目前这在我的代码中不起作用。

create_filter <- function(dat, Species, Sepal.Width = NA, ...)
    lst_arg <- list(dat, Species, Sepal.Width, ...)

if (!"data.frame" %in% class(dat))
    stop("First input must be dataframe")

ln <- Sepal.Width
sp <- Species
args <- enquos(...)
ex_args <- unname(imap(args, function(expr, name) quo(!!sym(name)==!!expr)))
dat <- dat %>% filter(!!!ex_args)
if(is.na(Sepal.Width) && (Species == 'setosa'))
    dat %>% filter(Species == 'setosa' & Sepal.Width > 5 ) else
        dat %>% filter(Sepal.Width %in% (ln),Species %in% (sp))
        

【问题讨论】:

您使用的是 iris 的过滤版本,还是有 NA 的版本?我不明白将五个 setosa 和三个 virginica 子集在您最后的预期输出中的逻辑。 【参考方案1】:

更简单的替代方案,应该适用于 dplyr 1.0+

create_filter <- function(dat, ...)
  dat %>% filter(...)

结果

create_filter(iris, Species == "setosa", Sepal.Length == 5.1)
#  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#1          5.1         3.5          1.4         0.2  setosa
#2          5.1         3.5          1.4         0.3  setosa
#3          5.1         3.8          1.5         0.3  setosa
#4          5.1         3.7          1.5         0.4  setosa
#5          5.1         3.3          1.7         0.5  setosa
#6          5.1         3.4          1.5         0.2  setosa
#7          5.1         3.8          1.9         0.4  setosa
#8          5.1         3.8          1.6         0.2  setosa

create_filter(iris, Species %in% c("setosa", "versicolor"), Sepal.Width == 3.2)
#  Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
#1          4.7         3.2          1.3         0.2     setosa
#2          4.7         3.2          1.6         0.2     setosa
#3          5.0         3.2          1.2         0.2     setosa
#4          4.4         3.2          1.3         0.2     setosa
#5          4.6         3.2          1.4         0.2     setosa
#6          7.0         3.2          4.7         1.4 versicolor
#7          6.4         3.2          4.5         1.5 versicolor
#8          5.9         3.2          4.8         1.8 versicolor

编辑: 听起来 OP 想要根据变量来区别对待 NA,例如物种。假设我们在 Sepal.Width 中每 3 行修改了一个带有 NA 的 iris

iris_na <- iris %>%
  mutate(Sepal.Width = if_else(row_number() %% 3 == 0, NA_real_, Sepal.Width))

head(iris_na)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7          NA          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4          NA          1.7         0.4  setosa

我们可以使用:

create_filter(iris_na, Sepal.Width >= 3.7 | (Species == "setosa" & is.na(Sepal.Width)))

获取所有具有宽 Sepal.Width 的示例(主要是 setosas,但也有一个Virginica),以及所有缺少 Sepal.Width 的 setosas:

   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
1           4.7          NA          1.3         0.2    setosa
2           5.4          NA          1.7         0.4    setosa
3           4.4          NA          1.4         0.2    setosa
4           5.4         3.7          1.5         0.2    setosa
5           4.8          NA          1.6         0.2    setosa
6           5.8          NA          1.2         0.2    setosa
7           5.7         4.4          1.5         0.4    setosa
8           5.4         3.9          1.3         0.4    setosa
9           5.1          NA          1.4         0.3    setosa
10          5.7         3.8          1.7         0.3    setosa
11          5.1         3.8          1.5         0.3    setosa
12          5.4          NA          1.7         0.2    setosa
13          5.1         3.7          1.5         0.4    setosa
14          5.1          NA          1.7         0.5    setosa
15          5.0          NA          1.6         0.4    setosa
16          4.7          NA          1.6         0.2    setosa
17          5.2          NA          1.5         0.1    setosa
18          5.5         4.2          1.4         0.2    setosa
19          5.0          NA          1.2         0.2    setosa
20          4.4          NA          1.3         0.2    setosa
21          4.5          NA          1.3         0.3    setosa
22          5.1          NA          1.9         0.4    setosa
23          5.1         3.8          1.6         0.2    setosa
24          4.6          NA          1.4         0.2    setosa
25          5.3         3.7          1.5         0.2    setosa
26          7.7         3.8          6.7         2.2 virginica

【讨论】:

谢谢。这确实是一个很好的解决方案,但我想做的有点复杂。例如,我想添加特殊情况,如诸如选择物种setosa和sepal.width是na或未给出的,然后默认为sepal.width> = 5。 span> 请多解释。如果 Sepal.Width 为 NA,则默认为 Sepal.Width >= 5? 对于选择 Setosa 且 Sepal.Width 为 NA 或缺失的情况,函数需要返回 Sepal.Width >=5 的 Species 'Setosa' 的行。这就像针对特定情况的特定规则。 对不起,我还是不清楚。如果某些行有 NA,只返回可用值 >=5 的行,否则返回所有行? 好吧,这是需要编码的特殊情况。当我运行此函数“create_cohort(dat = iris,Species = 'setosa')”时,我希望看到 Setosa 的行,Sepal.Width >=5。由于我没有为 Sepal.Width 传递任何参数,因此 Setosa 的默认值是 Sepal.Width >=5。但是如果我有其他物种,那么我们就不会使用相同的默认值。就像 If ((Species == 'setosa') && (missing(Sepal.Width)|is.na(Sepal.Width))Sepal.Width >=5... 如果有意义的话

以上是关于创建一个函数,该函数通过运算符传递多个参数,用于使用 dplyr 过滤数据集的主要内容,如果未能解决你的问题,请参考以下文章

第6 章函数

关于构造函数和原型prototype对象的理解

php函数中,多个参数的情况下怎么使其中一个参数为默认值而其他的使用指定值

在 Python 中通过三元运算符传递函数参数

函数的格式

Lambda