mutateの右側のrecodeでのtidyevalベースの非標準評価の使用


13

各列が多くの値をとることができる文字ベクトルであるティブルを考えてみましょう-「A」から「F」までとしましょう。

library(tidyverse)
sample_df <- tibble(q1 = c("A", "B", "C"), q2 = c("B", "B", "A"))

引数として列名を取り、その列を再コード化して、回答「A」がNAになり、それ以外の場合はdfがそのまま返されるようにする関数を作成したいと思います。このように設計する理由は、特定の列を使用して一連の操作を実行するより広範なパイプラインに適合するためです。

これを行うには多くの方法があります。しかし、私は、慣用的なtidy_eval / tidyverseアプローチが何であるかを理解することに興味があります。まず、質問名はmutate動詞の左側にある必要があるため、!!and :=演算子を適切に使用します。しかし、それでは右側に何を置くべきでしょうか?

fix_question <- function(df, question) {
    df %>% mutate(!!question := recode(... something goes here...))
}

fix_question(sample_df, "q1") # should produce a tibble whose first column is (NA, "B", "C")

私の最初の考えはこれがうまくいくと思いました:

df %>% mutate(!!question := recode(!!question, "A" = NA_character_))

ただし、関数内のbang-bangは、リテラル文字列(たとえば "q1")を返すだけです。私は、基本的なR [[演算子を使用し、.dplyrからの構成に依存して、右側のデータを参照するためにハッキーなルートのように感じることになり、それが機能するので、ある意味で根本的な問題を解決しました:

df %>% mutate(!!question := recode(.[[question]], "A" = NA_character_))

これを行うためのより慣用的な方法があるかどうかについて、tidyevalが非常に得意な人からフィードバックを得ることに興味があります。実際に動作する例を見れば、tidyeval関数セットの理解がより一般的になると期待しています。何かご意見は?


おかげで、これは賢いアプローチです-私はコードの他の部分で関数型のアプローチを使用しており、ここでもそれを行うことを考えていたかもしれません。SOでコードスタイルの話に眉をひそめる人がいることは知っていますが、いくつかの異なるスタイルの回答をすぐに目にしたことは、私にとって非常に実り多いものでした。
アーロン

1
この質問にいくつかのアイデアを組み合わせることで、私はこれは両方で動作することを最も簡潔なバージョンであると考えているq1(シンボル)と"q1":(文字列)df %>% mutate_at( vars(!!ensym(question)), recode, A = NA_character_)
アルテムソコロフ

回答:


6

ここでは、の右側で:=symシンボルに変換してから評価するように指定できます(!!

fix_question <- function(df, question) {
    df %>%
       mutate(!!question := recode(!! rlang::sym(question), "A" = NA_character_))
  }

fix_question(sample_df, "q1") 
# A tibble: 3 x 2
#  q1    q2   
#  <chr> <chr>
#1 <NA>  B    
#2 B     B    
#3 C     A    

引用符付きと引用符なしの両方の入力で機能するより良いアプローチは、 ensym

fix_question <- function(df, question) {
    question <- ensym(question)
    df %>%
       mutate(!!question := recode(!! question, "A" = NA_character_))
  }


fix_question(sample_df, q1)
# A tibble: 3 x 2
#  q1    q2   
#  <chr> <chr>
#1 <NA>  B    
#2 B     B    
#3 C     A    

fix_question(sample_df, "q1")
# A tibble: 3 x 2
#  q1    q2   
#  <chr> <chr>
#1 <NA>  B    
#2 B     B    
#3 C     A    

2
私はいくつかのrlang変換関数を配置しようとしましたが、明らかに正しい関数を選択しませんでしたが、あなたのアプローチは機能します-本当に頭の中で型変換をワークフローする必要があるだけだと思います。文字列を文字どおりに評価するため、!!質問は機能しません。最初に文字列をシンボルに変換し、次にシンボルを評価してベクトルを返すため、あなたのものは機能します。私はそれが操作の順序であるという私の頭を包むことができなかった。再度、感謝します。
アーロン

8

rlang> = 0.4.0の場合は、「カーリーカーリー」メソッドを使用できます。

@ eipi10による説明:

これは、引用してから引用解除するという2つのステップのプロセスを1つのステップに結合するので{{question}}!!enquo(question)

fix_question <- function(df, question){
  df %>% mutate({{question}} := recode({{question}}, A = NA_character_))
}

fix_question(sample_df, q1)
# # A tibble: 3 x 2
#   q1    q2   
#   <chr> <chr>
# 1 NA    B    
# 2 B     B    
# 3 C     A    

ensymアプローチとは異なり、これはキャラクター名では機能しないことに注意してください。さらに悪いことに、エラーを表示するだけでなく、間違った動作をします。

fix_question(sample_df, 'q1')

# # A tibble: 3 x 2
#   q1    q2   
#   <chr> <chr>
# 1 q1    B    
# 2 q1    B    
# 3 q1    A    

2
私はまだ「カーリーカーリー」の習慣に慣れていません。これが機能する理由を知っていますが、OPの外観が同一の「バンバン」バージョンは機能しませんでしたか?
カミーユ

近々聞いていた巻き毛に触れてくれてありがとう。答えは、インストールしたrlang / dplyrのどのバージョンでも機能しません。LHSでエラーが発生します。LHSをLHSに置き換えてq1を引用すると、上記と同じ問題が発生します。q1を引用しないとエラーになります。これはおそらくバージョンのものです。
アーロン、

1
うんrlang 0.4.0は6月の終わりにリリースされたばかりなので、それ以降それを更新していないとこれはあなたのために機能しません
IceCreamToucan

2
question最初question = enquo(question)はdplyrパイプで使用する前に定数()に変換する必要があるため、バンバンは機能しなかったと思います。{{question}}と同等!!enquo(question)です。
eipi10

2
それが同等であるためには、質問の最初のインスタンスにもenquoが必要です。
IceCreamToucan

7

再コーディングされた値のベクトルも引数として入力できるようにすることで、関数を少し柔軟にすることができます。例えば:

library(tidyverse)
sample_df <- tibble(q1 = c("A", "B", "C"), q2 = c("B", "B", "A"))

fix_question <- function(df, question, recode.vec) {

  df %>% mutate({{question}} := recode({{question}}, !!!recode.vec))

}

fix_question(sample_df, q1, c(A=NA_character_, B="Was B"))
  q1    q2   
1 <NA>  B    
2 Was B B    
3 C     A

これrecode.vecは「引用符でつながれた」と!!!。この例では、dplyrを使用したプログラミングビネットからの変更を確認できます(「splice」を検索して、関連する例を参照してください)。!!!で再コーディング値のペアをrecode関数に「スプライス」して、それらをの...引数として使用する方法に注意してくださいrecode

x = c("A", "B", "C")
args = c(A=NA_character_, B="Was B")

quo(recode(x, !!!args))

<quosure>
expr: ^recode(x, A = <chr: NA>, B = "Was B")
env:  global

複数の列で再コーディング関数を実行する可能性がある場合は、それを列名と再コーディングベクトルのみを受け取る関数に変換できます。このアプローチは、よりパイプフレンドリーになるようです。

fix_question <- function(question, recode.vec) {

  recode({{question}}, !!!recode.vec)

}

sample_df %>% 
  mutate_at(vars(matches("q")), list(~fix_question(., c(A=NA_character_, B="Was B"))))
  q1    q2   
1 <NA>  Was B
2 Was B Was B
3 C     <NA>

または、単一の列を再コーディングするには:

sample_df %>% 
  mutate(q1 = fix_question(q1, c(A=NA_character_, B="Was B")))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.