public bigdata

Programming with dplyr_비표준 평가 관련1 본문

R programming

Programming with dplyr_비표준 평가 관련1

public bigdata 2019. 8. 16. 20:21

<Programming with dplyr>

 

Programming with dplyr

Operations on data frames can be expressed succinctly because you don’t need to repeat the name of the data frame. For example, you can write filter(df, x == 1, y == 2, z == 3) instead of df[df$x == 1 & df$y ==2 & df$z == 3, ].

cran.r-project.org

비표준적 평가 : 대부분의 프로그래밍 언어에서는 단지 함수 인자의 값에만 접근할 수 있다. 그런데 R에서는 그것들을 계산하는데 사용된 코드에도 접근할 수 있다. 비표준적 평가로 인해서

대부분의 dplyr 함수는 비표준적 평가를 사용한다. 일반적인 R의 평가 규칙을 따르지 않는다는 말이다. 대신에 표현식을 캡쳐하고 맞춤형으로 평가한다. 이것은 dplyr 코드를 사용함으로서 두가지의 주요한 이점을 가질 수 있게 한다.

  • 데이터프레임의 이름을 반복 할 필요가 없으므로 데이터프레임에 대한 작업을 간결하게 표현할 수 있습니다.

library(tidyverse)
df <- tibble(x = 1:10, y=2:11, z=3:12)
# 이런 명령 대신
df[df$x == 1 & df$y ==2 & df$z == 3, ]
# 이렇게 깔끔하게 사용이 가능하다.
dplyr::filter(df, x == 1, y == 2, z == 3) 
  • dplyr은 base R과는 다른 방식으로 결과를 계산하도록 선택할 수 있다. 이것은 dplyr자체는 어떤 작업도 하지 않지만 대신 데이터베이스에 무엇을 해야 하는지 알려주는 SQL을 생성하기 때문에 이는 데이터베이스 백엔드에 중요하다(이건 무슨 말인지 이해가 안된다.)

이런 이점들은 그냥 제공되는 것이 아니다. 두 가지 문제점이 있다

  • 대부분의 dplyr 인자들은 참조적으로 명료하지 않다. 즉, 다른 곳에서 정의한 동등한 것으로 값을 대체할 수 없다. 아래 코드를 보자.

df <- tibble(x = 1:3, y = 3:1)
filter(df, x == 1)
#> # A tibble: 1 x 2
#>       x     y
#>   <int> <int>
#> 1     1     3

위 코드는 아래 코드와 같지 않다

my_var <- x
#> Error in eval(expr, envir, enclos): object 'x' not found
filter(df, my_var == 1)
#> Error: object 'my_var' not found

아래 코드와도 같지 않다

my_var <- "x"
filter(df, my_var == 1)
#> # A tibble: 0 x 2
#> # … with 2 variables: x <int>, y <int>

이것은 dplyr 동사가 계산되는 방식을 바꾸는 인수를 가지고 함수를 만드는 것을 어렵게 만든다.

dplyr 코드는 모호하다 filter(df, x == y) 어떤 변수가 어디서 정의 되었는지에 따라 앞의 코드는 아래와 같을 수 있다.

df[df$x == df$y, ]
df[df$x == y, ]
df[x == df$y, ]
df[x == y, ]

이런 코드는 대화형식으로 작업할 때 유용하지만(입력을 저장하고 빠르게 발견하기 때문에) 기능을 본인이 원하는 것보다 더 예측할 수 없게 만든다.

다행이도 dplyr은 이런 문제들을 극복할 수 있는 도구를 제공한다. 조금 더 타이핑이 필요하지만 적은 양의 추가 타이핑이 장기적으로 시간을 절약할 수 있도록 도와주기 때문에 그만한 가치가 있다.

이 vignette의 목적은 2가지이다.

  • dplyr의 대명사, 준 따옴표를 이용하여 데이터 분석 코드에서 중복을 줄이는 신뢰할 수 있는 함수를 작성하는 방법을 보여준다.

  • 표현식과 환경을 모두 저장하는 데이터 구조인 quosures를 포함한 기본 이론과 기본 툴킷인 tidyeval을 알려준다.

