[经过大量的试验和错误并咨询了先前的答案,例如How to detect if bare variable or string,我想我已经完成了大部分自己需要做的事情。但是,在我将“解决方案”投入生产之前,我很想知道我是在做一些错误的假设还是在愚蠢地解决问题。
请考虑以下数据:
library(dplyr)
library(purrr)
library(tidyselect)
set.seed(1111)
dat1 <- data.frame(Region = rep(c("r1","r2"), each = 100),
State = rep(c("NY","MA","FL","GA"), each = 10),
Loc = rep(c("a","b","c","d","e","f","g","h"),each = 5),
ID = rep(c(1:10), each = 2),
var1 = rnorm(200),
var2 = rnorm(200),
var3 = rnorm(200),
var4 = rnorm(200),
var5 = rnorm(200))
我想编写一个功能可做很多事情的函数,但我将从一个最小的可复制示例开始。我想获得tidied
aov
结果,对于单个情况var1 ~ State
或使用map2
的一对匹配列表,其中一个列表包含“结果”]其他“预测变量”] >。它们从使用到使用都不是完全相同的,并且变量与我的示例不同,很少适合于starts_with
之类的简单解决方案。
两个特定问题和一个一般性问题。
问题1-我已经放弃让最终用户(包括我)传递裸变量名总是让我以后遇到麻烦。按照上面的参考,类似我的代码的东西是捕获它们并告诉用户的最快,最可靠的方法? (我在代码中添加了注释以指示我在说什么。
问题2-通过基本的错误和错误,我认为我解决了另一个问题,该问题是生成一些文本以供以后用作标签。当我不将函数与map2
一起使用时,我发现了很多解决方案,但只有这一点似乎可以与map2一起使用。似乎如此令人费解,我简直不敢相信这是个好选择……(再次在代码中显示注释以显示位置)
[一般问题:我添加了推荐的tidyselect::all_of
,因为它们可能是不明确的列表,为什么我仍然必须提防.x
和.y
被视为调用,而不仅仅是迭代标记?] >
MyFunction <- function(data, groupvar, var) { # Issue #1 is this best way to warn/stop user? lst <- as.list(match.call()) if (is.symbol(lst$groupvar) || is.symbol(lst$var)) { stop("Please quote all variables") } # Issue #2 I want the group label but if I don't include # this if logic it errors with " Error: Can't convert a call to a string" # when I run it with purrr::map2 if (!is.call(groupvar)) { grouplabel <- rlang::as_name(rlang::enquo(groupvar)) } data <- dplyr::select( .data = data, var = {{ var }}, groupvar = {{ groupvar }} ) aov_object <- aov(var ~ groupvar, data = data) aov_results <- broom::tidy(aov_object) %>% mutate(term = if_else(term != "Residuals", grouplabel, term)) return(aov_results) } # Expected output MyFunction(data = dat1, groupvar = "State", var = "var1") # works #> # A tibble: 2 x 6 #> term df sumsq meansq statistic p.value #> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 State 3 1.75 0.582 0.485 0.693 #> 2 Residuals 196 235. 1.20 NA NA MyFunction(data = dat1, groupvar = State, var = var1) # warns appropriately #> Error in MyFunction(data = dat1, groupvar = State, var = var1): Please quote all variables # Quick test of `map2` grouping_vars <- names(dat1[,1:3]) names(grouping_vars) <- names(dat1[,1:3]) outcome_vars <- names(dat1[,5:7]) names(outcome_vars) <- names(dat1[,5:7]) names(outcome_vars) <- paste(outcome_vars, "~", grouping_vars) # get multiple results this is where issue #2 comes in but this is what I want it to look like. map2(.x = outcome_vars, .y = grouping_vars, .f = ~ MyFunction(dat = dat1, var = tidyselect::all_of(.x), groupvar = tidyselect::all_of(.y))) #> $`var1 ~ Region` #> # A tibble: 2 x 6 #> term df sumsq meansq statistic p.value #> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 Region 1 0.0512 0.0512 0.0427 0.836 #> 2 Residuals 198 237. 1.20 NA NA #> #> $`var2 ~ State` #> # A tibble: 2 x 6 #> term df sumsq meansq statistic p.value #> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 State 3 5.05 1.68 2.07 0.106 #> 2 Residuals 196 159. 0.814 NA NA #> #> $`var3 ~ Loc` #> # A tibble: 2 x 6 #> term df sumsq meansq statistic p.value #> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 Loc 7 5.09 0.727 0.772 0.612 #> 2 Residuals 192 181. 0.943 NA NA
[经过大量的试验和错误并咨询了先前的答案,例如如何检测裸变量或字符串,我认为我已经完成了大部分自己需要做的事情。但我很想...
在我看来,由于您一直坚持将字符串作为变量名进行传递,因此使用as.formula
而不是更改数据来更改公式以匹配变量将更加简单有效。这也避免了您必须在函数内部单独命名分组变量。
以下函数较短,在基准测试中的速度约为原始函数的两倍,但是行为保持不变:
我已经解决了问题#1。无论变量名是否带引号,您的函数都起作用。