반응형

R에서 함수는 코드의 재사용성을 높이고, 가독성을 향상시키며, 프로그래밍의 효율성을 높입니다.

이번 포스팅에서는 사용자 정의 함수를 만드는 방법에 대해 알아보겠습니다. 

 

 

목차

  1. 기본 구조
  2. 매개변수 정의하기
  3. 반환 값 설정하기
  4. 조건문과 제어문을 활용한 함수 로직 작성
  5. 함수 내의 변수 스코프 이해하기
  6. 익명 함수와 함수의 일급 객체 특성
  7. 함수 안의 함수: 중첩된 함수
  8. 함수의 문서화와 주석

 

1. 기본 구조

R에서 함수를 정의하는 방법은 매우 간단합니다. 사용자 정의 함수는 코드의 재사용성을 높여주며, 특정 작업을 수행하는 코드 블록을 한 곳에 모아 놓습니다.

 

function()의 기본 구조와 문법

R에서 함수를 정의할 때 사용하는 기본 구조는 function()입니다. 이 구조 내에서 원하는 매개변수를 설정하고, 중괄호 `{}` 내에 실행할 코드를 작성합니다.

my_function <- function(parameters) {
    # 함수의 코드 블록
    return(result)
}

여기서 `parameters`는 함수에 전달될 입력 값이며, `return`은 함수의 결과를 반환하는 역할을 합니다. `return`을 생략하면 함수의 마지막 표현식이 자동으로 반환됩니다.

 

간단한 함수 예제 생성

아래는 R에서 간단한 함수를 생성하는 예제입니다. 이 함수는 두 숫자의 합을 반환하는 기능을 합니다.

sum_two_numbers <- function(a, b) {
    result <- a + b
    return(result)
}

# 함수 사용 예제
sum_result = sum_two_numbers(5, 7)
print(sum_result)  # 출력: 12

위의 예제에서 sum_two_numbers는 사용자가 정의한 함수의 이름이며, `a`와 `b`는 함수의 매개변수입니다. 이 함수를 호출할 때, 원하는 값을 매개변수로 전달하여 결과를 얻을 수 있습니다.

함수를 사용하면 반복되는 코드를 줄이고, 코드의 가독성을 향상시킬 수 있습니다. 특히 복잡한 프로그램에서는 함수를 적절히 활용하면 코드의 구조와 로직을 더욱 명확하게 표현할 수 있습니다.

 

2. 매개변수 정의하기

R에서 함수를 정의할 때 가장 중요한 부분 중 하나는 매개변수의 정의입니다. 매개변수는 함수의 입력으로 사용되며, 함수 내에서 사용되는 변수를 의미합니다.

 

필수 매개변수와 선택적 매개변수

R의 함수에서는 매개변수를 필수적으로 요구하거나 선택적으로 지정할 수 있습니다. 필수 매개변수는 함수 호출 시 반드시 값을 전달해야 하는 반면, 선택적 매개변수는 값을 전달하지 않아도 됩니다.

# 예제 함수
example_function <- function(a, b = 3) {
    return(a + b)
}

print(example_function(5))       # 출력: 8 (b의 기본값 3을 사용)
print(example_function(5, 2))    # 출력: 7

위 예제에서, `a`는 필수 매개변수이고 `b`는 선택적 매개변수입니다. `b`에 값을 전달하지 않으면 기본값 3이 사용됩니다.

 

매개변수의 기본값 설정하기

선택적 매개변수는 기본값을 가질 수 있습니다. 함수를 호출할 때 해당 매개변수에 값을 전달하지 않으면 기본값이 사용됩니다.

multiply_numbers <- function(x, multiplier = 2) {
    return(x * multiplier)
}

print(multiply_numbers(4))       # 출력: 8 (multiplier의 기본값 2를 사용)
print(multiply_numbers(4, 3))    # 출력: 12

이 예에서, `multiplier`는 기본값 2를 가지는 선택적 매개변수입니다.

 

...를 사용한 가변 매개변수

R의 함수에서 `...`를 사용하면, 다양한 수의 매개변수를 전달할 수 있습니다. 이것을 가변 매개변수라고 합니다.