우리는 Warm up을 시작으로 이 문제를 뭔가 더 친숙한 것으로 연결시킨 다음, 몇 가지 실용적인 도구로 이동한 다음, 좀 더 깊은 이론으로 들어갈 것이다.


<Warm up>

아직 깨닫지 못했을 수도 있지만 당신은 이미 다른 도메인에서 이런 유형의 문제를 해결했을 것이다. 아래 코드가 원하는 것을 수행하지 않는 다는 것은 분명하다.

greet <- function(name) {
  "How do you do, name?"
}
greet("Hadley")
#> [1] "How do you do, name?"

"" 따옴표로 묶으면 입력을 해석하지 않기 때문이다. 단지 문자열로 저장만 하게 된다. 한 가지 함수를 동작하게 하는 방법은 paste()함수를 사용하여 문자열을 작성하는 것

greet <- function(name) {
  paste0("How do you do, ", name, "?")
}
greet("Hadley")
#> [1] "How do you do, Hadley?"

또 다른 접근법은 glue 패키지를 사용하는 것이다. 이 패키지는 문자열 내에서 문자가 아닌 부분을 허용하고 이를 R표현식으로 값을 대체할 수 있게 한다. 

greet <- function(name) {
  glue::glue("How do you do, {name}?")
}
greet("Hadley")
#> How do you do, Hadley?

<Programming recipes> 

다음의 예제들을 수행하면서 tidyeval의 기초에 대해서 알 것이고, 명시적인 목표는 dplyr코드의 중복을 줄이는 것이다.예제들은 다소 신빙성이 없을 수 있다. 왜냐하면 이해하기 쉽도록 매우 간단한 요소들로 줄였기 때문이다.


<Differnt data sets>

dplyr의 함수는 첫번째에 data 인자를 입력하고, dplyr은 그것에 대해서 아무런 행동도 하지 않기 때문에 참조적으로 명백합니다. 아래는 dplyr 함수의 활용 예 입니다.

mutate(df1, y = a + x)
mutate(df2, y = a + x)
mutate(df3, y = a + x)
mutate(df4, y = a + x)

위 코드는 아래와 같이 줄일 수 있습니다.

mutate_y <- function(df) {
  mutate(df, y = a + x)
}

그러나 이 간단한 함수에는 단점이 있습니다. 즉, 변수 중 하나가 데이터 프레임에 존재하지 않고 글로벌 환경에 존재한다면 조용히 실패할 수 있다.(a라는 변수가 df라는 데이터 프레임 안의 컬럼명인지, 아니면 글로벌 환경에 존재하는 어떤 벡터인지 알 수 없습니다.)

그렇지만 우리는 .data라는 대명사를 이용해서 충돌을 방지할 수 있다. 결과적으로 dplyr 함수가 참조하는 데이터 안에 해당 변수가 없다면 오류가 발생하도록 할 수 있다.

mutate_y <- function(df) {
  mutate(df, y = .data$a + .data$x)
}

mutate_y(df1)
#> Column `a` not found in `.data`

If this function is in a package, using .data also prevents R CMD check from giving a NOTE about undefined global variables (provided that you’ve also imported rlang::.data with @importFrom rlang .data).
>> 이건 무슨 말인지 모르겠다....

<Differnt expressions>

함수의 인자로 변수 명이나 x+y와 같은 표현식을 사용한다면 함수를 작성하기가 어렵다. 왜냐하면 dplyr이 자동적으로 인자의 입력 값을 인용하기 때문이다. 그리고 참조적으로 투명하지 않기 때문이다??-무슨 말이지 .... 어쨋든 아래 예시를 보자. 

df <- tibble(
  g1 = c(1, 1, 2, 2, 2),
  g2 = c(1, 2, 1, 2, 1),
  a = sample(5),
  b = sample(5)
)

