mutate의 오른쪽에있는 recode에서 tidyeval 기반의 비표준 평가 사용


13

각 열이 많은 값을 가질 수있는 문자형 벡터 인 "T"를 고려하십시오.

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 기능에 대한 이해가 향상되기를 바랍니다. 이견있는 사람?


고마워, 이것은 영리한 접근 방식입니다. 내 코드의 다른 부분에서 기능적 접근 방식을 사용하고 여기에서도 그렇게 할 수 있습니다. 나는 일부 사람들이 코드 스타일 대화에 대해 싫은 것을 알고 있지만 몇 가지 다른 스타일의 답변을 너무 빨리 보는 것은 나에게 매우 유익했습니다.
aaron

1
이 질문에 몇 가지 아이디어를 결합하여 이것이 q1(기호)와 "q1"(문자열) 모두에서 작동하는 가장 간결한 버전이라고 생각합니다 .df %>% mutate_at( vars(!!ensym(question)), recode, A = NA_character_)
Artem Sokolov

답변:


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 변환 함수를 사용하여 시도했지만 분명히 올바른 것을 선택하지는 않았지만 접근 방식은 효과적입니다. 제 머리에서 유형 변환을 워크 플로우해야한다고 생각합니다. 내 !! 질문은 문자 스트링을 문자 그대로 평가하기 때문에 작동하지 않습니다. 먼저 문자열을 기호로 변환 한 다음 기호를 평가하여 벡터를 반환하기 때문에 작동합니다. 나는 단지 순서대로 머리를 감쌀 수 없었다. 다시 감사합니다.
aaron

8

rlang> = 0.4.0 인 경우 "곱슬 곱슬"방법을 사용할 수 있습니다 .

@ eipi10 덕분에 설명 :

이것은 quote-then-quote의 두 단계 프로세스를 하나의 단계로 결합하므로 다음 {{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의 외관상 동일한 "뱅뱅"버전은 그렇지 않은 이유가 무엇인지 아십니까?
camille

내가 들었던 곱슬 곱슬하게 언급 해 주셔서 감사합니다. 내가 설치 한 rlang / dplyr 버전에 대해서는 답이 작동하지 않습니다. LHS에 오류가 있습니다. LHS를 LHS로 바꾸고 q1을 인용하면 위와 같은 문제가 발생합니다. q1을 인용하지 않으면 오류가 발생합니다. 이것은 아마도 버전 일 것입니다.
aaron

1
예, rlang 0.4.0은 6 월 말에 방금 릴리스되었으므로 그 이후로 업데이트하지 않으면 작동하지 않습니다
IceCreamToucan

2
dplyr 파이프에서 사용하기 전에 먼저 questionquosure ( question = enquo(question)) 로 전환해야하기 때문에 bang-bang이 작동하지 않았다고 생각합니다 . {{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 비네팅을 사용한 프로그래밍 (적절한 예를 보려면 "스플 라이스"를 검색) 에서이 예제로 수행 한 작업을 확인할 수 있습니다 . !!!레코딩 값 쌍을 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.