일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- 이기적 유전자
- 통계오류
- 비율
- 최저 시급
- R 프로그래밍
- 큰수의 법칙
- R4DS
- 산입범위
- t-test
- 산입 범위
- 비행기 추락
- 동전 던지기
- 티모시페리스
- 성악설
- 자기관리
- 핵 개발
- 수학적 사고
- 비선형성
- 조던피더슨
- 인터스텔라
- 통계 오류
- 최저시급 개정안
- 유닛테스트
- 선형성
- 멘탈관리
- t검정
- 핵개발
- R 기초
- 아인슈타인
- 찬물샤워
- Today
- Total
public bigdata
Programming with dplyr_비표준 평가 관련1 본문
<Programming with dplyr>
-
Programming with dplyr vignette을 개인적으로 번역?, 이해한 내용 입니다.
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
!!과 :=의 조합을 통해서 := 왼쪽에 있는 표현식을 평가할 수 있도록 하는데 무슨 말이냐 하면 원래 = 왼쪽에 있는 표현식은 문자열이나 기호로 인식되서 객체의 이름이나 함수의 이름 즉. 객체의 이름으로 자동으로 평가된다. 그런데 !!과 :=을 함께 사용하므로서 :=의 왼쪽의 값도 평가하여 다른 객체에 저장된 문자열을 참조할 수 있도록 한다.
<후기>
복습 후에 설명을 다듬고 나도 제대로 이해 하도록 하자!!
'R programming' 카테고리의 다른 글
%>% (R4DS 일부, help("%>%) 일부) 정리 (0) | 2019.09.02 |
---|---|
apply 계열 함수 정리하기 (0) | 2019.09.01 |
tidyverse other verbs, tips (0) | 2019.09.01 |
Tidy evaluation, most common actions(개인적 정리)_비표준 평가 관련2 (0) | 2019.08.29 |
[R 코드 및 기능] 유닛테스팅 (0) | 2018.04.29 |