df %>%
  group_by(g1) %>%
  summarise(a = mean(a))
#> # A tibble: 2 x 2
#>      g1     a
#>   <dbl> <dbl>
#> 1     1  4   
#> 2     2  2.33

df %>%
  group_by(g2) %>%
  summarise(a = mean(a))
#> # A tibble: 2 x 2
#>      g2     a
#>   <dbl> <dbl>
#> 1     1  3.67
#> 2     2  2

위 코드를 아래와 같이 작성하면 오류가 발생한다.

my_summarise <- function(df, group_var) {
  df %>%
    group_by(group_var) %>%
    summarise(a = mean(a))
}

my_summarise(df, g1)
#> Error: Column `group_var` is unknown

코드를 아래와 같이 작성해도 안된다.

my_summarise(df, "g2")
#> Error: Column `group_var` is unknown

왜냐면 group_by 함수는 입력을 평가하지 않고 인용하기 때문이다. (인용 : ""붙이는 거라고 이해하기 보다 있는 그대로 가져온다. 해당 객체가 무엇인지 연결해서 가져올려는 동작을 하지 않고 캡쳐해서 그대로 가져온다고 이해...) 이 기능을 작동시키려면 두 가지를 해야 한다. 첫째는 입력 자체를 인용하고 그런 다음 group_by에게 입력값을 인용하지 말라고 말해야 한다.(인용하지 않는 다는 것은 해당 입력 값을 평가해서 연결된 무언가를 가져오라는 뜻으로 이해함)

입력 내용을 어떻게 인용(평가하지 않도록, 있는 그대로 가져온다는 의미로 이해하자)할 수 있는가 기본 R에서 quote, ~를 사용하는 방법이 있는데 우리가 원하는 대로 작동하지 않기 때문에, 우리는 quo라는 새로운 기능을 사용할 것이다.(... 무슨 말인지 이해가 안된다.. 왜 quote, ~는 안되는 것이지?)

quo() works like ": it quotes its input rather than evaluating it. : quo는 동작한다 ''처럼, 이 인용은 그것을 평가하기보다 그것의 입력을 인용한다. - ??? 뭔말이냐 .....잉닝ㄹ니ㅏㄴㅇ라ㅣㄴㅇ리ㅏㅇㄹ나ㅣ

quo(g1)
#> <quosure>
#> expr: ^g1
#> env:  global
quo(a + b + c)
#> <quosure>
#> expr: ^a + b + c
#> env:  global
quo("a")
#> <quosure>
#> expr: ^"a"
#> env:  empty

quo는 특별한 유형의 formula를 반환한다. quosure라는 것을 반환, g1이라는 것을 인용했다. quo(g1)통해서 그러나 그대로 dplyr함수에 밀어 넣기만 해서는 효과가 없다

my_summarise(df, quo(g1))
#> Error: Column `group_var` is unknown

이전과 같은 동일한 오류를 얻는다. 왜냐면 우리는 아직 group_by함수에게 인용하라고 말하고 있기 때문이다. 다른 말로 우리는 group_by에게 입력 값을 인용하지 말라고 말해야 한다. my_summarise에서 여전히 인용되고 있기 때문에

dplyr에서 !!를 통해서 입력을 인용하지 않고 평가하고 싶다고 말하면 된다. 이것을 통해서 우리는 원하는 동작을 취할 수 있다.

my_summarise <- function(df, group_var) {
  df %>%
    group_by(!! group_var) %>%
    summarise(a = mean(a))
}

my_summarise(df, quo(g1))
#> # A tibble: 2 x 2
#>      g1     a
#>   <dbl> <dbl>
#> 1     1  4   
#> 2     2  2.33

두 번째 단계 실행.

my_summarise(df, g1)

그러나 이것은 동작하지 않는다 g1이라고 불리는 객체가 없기 때문이다. 우리는 사용자가 입력한 것을 캡처하여 그들을 위해 인용해야 한다???.  quo를 통해서 그것을 할 수 있다?

