web-dev-qa-db-ja.com

ユーザー作成関数内のpurrr + dplyr NSEの問題

多くの試行錯誤と以前の回答との協議 裸の変数または文字列かどうかを検出する方法 自分が必要なことのほとんどは自分で行ったと思います。しかし、私が「解決策」を本番環境に移行する前に、私がいくつかの悪い仮定をしているのか、または問題に愚かに取り組んでいるのかを理解したいと思っています。

次のデータを検討してください。

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))

かなり多くのことを行う関数を書きたいのですが、再現可能な最小限の例から始めます。 tidiedaovの結果を、単一のケースvar1 ~ Stateまたはmap2を使用して一致するリストのペアに対して取得し、1つのリストに "結果"他の "予測子"。私の例とは異なり、使用方法と使用方法が同じになることは決してなく、starts_withのような簡単な解決策に役立つことはめったにありません。

2つの特定の問題と一般的な質問。

問題#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
4
Chuck P

問題#1を解決しました。関数は、変数名が引用符で囲まれているかどうかに関係なく機能します。

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)) {
    q <- paste0("groupvar")
    varname <- expr('$'(lst,!!q))
    gval <- eval_tidy(varname)
    groupvarc <- as.character(gval)
  }else{groupvarc <- eval_tidy(lst$groupvar)}

  if (is.symbol(lst$var)) {
    v <- paste0("var")
    varnam <- expr('$'(lst,!!v))
    vval <- eval_tidy(varnam)
    varc <- as.character(vval)
  }else{varc <- eval_tidy(lst$var)}

  grouplabel <- groupvarc[1] 

  data <- dplyr::select(.data = data,
                        var = varc[[1]],
                        groupvar = groupvarc[[1]] )

  aov_object <- aov(var ~ groupvar, data = data)
  aov_results <- broom::tidy(aov_object)  %>%
     mutate(term = if_else(term != "Residuals", grouplabel, term))
  return(aov_results)
}

MyFunction(data = dat1, groupvar = "State", var = "var1") # works

MyFunction(data = dat1, groupvar = State, var = var1) # Also works

複数の変数については、それを関数にしてlapplyを循環させる必要があります。また、問題1の同じコードを2回繰り返すことで整理されます。これがあなたの前進に役立つことを願っています。

1
YBS