Tidy Evaluation (타이디 평가)

Author

Samir Parmar

Published

March 14, 2023

Tidy evaluation (줄여서 tidyeval)은 dplyr이나 ggplot2 같은 패키지에서 따옴표 없이 변수명을 사용할 수 있게 해주는 마법 같은 도구입니다. 이 기술을 활용하면 더 유연하고 강력한 사용자 정의 함수를 만들 수 있습니다.

1. tidyeval이란 무엇인가?

우리는 이미 mtcars %>% select(mpg, cyl) 처럼 변수명을 따옴표 없이 사용해 왔습니다. Tidyeval은 R에서 메타프로그래밍(Metaprogramming)을 가능하게 하는 기술입니다. SAS 매크로와 유사한 역할을 한다고 이해할 수 있습니다.

2. 평가된 인수와 인용된 인수

  • 평가된 인수(Evaluated): 함수에 전달되기 전 미리 계산된 값 (예: log(10))
  • 인용된 인수(Quoted): 변수명 그 자체를 전달하고, 나중에 데이터의 맥락에서 해석함
mpg <- 5020
# 전역 변수 mpg(5020)가 아닌 데이터셋의 mpg 열을 사용함
mtcars %>% select(mpg) %>% head(2)
              mpg
Mazda RX4      21
Mazda RX4 Wag  21

3. 열 충돌(Column Collision) 방지

데이터셋 내부의 변수명과 외부 환경의 변수명이 같을 때 발생하는 문제를 방지하기 위해 .data.env 대명사를 사용할 수 있습니다.

df <- data.frame(x = 1, y = 2)
x <- 100

df %>% 
  mutate(z1 = y / x) %>%             # x를 외부 변수로 사용하려 함
  mutate(z2 = .data$y / .env$x)      # 명확하게 구분함
  x y z1   z2
1 1 2  2 0.02

4. 동적 점 (Dynamic Dots, ...)

함수에서 ...을 사용하면 개수에 상관없이 여러 인수를 한 번에 받을 수 있습니다.

mySummary <- function(myData, ...) {
  myData %>%
    group_by(TRT01AN, TRT01A) %>%
    summarise(mean = round(mean(AGE), ...), .groups = 'drop')
}

# 추가 인수를 round() 함수로 전달 (digits = 1)
mySummary(adsl_saf, digits = 1)
# A tibble: 3 × 3
  TRT01AN TRT01A                mean
    <dbl> <chr>                <dbl>
1       0 Placebo               75.2
2      54 Xanomeline Low Dose   75.7
3      81 Xanomeline High Dose  74.4

5. Embrace 연산자 ({{ }})

데이터셋의 변수명을 함수의 인수로 받고 싶을 때 가장 쉬운 방법은 { }를 사용하는 것입니다. 이를 Embrace 한다고 표현합니다.

plot_bar <- function(data, var) {
  data %>% 
    ggplot(aes({{ var }})) +
    geom_bar()
}

plot_bar(adsl_saf, SEX)

6. Walrus 연산자 (:=)

함수 내부에서 새로운 변수 이름을 동적으로 지정하고 싶을 때는 = 대신 := (Walrus 연산자)를 사용해야 합니다.

data(mpg)
summary_mean <- function(df, summary_var, new_name) {
  df %>% 
    summarize({{ new_name }} := mean({{ summary_var }}))
}

summary_mean(mpg, cty, mean_cty)
# A tibble: 1 × 1
  mean_cty
     <dbl>
1     16.9

7. 기호를 문자열로 변환 (as_name)

드물게 인수로 받은 변수명을 실제 문자열(“variable_name”)로 바꿔서 사용해야 할 때가 있습니다. 이때 rlang::as_name(enquo(var)) 형식을 사용합니다.

get_var_name <- function(data, var) {
  var_str <- rlang::as_name(enquo(var))
  message("선택된 변수명은: ", var_str)
}
get_var_name(adsl_saf, RACE)
선택된 변수명은: RACE

챌린지

미니 프로젝트 7에서 개발한 plot_ALT 함수에 tidyeval 기술을 적용해 보세요. { }...을 사용하여 더 유연하게 작동하도록 개선해 보세요.

library(ggdist)

plot_ALT <- function(data, x_var, y_var, ...) {
  data %>%
    ggplot(aes(x = {{ x_var }}, y = as.factor({{ y_var }}))) +
    ggdist::stat_dotsinterval() +
    labs(...) # labs에 전달할 인수를 ...로 받음
}

# 사용 예시
# import("./data/adlbc.xpt") %>% filter(PARAMCD == "ALT") %>%
#   plot_ALT(x_var = LBSTRESN, y_var = VISITNUM, x = "ALT 수치", y = "방문 회차")