my_summarise <- function(df, group_var) {
  quo_group_var <- quo(group_var)
  print(quo_group_var)

  df %>%
    group_by(!! quo_group_var) %>%
    summarise(a = mean(a))
}

my_summarise(df, g1)
#> <quosure>
#> expr: ^group_var
#> env:  0x7ffa5823d498
#> Error: Column `group_var` is unknown

무슨 일이 일어나는지 분명히 하기 위해서 print를 호출했다. quo(group_var)은 ~group_var을 반환한다. 우리는 이게 아니라 ~g1을 반환하고 싶은 것이다. enquo를 사용하면 된다. enquo의 기능은 한 단계 실행하고, 가져온 값에 대해서 quo 한다고 생각하자..

my_summarise <- function(df, group_var) {
  group_var <- enquo(group_var)
  print(group_var)

  df %>%
    group_by(!! group_var) %>%
    summarise(a = mean(a))
}

my_summarise(df, g1)
#> <quosure>
#> expr: ^g1
#> env:  global
#> # A tibble: 2 x 2
#>      g1     a
#>   <dbl> <dbl>
#> 1     1  4   
#> 2     2  2.33

제대로 실행된다. enquo(group_var)하면 한 단계 실행 --> group_var이 뭔지 한 번 찾아서 quo한다 --> quo(g1)라고 일단 생각한다.

base R의 quote, substitute에 익숙하다면 quo는 quote와 동등하고, enquo는 substitute와 동등하다. 여러 그룹화 변수를 처리하는 방법이 궁금할 수도 있지만 이는 나중에 알아본다고 vignette에 적혀있다..... 어렵다 R


<Different input variable>

아래 코드는 입력 변수를 계산하여 세 개의 요약을 하는 코드이다

summarise(df, mean = mean(a), sum = sum(a), n = n())
#> # A tibble: 1 x 3
#>    mean   sum     n
#>   <dbl> <int> <int>
#> 1     3    15     5
summarise(df, mean = mean(a * b), sum = sum(a * b), n = n())
#> # A tibble: 1 x 3
#>    mean   sum     n
#>   <dbl> <int> <int>
#> 1   8.6    43     5

표현식을 quo 를 통해서 인용하고 함수 내에서는 !!를 통해서 인용하지 않고 바로 평가 하도록 한다

my_var <- quo(a)
summarise(df, mean = mean(!! my_var), sum = sum(!! my_var), n = n())
#> # A tibble: 1 x 3
#>    mean   sum     n
#>   <dbl> <int> <int>
#> 1     3    15     5

또한 내부 동작이 어떻게 되는지 보기 위해서 quo를 통해 함수 전체를 quo 감쌀수도 있다.

quo(summarise(df,
  mean = mean(!! my_var),
  sum = sum(!! my_var),
  n = n()
))
#> <quosure>
#> expr: ^summarise(df, mean = mean(^a), sum = sum(^a), n = n())
#> env:  global

함수로 작성하기 위해서는 quo 대신 enquo를 사용해야 한다. 그 이유는 아래와 같은 함수에서 expr을 quo로 감싸면 expr 그대로의 의미이기 때문에 인자 부분에서 expr에 입력한 표현식을 quo가 가져오지 못한다. enquo로 작성해야 인자 부분에서 작성한 expr을 한 번은 참조해서 가져온 뒤에 인용해야 함수가 작동하기 때문이다.

my_summarise2 <- function(df, expr) {
  expr <- quo(expr)

  summarise(df,
    mean = mean(!! expr),
    sum = sum(!! expr),
    n = n()
  )
}
my_summarise2(df, a)
#> # A tibble: 1 x 3
#>    mean   sum     n
#>   <dbl> <int> <int>
#> 1     3    15     5
my_summarise2(df, a * b)
#> # A tibble: 1 x 3
#>    mean   sum     n
#>   <dbl> <int> <int>
#> 1   8.6    43     5

아래와 같이 함수 작성 시에는 enquo로 인용해야 한다.