sum_numbers <- function(...) {
    return(sum(...))
}

print(sum_numbers(1, 2, 3, 4, 5))   # 출력: 15

`sum_numbers` 함수는 원하는 만큼의 숫자를 전달할 수 있습니다. 함수 내부에서 `...`는 전달된 모든 값을 포함하는 리스트로 처리됩니다.

매개변수는 함수의 동작을 제어하고 다양한 상황에 맞게 함수의 동작을 조절하는 데 중요한 역할을 합니다. R에서는 다양한 방식으로 매개변수를 정의하고 활용할 수 있어, 상황에 맞는 최적의 방법을 선택하는 것이 중요합니다.

 

3. 반환 값 설정하기

함수는 특정 작업을 수행한 후 그 결과를 반환하는 역할을 합니다. R에서 함수의 결과를 반환하는 방법은 return() 함수를 사용하는 것입니다.

 

return()의 사용법 및 중요성

R의 함수 내에서 return() 함수는 특정 값을 반환하고 함수의 실행을 종료하는 역할을 합니다.

calculate_area <- function(radius) {
    area <- 3.14159 * radius^2
    return(area)
}

result <- calculate_area(5)
print(result)  # 출력: 78.53975

위의 예제에서, 함수 calculate_area는 반지름을 입력으로 받아 원의 넓이를 계산한 후 그 결과값을 반환합니다. return()는 함수의 출력을 결정하며, 함수가 호출된 위치로 값을 반환합니다.

R에서는 return()을 생략할 경우, 함수의 마지막 표현식이 자동으로 반환값이 됩니다. 그러나 명시적으로 return()을 사용하는 것이 코드의 가독성과 명확성을 높이는 데 도움이 됩니다.

 

다양한 형태의 반환 값 (벡터, 리스트, 데이터프레임 등)

R에서 함수는 다양한 형태의 값을 반환할 수 있습니다. 기본적인 숫자나 문자열 뿐만 아니라 벡터, 리스트, 데이터프레임 등의 복잡한 데이터 구조도 반환 가능합니다.

# 벡터를 반환하는 함수
return_vector <- function(n) {
    return(1:n)
}

# 리스트를 반환하는 함수
return_list <- function(a, b) {
    list_data <- list(first = a, second = b)
    return(list_data)
}

# 데이터프레임을 반환하는 함수
return_dataframe <- function(names, ages) {
    df <- data.frame(Name = names, Age = ages)
    return(df)
}

print(return_vector(5))          # 출력: 1 2 3 4 5
print(return_list("apple", 10))  # 출력: $first [1] "apple", $second [1] 10
print(return_dataframe(c("Alice", "Bob"), c(25, 30))) 
# 출력: 
#    Name Age
# 1 Alice  25
# 2   Bob  30

위의 예제들은 함수가 벡터, 리스트, 데이터프레임을 반환하는 방법을 보여줍니다. 이처럼 R의 함수는 다양한 데이터 타입의 값을 반환할 수 있어, 원하는 형태의 결과를 얻기 위한 유연성이 높습니다.

함수의 반환 값은 함수의 목적과 사용되는 문맥에 따라 결정됩니다. 따라서 함수를 작성할 때는 어떤 형태의 값을 반환할 것인지, 그 값을 어떻게 활용할 것인지를 명확히 고려해야 합니다.

 

4. 조건문과 제어문을 활용한 함수 로직 작성

함수는 특정 작업을 수행하기 위한 코드 블록입니다. 때로는 함수 내에서 조건에 따라 다른 작업을 수행하거나, 특정 작업을 여러 번 반복해야 할 경우가 있습니다. 이러한 상황에서 조건문과 제어문은 함수의 로직을 효과적으로 작성하는 데 중요한 도구입니다.

 

if, else, switch 등을 활용한 조건부 실행

조건문은 특정 조건을 평가하고, 그 결과에 따라 다른 코드를 실행합니다. R에서는 if, elseswitch를 사용하여 조건부 실행을 구현할 수 있습니다.

# 조건에 따라 메시지 출력하는 함수
print_message <- function(number) {
    if (number > 0) {
        return("Positive number")
    } else if (number < 0) {
        return("Negative number")
    } else {
        return("Zero")
    }
}

