我正在尝试使用 dplyr 根据动态变量进行过滤。
我发现要使过滤器正常工作,我需要将变量名称括在括号中。但是,如果我将其编程为一个函数,它就无法正常工作。
df_ex <- data.frame(a = 1:10, b = 11:20)
param <- quo(a)
# returns df_ex with column a, only, as expected
df_ex %>%
dplyr::select(!!param)
# returns expected df
df_ex %>%
dplyr::filter((!!param)==5)
# Now for the function
testfun <- function(test_df, filt_var){
filt_var_mod <- quo(filt_var)
test_df %>%
dplyr::filter((!!filt_var_mod)==5)
}
# returns empty df, not as expected
testfun(df_ex, "a")
Run Code Online (Sandbox Code Playgroud)
我想学习为自己找到这些关于 dplyr 的问题类型的答案,所以请随时向我推荐编程小插图的相关部分
我只是想了解这里出了什么问题.在第一种情况(工作)中,我将enquo()-ted参数分配给变量,在第二种情况下,我在调用中直接使用了enquoted参数mutate.
library("dplyr")
df <- tibble(x = 1:5, y= 1:5, z = 1:5)
# works
myfun <- function(df, transformation) {
my_transformation <- rlang::enquo(transformation)
df %>%
gather("key","value", x,y,z) %>%
mutate(value = UQ(my_transformation))
}
myfun(df,exp(value))
# does not work
myfun_2 <- function(df, transformation) {
df %>%
gather("key","value", x,y,z) %>%
mutate(value = UQ(rlang::enquo(transformation)))
}
myfun_2(df,exp(value))
#>Error in mutate_impl(.data, dots) : Column `value` is of unsupported type closure
Run Code Online (Sandbox Code Playgroud)
编辑 这里有更多的思路:)
将调用包含到quo()中看起来好像要评估的表达式是"正确构建"的
# looks as if the whole thing should be working …Run Code Online (Sandbox Code Playgroud) 我想把一堆列传递到pmap()里面mutate()。以后,我想选择那些相同的列。
目前,我正在将列名列表传递给pmap()quosure,这很好用,尽管我不知道这是否是“正确”的方法。但是我无法弄清楚如何使用相同的quosure / list select()。
我几乎没有tidyeval的经验,我只能通过玩耍来达到这一目的。我想必须有一种对pmap()和都使用相同内容的方法select(),最好不必将我的每个列名都用引号引起来,但是我还没有找到它。
library(dplyr)
library(rlang)
library(purrr)
df <- tibble(a = 1:3,
b = 101:103) %>%
print
#> # A tibble: 3 x 2
#> a b
#> <int> <int>
#> 1 1 101
#> 2 2 102
#> 3 3 103
cols_quo <- quo(list(a, b))
df2 <- df %>%
mutate(outcome = !!cols_quo %>%
pmap_int(function(..., word) {
args <- list(...)
# just to be clear this isn't …Run Code Online (Sandbox Code Playgroud) 我很难理解这一点。
下面让我以“整洁”的方式过滤我的 data.frame,并使用plotly 绘制一个图。在本例中,我使用plotly基于公式的API来说明要使用数据框的哪些列:
library(plotly)
tidy_filter = function(data, x) {
x = enquo(x)
filter(data, !!x > 5)
}
mtcars %>%
tidy_filter(wt) %>%
plot_ly(x = ~wt, y = ~wt)
Run Code Online (Sandbox Code Playgroud)
我可以将其包装在一个函数中以获得相同的结果:
tidy_ply = function(data, x) {
x = enquo(x)
data = filter(data, !!x > 5)
plot_ly(data, x = x, y = x)
}
tidy_ply(mtcars, wt)
Run Code Online (Sandbox Code Playgroud)
现在:
我认为enquo(x)在这种情况下至少部分相当于~wt因为这就是它的工作原理。但它们是两个不同的东西(定量VS公式)。它们之间是什么关系,为什么上面的方法有效?
plotly 的公式 API 的优点是,如果我想操纵输入值,我可以做类似~wt/2. 但在上面的操作中,plot_ly(data, x = x, y = x/2)会产生错误。有办法让这项工作发挥作用吗?
我想普遍的问题是如何最好地将整洁的评估方法与情节的公式方法结合起来?
因此,此示例基本上来自https://tidyeval.tidyverse.org/dplyr.html#patterns-for-single-arguments,它可以正常工作:
library(tidyverse)
group_mean <- function(df, group_var, summary_var){
group_var <- rlang::enquo(group_var)
summary_var <-rlang::enquo(summary_var)
name <- paste0(rlang::quo_name(summary_var), "_mean")
df %>%
dplyr::group_by(!!group_var) %>%
dplyr::summarise(!!name := mean(!!summary_var, na.rm = TRUE))
}
mtcars %>% group_mean(group_var = cyl, summary_var = disp)
#> # A tibble: 3 x 2
#> cyl disp_mean
#> <dbl> <dbl>
#> 1 4 105.
#> 2 6 183.
#> 3 8 353.
Run Code Online (Sandbox Code Playgroud)
我想例如能够有时选择中位数而不是均值,例如将函数名称更改为group_stat()。
如何利用魔术点 (...) / 省略号来过滤任意列?
df = tibble::tibble(col1 = c('a', 'b', 'c'), col2 = c(1,3,4))
my_func = function(x, ...){
df %>%
dplyr::filter(... == x)
}
my_func('a', col1)
# Should return:
# A tibble: 1 x 2
col1 col2
<chr> <dbl>
1 a 1
Run Code Online (Sandbox Code Playgroud) 我想创建一个简单的函数,它接受一个数据框和用户为该数据框中的两列提供的名称。目的是让它能够轻松地与dplyr管道一起工作。它将生成一个粘合字符串的字符向量:
func <- function(data, last, first) {
last <- rlang::enquo(last)
first <- rlang::enquo(first)
glue::glue_data(data, "{!!last}, {!!first}")
}
Run Code Online (Sandbox Code Playgroud)
我理想地希望用户能够调用:
df %>% func(lastName, firstName)
Run Code Online (Sandbox Code Playgroud)
这将生成一个由多个值组成的字符向量,格式为Smith, John.
我的函数目前不起作用,因为 bang-bang 运算符在 的上下文中不起作用glue_data。在仍然使用 NSE 的同时如何解决这个问题?我收到的错误是:
Error: Quosures can only be unquoted within a quasiquotation context.
代表:
df <- data.frame(lastName = c("Smith", "Bond", "Trump"), firstName = c("John","James","Donald"))
> df
lastName firstName
1 Smith John
2 Bond James
3 Trump Donald
Run Code Online (Sandbox Code Playgroud)
预期产出
> glue::glue_data(df, "{lastName}, {firstName}")
Smith, John
Bond, James
Trump, …Run Code Online (Sandbox Code Playgroud) 我最近发布了两个与我试图编写的函数相关的问题(1、2 )。我收到了每个问题的有用答案,从而产生了以下两个功能:
second_table <- function(dat, variable1, variable2){
dat %>%
tabyl({{variable1}}, {{variable2}}, show_na = FALSE) %>%
adorn_percentages("row") %>%
adorn_pct_formatting(digits = 1) %>%
adorn_ns()
}
Run Code Online (Sandbox Code Playgroud)
和
second_table2 = function(dat, variable1, variable2){
variable1 <- sym(variable1)
dat %>%
tabyl(!!variable1, {{variable2}}, show_na = FALSE) %>%
adorn_percentages("row") %>%
adorn_pct_formatting(digits = 1) %>%
adorn_ns()
}
Run Code Online (Sandbox Code Playgroud)
这些函数按预期工作,但我以前从未使用过 rlang 包,并且仍然对 {{}} 运算符和 !! 之间的区别感到困惑 + sym() 在查看了可用的文档并编写了一些附加函数之后。我不喜欢使用我不完全理解的代码,并且确信将来我会进一步使用这些 rlang 运算符,因此非常感谢您对这些运算符之间的差异进行简单的语言解释。
给定一个参考列z,我想用它dplyr来将每一列转换为:
x = log(x) - log(z)\nRun Code Online (Sandbox Code Playgroud)\n我想z成为一个字符串,或者更好,一个带引号的表达式(例如用户输入 - 所有这些都在一个函数内)。
这是我尝试过的:
\nlibrary(dplyr)\nm <- data.frame(x=1:5,y=11:15,z=21:25)\ndenom = "z"\nRun Code Online (Sandbox Code Playgroud)\n这有效:
\nm %>%\n mutate(across(x:z ,\n list(~ log(.) - log(z) )))\nRun Code Online (Sandbox Code Playgroud)\n这失败了:
\nm %>%\n mutate(across(x:z ,\n list(~ log(.) - log(rlang::sym(denom)))))\n\n# Error: Problem with `mutate()` input `..1`.\n# \xe2\x84\xb9 `..1 = across(x:z, list(~log(.) - log(rlang::sym(denom))))`.\n# \xe2\x9c\x96 non-numeric argument to mathematical function\n# Run `rlang::last_error()` to see where the error occurred.\nRun Code Online (Sandbox Code Playgroud)\n这也失败了:
\nm %>%\n …Run Code Online (Sandbox Code Playgroud) {{}}包中的运算符使得rlang将列名作为函数参数传递(又名准引用)变得非常容易。我理解rlang是为了使用tidyverse,但是有没有办法{{}}使用data.table?
test_dplyr <- function(dt, col1, col2){
temp <- dt %>%
group_by( {{col2}} ) %>%
summarise(test = mean( {{col1}} ))
return(temp)
}
test_dplyr(dt=iris, col1=Sepal.Length, col2=Species)
> # A tibble: 3 x 2
> Species test
> <fct> <dbl>
> 1 setosa 5.01
> 2 versicolor 5.94
> 3 virginica 6.59
Run Code Online (Sandbox Code Playgroud)
理想情况下,这是我想做的,但它返回一个错误。
test_dt2 <- function(dt, col1, col2){
data.table::setDT(dt)
temp <- dt[, …Run Code Online (Sandbox Code Playgroud)