my_summarise2 <- function(df, expr) {
  expr <- enquo(expr)

  summarise(df,
    mean = mean(!! expr),
    sum = sum(!! expr),
    n = n()
  )
}
my_summarise2(df, a)
#> # A tibble: 1 x 3
#>    mean   sum     n
#>   <dbl> <int> <int>
#> 1     3    15     5
my_summarise2(df, a * b)
#> # A tibble: 1 x 3
#>    mean   sum     n
#>   <dbl> <int> <int>
#> 1   8.6    43     5

 

<Different input and output variable>

다음의 과제는 출력 변수의 이름을 바꾸는 것이다.

mutate(df, mean_a = mean(a), sum_a = sum(a))
#> # A tibble: 5 x 6
#>      g1    g2     a     b mean_a sum_a
#>   <dbl> <dbl> <int> <int>  <dbl> <int>
#> 1     1     1     5     4      3    15
#> 2     1     2     3     2      3    15
#> 3     2     1     4     1      3    15
#> 4     2     2     1     3      3    15
#> # … with 1 more row
mutate(df, mean_b = mean(b), sum_b = sum(b))
#> # A tibble: 5 x 6
#>      g1    g2     a     b mean_b sum_b
#>   <dbl> <dbl> <int> <int>  <dbl> <int>
#> 1     1     1     5     4      3    15
#> 2     1     2     3     2      3    15
#> 3     2     1     4     1      3    15
#> 4     2     2     1     3      3    15
#> # … with 1 more row

표현식을 문자로 가져와 새로운 이름을 자동적으로 생성할 것이므로 quo_name이라는 함수가 필요하다. 그리고 새롭게 생성할 변수명이 저장되어 있는 변수 앞에 마찬가지로 !!를 사용해서 바로 인용하지 않고 평가하도록 한다. 아래 예시를 보면 더 쉽게 이해가 된다.

my_mutate <- function(df, expr) {
  expr <- enquo(expr)
  mean_name <- paste0("mean_", quo_name(expr))
  sum_name <- paste0("sum_", quo_name(expr))

  mutate(df,
    !! mean_name := mean(!! expr),
    !! sum_name := sum(!! expr)
  )
}

my_mutate(df, a)
#> # A tibble: 5 x 6
#>      g1    g2     a     b mean_a sum_a
#>   <dbl> <dbl> <int> <int>  <dbl> <int>
#> 1     1     1     5     4      3    15
#> 2     1     2     3     2      3    15
#> 3     2     1     4     1      3    15
#> 4     2     2     1     3      3    15
#> # … with 1 more row

<Capturing multiple variables>

여러 변수들을 이용하는 함수를 작성하기 위해서는 세 가지를 유의해 작성해야 한다.

  • 함수의 정의에서  ...을 활용해서 여러 변수들을 인자로 받는다
  • enquos를 활용해 ...을 통해서 입력 받은 인자들을 인용한다.
  • !!대신 !!!을 활용한다. (!!!은 여러 인자 값들을 받아서 하나씩 나누어 전달한다.)
my_summarise <- function(df, ...) {
  group_var <- enquos(...)

  df %>%
    group_by(!!! group_var) %>%
    summarise(a = mean(a))
}

my_summarise(df, g1, g2)
#> # A tibble: 4 x 3
#> # Groups:   g1 [2]
#>      g1    g2     a
#>   <dbl> <dbl> <dbl>
#> 1     1     1     5
#> 2     1     2     3
#> 3     2     1     3
#> 4     2     2     1

변수 명으로 활용할 값 뿐만 아니라 list 함수를 통해서 옵션으로 활용할 인자들을 나누어 줄 수 도 있다.

args <- list(na.rm = TRUE, trim = 0.25)
quo(mean(x, !!! args))
#> <quosure>
#> expr: ^mean(x, na.rm = TRUE, trim = 0.25)
#> env:  global