print(print_message(5))   # 출력: Positive number
print(print_message(-3))  # 출력: Negative number
print(print_message(0))   # 출력: Zero

위의 예제에서, print_message 함수는 입력된 숫자가 양수인지, 음수인지, 아니면 0인지를 평가하고 그에 따른 메시지를 반환합니다.

switch 문은 여러 조건 중 하나를 선택하여 실행합니다. 주로 특정 값을 기준으로 다양한 경우의 수를 처리할 때 사용됩니다.

# 날짜의 요일을 반환하는 함수
day_of_week <- function(day_number) {
    switch(day_number,
           "Sunday",
           "Monday",
           "Tuesday",
           "Wednesday",
           "Thursday",
           "Friday",
           "Saturday")
}

print(day_of_week(3))  # 출력: Tuesday

 

for, while 등의 반복문 사용하기

반복문은 특정 코드 블록을 여러 번 실행할 때 사용합니다. R에서는 forwhile 반복문을 주로 활용합니다.

# 1부터 n까지의 합을 계산하는 함수
sum_n_numbers <- function(n) {
    total = 0
    for (i in 1:n) {
        total = total + i
    }
    return(total)
}

print(sum_n_numbers(5))  # 출력: 15

위의 예제에서, sum_n_numbers 함수는 1부터 n까지의 숫자를 합산하는 함수입니다. for 반복문을 사용하여 1부터 n까지 각 숫자를 total에 더하고, 최종 합산 결과를 반환합니다.

반복문은 데이터 처리, 시뮬레이션, 알고리즘 구현 등 다양한 상황에서 활용됩니다. R에서는 forwhile 외에도 apply 계열의 함수를 활용하여 반복 작업을 효율적으로 수행할 수 있습니다.

 

5. 함수 내의 변수 스코프 이해하기

함수 내에서 변수를 사용할 때, 해당 변수의 "스코프"는 매우 중요한 개념입니다. 스코프는 변수가 존재하고 접근 가능한 범위를 의미합니다. R에서는 주로 지역변수와 전역변수라는 두 가지 유형의 스코프를 구분합니다.

 

지역변수 vs 전역변수

지역변수는 함수 내에서만 사용되며, 함수 외부에서는 접근할 수 없습니다. 반대로 전역변수는 함수 외부에서 정의되며, 프로그램 전체에서 접근 가능합니다.

global_var <- "I am a global variable"

example_function <- function() {
    local_var <- "I am a local variable"
    print(global_var)
    print(local_var)
}

example_function()
# 함수 외부에서 지역변수에 접근 시도
# print(local_var)  # 에러 발생

위 예제에서 global_var는 전역변수이며, local_varexample_function 내의 지역변수입니다. 지역변수는 함수 외부에서 접근할 수 없기 때문에 마지막 줄의 코드는 에러를 발생시킵니다.

 

함수 내부에서 전역 변수 접근 방법

함수 내부에서 전역변수를 참조하거나 변경하려면 특별한 접근 방법이 필요합니다. R에서는 global 접두사나 <<- 연산자를 사용하여 전역변수에 접근할 수 있습니다.

counter <- 0

increase_counter <- function() {
    counter <<- counter + 1
}

increase_counter()
print(counter)  # 출력: 1

위 예제에서 increase_counter 함수는 전역변수 counter를 직접 수정합니다.

 

환경과 스코프에 대한 깊은 이해

R에서 모든 객체(변수, 함수 등)는 특정 "환경"에 존재합니다. 환경은 이름과 값의 쌍을 저장하는 테이블로 생각할 수 있습니다. 스코프는 환경과 밀접한 관련이 있으며, 변수가 어떤 환경에 존재하는지 결정합니다.

outer_function <- function() {
    x <- 1
    inner_function <- function() {
        x <- 2
        print(x)
    }
    inner_function()
    print(x)
}

outer_function()  # 출력: 2 1

위 예제에서, inner_functionxouter_functionx와는 다른 환경에 존재합니다. 따라서 두 함수는 서로 다른 x 값을 갖게 됩니다.

