iBetter Books
수정

함수 만들기

R에서 함수는 일급 객체입니다. 변수에 담을 수 있고, 다른 함수에 인자로 넘길 수 있습니다. 직접 함수를 만들 줄 알면 반복되는 분석 코드를 깔끔하게 정리할 수 있습니다.

function()으로 함수 만들기

# 파일: 04_functions.R
add <- function(x, y) {
  x + y
}

add(3, 5)   # 8

R에서 함수의 반환값은 마지막으로 평가된 표현식입니다. return()을 명시하지 않아도 됩니다. 물론 명시해도 됩니다.

add <- function(x, y) {
  result <- x + y
  return(result)   # 명시적 반환
}

중간에 조건부로 반환할 때는 return()을 씁니다.

safe_divide <- function(x, y) {
  if (y == 0) {
    return(NA)   # 0으로 나누면 NA 반환
  }
  x / y
}

safe_divide(10, 2)   # 5
safe_divide(10, 0)   # NA

기본값 설정

인자에 기본값을 주면 생략 가능한 인자가 됩니다.

greet <- function(name, greeting = "안녕하세요") {
  cat(greeting, name, "\n")
}

greet("Alice")                   # 안녕하세요 Alice
greet("Bob", "반갑습니다")       # 반갑습니다 Bob

기본값이 있는 인자는 없는 인자 뒤에 오는 것이 관례입니다.

# 좋은 패턴
describe <- function(x, digits = 2, na.rm = TRUE) {
  round(mean(x, na.rm = na.rm), digits)
}

scores <- c(85.5, 92.3, NA, 78.8, 95.1)
describe(scores)           # 87.93
describe(scores, digits = 0)  # 88
describe(scores, na.rm = FALSE)  # NA

인자 이름으로 호출

R에서는 인자 이름을 명시하면 순서와 상관없이 값을 넘길 수 있습니다.

power <- function(base, exponent) {
  base ^ exponent
}

power(2, 10)              # 1024
power(base = 2, exponent = 10)  # 1024
power(exponent = 10, base = 2)  # 1024 — 순서 달라도 됨

인자가 여러 개일 때 이름을 명시하면 코드가 읽기 쉬워집니다.

가변 인자 ...

인자 개수가 정해지지 않을 때 ...를 씁니다.

my_sum <- function(...) {
  args <- c(...)
  sum(args)
}

my_sum(1, 2, 3)           # 6
my_sum(1, 2, 3, 4, 5)    # 15

...는 내부 함수에 그대로 전달할 수도 있습니다. 래퍼 함수를 만들 때 유용합니다.

my_mean <- function(x, ...) {
  mean(x, ...)   # na.rm 같은 추가 인자를 그대로 전달
}

scores <- c(85, NA, 92, NA, 78)
my_mean(scores)              # NA
my_mean(scores, na.rm = TRUE)  # 85

...로 받은 인자를 리스트로 보려면 list(...)를 씁니다.

show_args <- function(...) {
  args <- list(...)
  for (i in seq_along(args)) {
    cat(i, ":", args[[i]], "\n")
  }
}

show_args(10, "hello", TRUE)
# 1 : 10
# 2 : hello
# 3 : TRUE

여러 값 반환

R 함수는 값 하나만 반환할 수 있습니다. 여러 값을 반환하려면 리스트에 담아 반환합니다.

stats <- function(x, na.rm = TRUE) {
  list(
    mean   = mean(x, na.rm = na.rm),
    sd     = sd(x, na.rm = na.rm),
    min    = min(x, na.rm = na.rm),
    max    = max(x, na.rm = na.rm),
    n      = sum(!is.na(x))
  )
}

scores <- c(85, 92, 78, 95, NA, 88)
result <- stats(scores)

result$mean    # 87.6
result$n       # 5

익명 함수

이름 없이 바로 쓰는 함수입니다. 다른 함수에 인자로 넘길 때 자주 씁니다.

# apply 계열과 함께
scores_list <- list(c(85, 92, 78), c(70, 65, 80))

sapply(scores_list, function(x) mean(x))   # 85.0 71.7

# 한 번만 쓸 때 바로 호출
(function(x, y) x + y)(3, 5)   # 8

R 4.1부터는 \(x) 형태의 단축 문법을 씁니다.

sapply(scores_list, \(x) mean(x))   # R 4.1+ 단축 문법

함수를 인자로 넘기기

R에서 함수는 변수처럼 다룰 수 있습니다.

apply_fn <- function(x, fn) {
  fn(x)
}

scores <- c(85, 92, 78, 95, 88)

apply_fn(scores, mean)    # 87.6
apply_fn(scores, sum)     # 438
apply_fn(scores, max)     # 95
apply_fn(scores, \(x) round(mean(x), 1))  # 87.6

스코프 — 변수의 유효 범위

함수 안에서 만든 변수는 함수 밖에서 보이지 않습니다.

outer_val <- 10

my_func <- function() {
  inner_val <- 20
  cat(outer_val, "\n")   # 10 — 외부 변수는 읽을 수 있음
}

my_func()
cat(inner_val, "\n")     # 오류 — inner_val은 함수 밖에서 없음

<<-를 쓰면 상위 환경의 변수를 수정할 수 있습니다. 그러나 부작용이 생기기 쉬우므로 신중하게 씁니다.

counter <- 0

increment <- function() {
  counter <<- counter + 1   # 전역 변수 수정
}

increment()
increment()
counter   # 2

재귀 함수

함수가 자기 자신을 호출합니다.

factorial <- function(n) {
  if (n <= 1) return(1)
  n * factorial(n - 1)
}

factorial(5)   # 120

R에서는 깊은 재귀가 느릴 수 있습니다. 반복문이나 내장 함수(factorial())가 있다면 그쪽을 쓰는 것이 좋습니다.

함수 문서화

함수를 만들 때 주석으로 설명을 남겨두면 나중에 다시 봤을 때 빠르게 이해할 수 있습니다.

# 성적 통계를 계산합니다.
# 
# @param scores numeric 벡터 — 점수 데이터
# @param na.rm  logical — NA 제거 여부 (기본값: TRUE)
# @return list — mean, sd, min, max, n 포함
#
summarize_scores <- function(scores, na.rm = TRUE) {
  list(
    mean = mean(scores, na.rm = na.rm),
    sd   = sd(scores, na.rm = na.rm),
    min  = min(scores, na.rm = na.rm),
    max  = max(scores, na.rm = na.rm),
    n    = sum(!is.na(scores))
  )
}

패키지를 만들 계획이라면 roxygen2 스타일 주석(#')을 쓰면 자동으로 문서가 생성됩니다.