args <- list(quo(x), na.rm = TRUE, trim = 0.25)
quo(mean(!!! args))
#> <quosure>
#> expr: ^mean(^x, na.rm = TRUE, trim = 0.25)
#> env:  global

<Quasiquotation> 

dplyr의 자동 인용은 대화형 코딩에 매우 편리하다(Ex. iris %>% select(Species) 그러나 함수형 프로그래밍을 하려면 변수가 들어있는 변수를 참초하는 것이 필요한데 이 문제에 대한 해결책은 Quasiquation이다. quo 등으로 인용된 표현 안에서 부분적으로 평가할 수 있도록 하는 방법이다. quo로 인용하지 않으면 표현식 내에서 부분적으로 평가하라는 명령은 취소된다. 견적에는 아래와 같은 3가지 유형이 있다.

  • basic
  • unquote splicing
  • unquoting names

1. Unquoting

첫 번째 조작은 basic unquote이다 함수형으로는 UQ 그리고 별칭으로는 !!를 통해 사용한다.

# Here we capture `letters[1:5]` as an expression:
quo(toupper(letters[1:5]))
#> <quosure>
#> expr: ^toupper(letters[1:5])
#> env:  global

# Here we capture the value of `letters[1:5]`
quo(toupper(!! letters[1:5]))
#> <quosure>
#> expr: ^toupper(<chr: "a", "b", "c", "d", "e">)
#> env:  global
quo(toupper(UQ(letters[1:5])))
#> <quosure>
#> expr: ^toupper(<chr: "a", "b", "c", "d", "e">)
#> env:  global

첫 번째 예시는 toupper(letters[1:5]) 자체를 모두 인용하는 예시다
두 번째 예시는 !! 표현을 통해 letters[1:5]를 부분적으로 평가하도록 한다. quo를 통해서 인용되지만 quo 내부에서 부분적으로 letters[1:5]는 평가된다. 
세 번째 예시는 두 번째 예시와 동일하며 두 번째 예시는 UQ를 !!을 통해서 구현한 것이다.

인용된 표현을 부분 평가하여 가져오는 것도 가능하다

var1 <- quo(letters[1:5])
quo(toupper(!! var1))
#> <quosure>
#> expr: ^toupper(^letters[1:5])
#> env:  global

 var1에 할당된 인용된 표현식을 !!var1 을 통해서 var1을 부분 평가하여 가져오는 것이다.

그리고 인용된 표현식을 안전하게 인용 해제 할 수 있다. 인용과 인용 해제는 그들의 환경을 추적하기 때문인데 예를 들면 function(X) (3+x)라는 함수는 function 내부에 x값에 대한 할당이 없기 때문에 x인자 단계까지 역추적하기 때문이다. 이는 인용과 인용 해제에 깊이를 허용하게 된다.(같은 환경이 아닌 더 높은 환경에까지 역추적하기 때문이다.)

my_mutate <- function(x) {
  mtcars %>%
    select(cyl) %>%
    slice(1:4) %>%
    mutate(cyl2 = cyl + (!! x))
}

f <- function(x) quo(x)
expr1 <- f(100)
expr2 <- f(10)

my_mutate(expr1)
#>   cyl cyl2
#> 1   6  106
#> 2   6  106
#> 3   4  104
#> 4   6  106
my_mutate(expr2)
#>   cyl cyl2
#> 1   6   16
#> 2   6   16
#> 3   4   14
#> 4   6   16

함수적인 형태(?)는 !표현 간에 우선 순위가 문제를 일으키는 경우에 유용하다.

my_fun <- quo(fun)
quo(!! my_fun(x, y, z))
#> Error in my_fun(x, y, z): could not find function "my_fun"
quo(UQ(my_fun)(x, y, z))
#> <quosure>
#> expr: ^`~`(fun)(x, y, z)
#> env:  global

my_var <- quo(x)
quo(filter(df, !! my_var == 1))
#> <quosure>
#> expr: ^filter(df, (^x) == 1)
#> env:  global
quo(filter(df, UQ(my_var) == 1))
#> <quosure>
#> expr: ^filter(df, (^x) == 1)
#> env:  global

첫 번째 예시는 !!를 사용하면 my_fun(x,y,z)를 평가하라는 뜻이기 때문에 my_fun이라는 함수를 호출하게 된다 우리가 원하는 것은 my_fun에 저장된 fun이라는 함수명을 가져오는 것인데 !!는 my_fun(x,y,z) 전체를 평가하는 것이기 때문에 원하는 결과를 얻지 못하는데 UQ를 통해서 my_fun 부분만 평가해서 quo(fun)을 가져오기 때문에 함수의 이름만 원하는데로 바꿔줄 수 있다.

<Unquote-splicing>

두 번째 인용 해제 동작은 인용 해제 슬라이싱이다 UQS로 사용하며 !!!로 축약해 사용하기도 한다. 

quo(list(!!! letters[1:5]))
#> <quosure>
#> expr: ^list("a", "b", "c", "d", "e")
#> env:  global

기본적인 동작은 !!와 다를바 없어 보인다.

인용 해제 슬라이싱의 매우 유용한 특징은 벡터의 이름을 인자의 이름으로 바꿔주는 것이다.

x <- list(foo = 1L, bar = quo(baz))
quo(list(!!! x))
#> <quosure>
#> expr: ^list(foo = 1L, bar = ^baz)
#> env:  global

리스트 x안의 foo, bar 객체명을 인자명으로 사용할 수 있다.

이는 dplyr 프로그래밍에 상당한 도움을 준다

args <- list(mean = quo(mean(cyl)), count = quo(n()))
mtcars %>%
  group_by(am) %>%
  summarise(!!! args)
#> # A tibble: 2 x 3
#>      am  mean count
#>   <dbl> <dbl> <int>
#> 1     0  6.95    19
#> 2     1  5.08    13

args 리스트에 summarise 단계에서 적용할 동작을 quo를 통해 적절히 묶어서 담는다. args내부에 quo를 통해서 인용해둔 부분들이 있는데 이는 해당 단계에서 평가할 것이 아니라 summarise 단계에서 group_by등의 함수 영향을 받을 때 사용하고 싶어서 quo로 필요한 부분을 묶은 것이다. 인용은 즉. 여기서 동작하지마! 내가 원하는 곳에서 동작해 그리고 원하는 곳을 알려주는 것이 !! 혹은 !!!인 것이다.

<Setting variable names>

세 번째 인용 해제 동작은 인수 이름을 설정하는 것입니다. 위에서 "="를 통해 인수 이름을 설정하는 것을 보았지만 = 대신 :=를 사용할 수도 있습니다. :=은 해당 연산자의 왼쪽 그리고 오른쪽 모두에서 인용을 해제하게 됩니다. (!!, !!!등과 함께 사용할 때) The rules on the LHS are slightly different: the unquoted operand should evaluate to a string or a symbol. (해당 영어 문장은 무슨 의미인지 이해하지 못했습니다.)

mean_nm <- "mean"
count_nm <- "count"

mtcars %>%
  group_by(am) %>%
  summarise(
    !! mean_nm := mean(cyl),
    !! count_nm := n()
  )
#> # A tibble: 2 x 3
#>      am  mean count
#>   <dbl> <dbl> <int>
#> 1     0  6.95    19
#> 2     1  5.08    13

!!과 :=의 조합을 통해서 := 왼쪽에 있는 표현식을 평가할 수 있도록 하는데 무슨 말이냐 하면 원래 = 왼쪽에 있는 표현식은 문자열이나 기호로 인식되서 객체의 이름이나 함수의 이름 즉. 객체의 이름으로 자동으로 평가된다. 그런데 !!과 :=을 함께 사용하므로서 :=의 왼쪽의 값도 평가하여 다른 객체에 저장된 문자열을 참조할 수 있도록 한다.

 

<후기>

복습 후에 설명을 다듬고 나도 제대로 이해 하도록 하자!!