스코프와 환경의 이해는 함수 내에서 변수를 올바르게 사용하고 예상치 못한 문제를 피하기 위해 중요합니다. 특히, 복잡한 프로그램에서는 변수의 스코프를 명확하게 관리하는 것이 필요합니다.

 

6. 익명 함수와 함수의 일급 객체 특성

R에서 함수는 '일급 객체'입니다. 이는 함수를 다른 함수의 인자로 전달하거나, 함수에서 반환하거나, 변수에 저장할 수 있다는 것을 의미합니다. 이러한 특성은 함수형 프로그래밍 패러다임에서 중요한 개념입니다.

 

함수를 변수로 저장하고 전달하기

R에서 함수는 변수에 할당되어 다른 이름으로 사용될 수 있습니다. 또한, 다른 함수의 매개변수로 전달될 수도 있습니다.

# 함수를 변수에 저장
square <- function(x) x^2
squared_value = square(4)  # 16

# 함수를 다른 변수에 할당
another_square <- square
squared_value2 = another_square(5)  # 25

위의 예제에서, 함수 square는 숫자의 제곱을 계산합니다. 이 함수는 another_square라는 다른 변수에 할당되어 동일한 기능을 수행합니다.

 

lapply나 sapply에서 익명 함수 사용 예시

R의 lapplysapply 같은 함수들은 리스트나 벡터의 각 요소에 함수를 적용합니다. 이때 익명 함수(이름이 없는 함수)를 자주 사용합니다.

numbers <- list(1, 2, 3, 4)

# lapply를 사용하여 각 숫자 제곱하기
squared_numbers <- lapply(numbers, function(x) x^2)  

# sapply를 사용하여 각 숫자 제곱하기
squared_numbers2 <- sapply(numbers, function(x) x^2)

위의 예제에서 lapplysapply는 각각 리스트와 벡터의 모든 요소에 함수를 적용합니다. 여기서 사용된 함수는 이름이 지정되지 않은 익명 함수입니다.

익명 함수는 간단한 작업을 수행하는 함수를 빠르게 정의하고 사용하고자 할 때 유용합니다. 특히 데이터 분석 작업에서는 데이터를 변환하거나 요약하는 데 익명 함수를 자주 활용합니다.

R에서 함수의 일급 객체 특성은 함수를 동적으로 생성하거나, 다른 함수에 전달하거나, 복잡한 데이터 구조와 함께 사용하는 데 큰 유용성을 제공합니다. 이는 R의 풍부한 데이터 분석 기능과 잘 어울리며, 사용자의 코드 작성 효율성을 높이는 데 기여합니다.

 

7. 함수 안의 함수: 중첩된 함수

R 프로그래밍에서는 하나의 함수 내부에서 다른 함수를 정의하는 것이 가능합니다. 이렇게 함수 내부에 정의된 함수를 '중첩된 함수'라고 부릅니다. 중첩된 함수는 외부 함수 내에서만 호출이 가능하며, 특정 작업을 지원하거나 코드의 가독성을 높이기 위해 사용됩니다.

 

함수 내부에서 추가 함수 정의하기

함수 내에서 추가 함수를 정의하려면, 그 함수 내에 일반적인 함수 정의 방법을 사용하면 됩니다. 중첩된 함수는 해당 함수 내에서만 접근 가능하고, 외부에서는 접근할 수 없습니다.

outer_function <- function(x) {
    inner_function <- function(y) {
        return(y^2)
    }
    return(inner_function(x) + 10)
}

result <- outer_function(4)  # 결과: 26 (4의 제곱은 16, 16 + 10 = 26)

위 예제에서, outer_function 내부에 inner_function이라는 함수가 정의되어 있습니다. inner_function은 숫자의 제곱을 반환하며, outer_function은 이 제곱값에 10을 더한 값을 반환합니다.

 

중첩된 함수의 활용 예시 및 유용성

중첩된 함수는 여러 가지 이유로 유용하게 사용될 수 있습니다. 일부 복잡한 연산을 단순화하거나, 코드의 가독성을 향상시키거나, 특정 연산의 반복을 줄이기 위해 중첩된 함수를 사용할 수 있습니다.

