purrr + dplyr NSE用户编写的函数内的问题

问题描述 投票:4回答:2

[经过大量的试验和错误并咨询了先前的答案,例如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

[经过大量的试验和错误并咨询了先前的答案,例如如何检测裸变量或字符串,我认为我已经完成了大部分自己需要做的事情。但我很想...

r dplyr purrr
2个回答
1
投票

在我看来,由于您一直坚持将字符串作为变量名进行传递,因此使用as.formula而不是更改数据来更改公式以匹配变量将更加简单有效。这也避免了您必须在函数内部单独命名分组变量。

以下函数较短,在基准测试中的速度约为原始函数的两倍,但是行为保持不变:


0
投票

我已经解决了问题#1。无论变量名是否带引号,您的函数都起作用。

© www.soinside.com 2019 - 2024. All rights reserved.