我想了解如何将表示表达式的字符串传递给dplyr,以便将字符串中提到的变量计算为数据帧中列的表达式.关于这个主题的主要内容包括传递,并且根本不讨论字符串.
很明显,在表示表达式时,quosures比字符串更安全,更清晰,所以当使用quosures时我们当然应该避免使用字符串.但是,在使用R生态系统之外的工具(例如javascript或YAML配置文件)时,通常需要使用字符串而不是quosures.
例如,假设我想要一个使用用户/调用者传入的表达式进行分组计数的函数.正如预期的那样,以下代码不起作用,因为dplyr使用非标准求值来解释参数group_by.
library(tidyverse)
group_by_and_tally <- function(data, groups) {
data %>%
group_by(groups) %>%
tally()
}
my_groups <- c('2 * cyl', 'am')
mtcars %>%
group_by_and_tally(my_groups)
#> Error in grouped_df_impl(data, unname(vars), drop): Column `groups` is unknown
Run Code Online (Sandbox Code Playgroud)
在dplyr 0.5中,我们将使用标准评估group_by_(.dots = groups)来处理这种情况.既然下划线动词已弃用,我们应该如何在dplyr 0.7中执行此类操作?
在只是列名的表达式的特殊情况下,我们可以使用这个问题的解决方案,但它们不适用于更复杂的表达式,例如2 * cyl不仅仅是列名.
我在使用dplyr重命名函数中的列时遇到困难.我已经找到了关于非标准评估和enquo的使用的有用帖子(例如,http://dplyr.tidyverse.org/articles/programming.html 和 在自定义dplyr函数中更改结果变量的名称).最终目标是使用该函数汇总每个组,然后将列重命名为比原始变量名更有意义的列.
这是一个简单的例子,除了注释掉的行外,它都有效.
library(tidyverse)
myfunc <- function(df, groupvar, colvar, grouplab, collab) {
groupvar <- enquo(groupvar)
colvar <- enquo(colvar)
grouplab <- enquo(grouplab) # quo_name instead?
collab <- enquo(collab) # quo_name instead?
t <- df %>%
select(!!groupvar, !!colvar) %>%
group_by(!!groupvar, !!colvar) %>%
summarise(
Frequencies = length(!!colvar),
Percentages = length(!!colvar) / nrow(df)
) %>%
mutate(
Frequencies = scales::comma(Frequencies),
Percentages = scales::percent(Percentages)
) #%>%
# rename(
# (!!grouplab) = !!groupvar, # add a more descriptive grouping var label
# (!!collab) …Run Code Online (Sandbox Code Playgroud) 我正在尝试编写一个函数,它将来自用户的列名称向量作为其参数之一.列名将用于指定将数据帧的哪些列粘贴在一起以在dplyr :: mutate中形成新列.我试图首先折叠参数向量的元素,然后在mutate中使用折叠的字符串 - 这是错误的.请参阅下面的最新尝试.我做了其他尝试,但我不理解dplyr中的新quo,enquo,UQ,!!!,!!等等.有人可以展示我需要做什么吗?
df <- data.frame(.yr = c("2000", "2001", "2002"), .mo = c("12", "01", "02"), .other = rnorm(3))
cols <- colnames(df)[1:2]
do_want <- df %>%
mutate(new = paste(.yr, .mo, sep = "-"))
my_func <- function(dat, vars){
.vars <- paste(vars, collapse = ",")
result <- dat %>%
mutate(new = paste(.vars, sep = "-" ))
return(result)
}
my_func(dat = df, vars = cols)
Run Code Online (Sandbox Code Playgroud)
编辑:这是我尝试使用quo和!! 在函数定义中.结果是一列重复的字符串".yr,.mo"
my_func <- function(dat, vars){
.vars <- quo(paste(vars, collapse = ","))
result <- dat %>%
mutate(new …Run Code Online (Sandbox Code Playgroud) 通常我需要spread多个值列,就像在这个问题中一样.但我经常这样做,我希望能够编写一个这样做的功能.
例如,给定数据:
set.seed(42)
dat <- data_frame(id = rep(1:2,each = 2),
grp = rep(letters[1:2],times = 2),
avg = rnorm(4),
sd = runif(4))
> dat
# A tibble: 4 x 4
id grp avg sd
<int> <chr> <dbl> <dbl>
1 1 a 1.3709584 0.6569923
2 1 b -0.5646982 0.7050648
3 2 a 0.3631284 0.4577418
4 2 b 0.6328626 0.7191123
Run Code Online (Sandbox Code Playgroud)
我想创建一个返回类似的函数:
# A tibble: 2 x 5
id a_avg b_avg a_sd b_sd
<int> <dbl> <dbl> <dbl> <dbl>
1 1 …Run Code Online (Sandbox Code Playgroud) 使用rlang包,我想知道sym()和parse_expr()之间有什么区别.考虑以下表达式:
ex1 = sym('a')
ex2 = parse_expr('a')
Run Code Online (Sandbox Code Playgroud)
他们都回来了
a
identical(ex1, ex2)
[1] TRUE
Run Code Online (Sandbox Code Playgroud)
假设我现在需要一个问题:
ex3 = quo(!!sym('a'))
ex4 = quo(!!parse_expr('a'))
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,结果是:
<quosure>
expr: ^a
env: global
identical(ex3, ex4)
[1] TRUE
Run Code Online (Sandbox Code Playgroud)
但是,由于某些原因,以下两个不一样.
ex5 = quo(!!sym('a - b'))
ex6 = quo(!!parse_expr('a - b'))
Run Code Online (Sandbox Code Playgroud)
显然他们两个都是相同的:
<quosure>
expr: ^a - b
env: global
Run Code Online (Sandbox Code Playgroud)
然而,
identical(ex5, ex6)
[1] FALSE
Run Code Online (Sandbox Code Playgroud)
我的一些问题是sym()和parse_expr()之间有什么区别?一个人做了什么,另一个不能?为什么ex5显然与ex6相似但相同(ex5,ex6)返回FALSE?
假设我有一个函数,它使用非标准评估(NSE)从该数据框中获取数据框和不同数量的变量.是否有更快/更直接的方法来计算提供的变量数量而不是select()这些变量并计算列数?
# Works but seems non-ideal
nvar <- function(df, vars) {
vars_en <- rlang::enquo(vars)
df_sub <- dplyr::select(df, !!vars_en)
ncol(df_sub)
}
nvar(mtcars, mpg:hp)
#> 4
Run Code Online (Sandbox Code Playgroud) 我在评论者建议!! ensym的地方回答这个问题,我认为这可能是使用卷曲{{的好地方,但我无法使其正常工作(也许不适用?)。
在不使用filter_,eval / parse或quote-unquote的情况下,如何执行此过滤器操作?会帮助吗?
我的解决方案(1g)使用filter_和使用粘贴建立的条件。1a有效(但是可以使用{{}}吗?)
如果我们想按多个变量过滤该怎么办?这是您看到2g在下面工作的地方(而2a不再工作了)。
library(tidyverse)
set.seed(1234)
A <- matrix(rnorm(30),nrow = 10, ncol = 3) %>% as_tibble() %>% set_names(paste("var", seq(1:3), sep = ""))
varnames_1 <- c("var2")
(expected_result_1 <- filter(A, var2 > 0))
#> # A tibble: 3 x 3
#> var1 var2 var3
#> <dbl> <dbl> <dbl>
#> 1 -2.35 0.0645 0.460
#> 2 0.429 0.959 -0.694
#> 3 -0.890 2.42 -0.936
(answer_1a <- filter(A,!!ensym(varnames_1) > 0)) # works (thanks joran and aosmith)
#> # A …Run Code Online (Sandbox Code Playgroud) 我正在尝试编写一个自定义函数,在其中使用rlang's quasiquotation。此函数也在内部使用dplyr的join函数。我在下面提供了一个最小的工作示例来说明我的问题。
# needed libraries
library(tidyverse)
# function definition
df_combiner <- function(data, x, group.by) {
# check how many variables were entered for this grouping variable
group.by <- as.list(rlang::quo_squash(rlang::enquo(group.by)))
# based on number of arguments, select `group.by` in cases like `c(cyl)`,
# the first list element after `quo_squash` will be `c` which we don't need,
# but if we pass just `cyl`, there is no `c`, this will take care of that
# issue …Run Code Online (Sandbox Code Playgroud) 我有一个stacked_plot()使用整洁评估来制作堆叠图的函数。我想将它包含在我的包中,并从该包中调用另一个函数来调用它。这是最小的例子:
stacked_plot <- function(data, what, by = NULL, date_col = date){
by <- rlang::enquo(by)
what <- rlang::ensym(what)
date_col <- rlang::ensym(date_col)
data <- data %>%
dplyr::group_by(!!date_col, !!by) %>%
dplyr::summarise(!!what := sum(!!what, na.rm = TRUE)) %>%
dplyr::ungroup() %>%
tidyr::complete(!!date_col, !!by, fill = rlang::list2(!!what := 0))
p <- data %>%
ggplot2::ggplot(ggplot2::aes(!!date_col, !!what, fill = !!by)) +
ggplot2::geom_area(position = 'stack')
print(p)
}
#' @importFrom rlang .data
call_plot <- function() {
to_plot <- data.frame(date = rep(seq(lubridate::ymd('2020-01-01'),
lubridate::ymd('2020-03-30'),
by = '1 day'), each = …Run Code Online (Sandbox Code Playgroud) 一个真正的问题。每当我需要编写 dplyr 函数时,我都在旁听。我知道 curl-curly 运算符可以简化很多任务。
https://www.tidyverse.org/blog/2019/06/rlang-0-4-0/
和
https://www.tidyverse.org/blog/2020/02/glue-strings-and-tidy-eval/
我不清楚什么时候使用简单的“=”和海象运算符“:=”。例如,考虑帖子末尾的片段。函数mean_by 和mean_by2 的不同只是因为前者依赖于“=”,后者依赖于“:=”,但结果是一样的。但是,如果我尝试编写一个依赖于 mutate 来添加新列的函数,如果我在创建新列时使用“=”而不是“:=”,我会收到一条错误消息。有人可以向我澄清为什么不同吗?这是否意味着使用 Walrus 运算符而不是“=”更安全?
谢谢!
library(tidyverse)
mean_by <- function(data, by, var) {
data %>%
group_by({{ by }}) %>%
summarise(avg = mean({{ var }}, na.rm = TRUE))
}
mean_by2 <- function(data, by, var) {
data %>%
group_by({{ by }}) %>%
summarise(avg := mean({{ var }}, na.rm = TRUE))
}
add_new_col <- function(data, old_col, new_col){
data %>%
mutate({{new_col}}:={{old_col}})
}
iris %>% mean_by(Species, Sepal.Width)
#> # A tibble: 3 x 2
#> …Run Code Online (Sandbox Code Playgroud)