calculate_area_volume <- function(radius) {
    pi_value <- 3.14159
    
    area_function <- function() {
        return(pi_value * radius^2)
    }
    
    volume_function <- function() {
        return((4/3) * pi_value * radius^3)
    }
    
    list(area = area_function(), volume = volume_function())
}

result <- calculate_area_volume(3)
print(result)  # 결과: area = 28.27431, volume = 113.09724

위 예제에서, calculate_area_volume 함수는 원의 반지름을 입력으로 받아, 해당 원의 면적과 구의 부피를 계산하여 리스트 형태로 반환합니다. 이때, 면적과 부피를 각각 계산하는 두 개의 중첩된 함수가 정의되어 있습니다.

중첩된 함수를 사용하면 코드의 구조가 더 명확해지며, 특정 작업에 대한 로직을 재사용하기 쉬워집니다. 또한, 중첩된 함수는 외부 함수의 변수에 접근할 수 있기 때문에, 특정 정보를 공유하면서 독립적인 작업을 수행하는 데 유용합니다.

 

8. 함수의 문서화와 주석

함수를 작성할 때, 해당 함수의 기능, 인자, 반환값 및 사용 예시 등을 명확하게 알려주는 것은 매우 중요합니다. 문서화와 주석은 다른 사용자나 개발자가 코드를 이해하고 활용하는 데 큰 도움을 줍니다.

 

roxygen2를 사용한 함수 문서화

roxygen2는 R에서 패키지와 함수를 문서화하는데 사용되는 유명한 패키지입니다. roxygen2를 사용하면 함수의 문서화를 간편하게 작성하고, 해당 문서를 R의 표준 형식으로 변환할 수 있습니다.

# install.packages("roxygen2")
library(roxygen2)

#' 함수의 설명
#'
#' @param x 첫 번째 인자의 설명
#' @param y 두 번째 인자의 설명
#' @return 반환값의 설명
#' @examples
#' example_function(1, 2)
example_function <- function(x, y) {
    return(x + y)
}

# roxygen2를 사용하여 문서 생성
devtools::document()

위 예제에서, example_function 함수 앞에 #'로 시작하는 주석은 roxygen2 문서화 주석입니다. @param, @return, @examples 등의 태그를 사용하여 함수에 대한 세부 정보를 제공합니다. 그 후, devtools::document() 함수를 호출하여 해당 주석을 R의 표준 문서 형식으로 변환합니다.

 

적절한 주석 작성의 중요성

코드 주석은 코드의 동작 방식, 의도, 특정 부분의 설명 등을 제공하는 짧은 메모와 같습니다. 주석은 코드의 가독성을 높이고, 코드의 유지 및 관리를 용이하게 합니다.

# 이 함수는 두 숫자의 합을 반환합니다.
add_numbers <- function(a, b) {
    # 두 숫자를 더하는 부분
    result <- a + b
    return(result)
}

위 예제에서, 간단한 주석들이 함수의 목적과 내부 동작을 설명하고 있습니다. 주석은 간결하면서도 명확하게 코드의 의도와 동작을 전달해야 합니다.

적절한 주석 작성은 코드의 품질을 향상시키며, 사람이 코드를 이해하고 활용하는 데 큰 도움을 줍니다. 또한, 코드 수정 시 주석은 중요한 참조 자료로 활용됩니다.

 

아래 포스팅도 참고해 보세요!

 R에서 데이터 정규성 검정 방법
 R에서 벡터 활용
 R에서 데이터 정렬하는 방법
 [R]데이터전처리 할때 필요한 10가지 코드
 R에서 흔히 마주치는 에러 상황과 그 대처법 정리

 

반응형

'통계 > R' 카테고리의 다른 글

R을 사용한 일원배치 ANOVA의 기초와 해석법  (0) 2023.10.11
[R]대응 표본 t-검정 하는 법  (0) 2023.10.11
R에서 데이터 정렬하기  (0) 2023.10.02
R에서 벡터 활용  (1) 2023.09.27
R을 활용한 조절효과 분석 기법  (0) 2023.09.27
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기