【问题标题】:how to provide expression to `purrr::pmap` for function using tidy evaluation如何使用整洁的评估为函数提供表达式 purrr::pmap
【发布时间】:2021-09-28 06:25:11
【问题描述】:

我正在尝试使用rlang 编写一个函数,以便我可以根据提供的表达式对数据进行子集化。虽然实际的功能很复杂,但这里有一个最小版本来说明问题。

所需函数的最小版本

library(rlang)

# define a function
foo <- function(data, expr = NULL) {
  if (!quo_is_null(enquo(expr))) {
    dplyr::filter(data, !!enexpr(expr))
  } else {
    data
  }
}

# does the function work? yes

head(foo(mtcars, NULL))     # with NULL
#>                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
#> Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
#> Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
#> Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
#> Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
#> Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
#> Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

head(foo(mtcars, mpg > 20)) # with expression
#>                 mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#> Mazda RX4      21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#> Mazda RX4 Wag  21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
#> Datsun 710     22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
#> Hornet 4 Drive 21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
#> Merc 240D      24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
#> Merc 230       22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2

purrr::pmap 的问题

当与purrr::pmap() 一起使用时,当exprNULL 时,它按预期工作,但不是其他情况。除了list,我还尝试使用alist 来提供输入。

library(purrr)

# works when expression is `NULL`
pmap(
  .l = list(data = list(head(mtcars)), expr = list(NULL)),
  .f = foo
) 
#> [[1]]
#>                    mpg cyl disp  hp drat    wt  qsec vs am gear carb
#> Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
#> Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
#> Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
#> Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
#> Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
#> Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

# but not otherwise
pmap(
  .l = list(data = list(head(mtcars)), expr = list("mpg > 20")),
  .f = foo
)
#> Error: Problem with `filter()` input `..1`.
#> ℹ Input `..1` is `"mpg > 20"`.
#> x Input `..1` must be a logical vector, not a character.

reprex package (v2.0.0) 于 2021-07-20 创建

【问题讨论】:

  • 你可以在pmap 中使用expr = list(quote(mpg &gt; 20))) 来代替字符串吗

标签: r purrr rlang tidyeval


【解决方案1】:

完成这项工作的一种方法是使用quote 包装

purrr::pmap(
  .l = list(data = list(head(mtcars)), expr = list(quote(mpg > 20))),
  .f = foo
)

-输出

[[1]]
                mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4      21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag  21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710     22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1

这也适用于NULL

pmap(
  .l = list(data = list(head(mtcars)), expr = list(quote(NULL))),
   .f = foo
 ) 
[[1]]
                   mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

subset 相同的输出

subset(head(mtcars), mpg > 20)
                mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4      21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag  21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710     22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1

或者另一种选择是通过将enexpr更改为parse_expr来修改函数

foo1 <- function(data, expr = NULL) {
  if (!quo_is_null(enquo(expr))) {
    dplyr::filter(data, !!parse_expr(expr))
  } else {
    data
  }
}

-测试

> pmap(
+   .l = list(data = list(head(mtcars)), expr = list(NULL)),
+   .f = foo1
+ ) 
[[1]]
                   mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

> 
> pmap(
+   .l = list(data = list(head(mtcars)), expr = list("mpg > 20")),
+   .f = foo1
+ )
[[1]]
                mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4      21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag  21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710     22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1  

【讨论】:

  • 可以做到这一点,这样就行了。但这将是一个面向用户的功能,如果可能的话,我不希望他们必须使用quote,这对于新手来说会很神秘。我会等着看是否有其他不需要引用的解决方案。如果没有,我会接受你的回答:)
  • @IndrajeetPatil 问题是您在函数中使用了enquo。可能,ensym 可能工作(未测试)
  • exprNULL 时使用ensym 会出现以下错误:Error: Only strings can be converted to symbols
  • @IndrajeetPatil 有你的功能,你可以在传递字符串时将enexpr 更改为parse_expr
  • @IndrajeetPatil 唯一的缺点是在这两种情况下都必须将输入作为字符串提供给 expr head(foo1(mtcars, "mpg &gt; 20"))
猜你喜欢
  • 1970-01-01
  • 2019-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-17
  • 2021-06-21
  • 2018-05-31
  • 1970-01-01
相关资源
最近更新 更多