개요

이번 포스팅에는 R에서 함수를 정의/호출하는 방법을 알아본다.

코드중심의 설명이기 때문에 본문 보다는 코드의 주석이 집중적으로 추가되어 있으니 참고 바란다.

함수 정의

f <- function(x, y) {
  x + y # 생략하면 마지막 줄을 리턴해줌
}

> f(1,2)
[1] 3

피타고라스의 정리를 한번 구현해보자.

> hypotenuse <- function(x, y) {
+ sqrt(x^2+y^2)
+ }

> hypotenuse <- function(x, y) sqrt(x^2+y^2) # inline으로 나타낼 수 있음

> hypotenuse(3, 4)
[1] 5

> hypotenuse(x=3, y=4) # same as hypotenuse(y=4, x=3)
[1] 5

함수 argument 정보 보기

> hypotenuse <- function(x=5, y=12) sqrt(x^2+y^2)

> args(hypotenuse)
function (x = 5, y = 12)
NULL

> formalArgs(hypotenuse)
[1] "x" "y"

함수 scope와 Global scope

> h <- function(x) {
+ k <- 2
+ k*(x+y)
+ }

> k
Error: object 'k' not found

> h(9)
Error in h(9) : object 'y' not found

> y <- 16

> h(9)
[1] 50

예제를 한번 더 보자면 아래와 같다.

> f <- function(x) {
+ a*x
+ }

> f(3)
Error in f(3) : object 'a' not found

> a <- 10

> f(3)
[1] 30

> g <- function(y) {
+ a <- 100
+ f(y)
+ }

> g(3)
[1] 30

함수 scope

위에서 정의한 f함수는 다른 함수에서 사용 할 수 있다. 시험문제로 내기 좋은 패턴이다.

> h <- function(y) {
+ a <<- 100
+ f(y)
+ }

> h(3)
[1] 300

예제를 하나 더 보도록 하자.
여담이지만 runif는 발음을 “run-if”가 아니라 “r-uniform”으로 발음한다. R을 쪼끔 아는지 모르는지 이걸로 테스트해보면 재밌을듯 하다.(농담)

> y <- 16

> h2 <- function(x) {
+ if(runif(1) > 0.5) y <- 12 # runinf(1)은 0~1사이의 랜덤값을 획득함
+ x*y
+ }

> replicate(10, h2(9)) # h2(9)를 10번 실행한다는 의미이다. 랜덤값에 따라 y는 16 아니면 12 둘중의 하나가 된다.
[1] 144 108 108 108 144 108 108 144 108 144

퀴즈

y <- 16

f <- function(x) {
  y <- 12
  x * y
}

f(3)

정답은 36이다.

벡터와 함수

아래와 같이 벡터를 입력값으로 받아 함수 계산을 할 수 있다. 시험문제로 내기 좋은 특징이다.

> normalize <- function(x) {
+ m <- mean(x)
+ s <- sd(x)
+ (x-m)/s
+ }

> x <- c(1, 3, 6, 10, 15)

> z <- normalize(x)

> mean(z) # almost 0
[1] -5.572799e-18

> sd(z)
[1] 1

하나 유의할 점은 normalize는 NA값이 하나라도 섞이면 모든 값이 NA로 바뀌므로 아래와 같은 처리가 필요하다.

> normalize(c(1, 3, 6, 10, NA))
[1] NA NA NA NA NA

> normalize <- function(x, na_remove=FALSE) {
+ m <- mean(x, na.rm=na_remove)
+ s <- sd(x, na.rm=na_remove)
+ (x-m)/s
+ }

> normalize(c(1, 3, 6, 10, NA), na_remove=TRUE)
[1] -1.0215078 -0.5107539 0.2553770 1.2768848  

> normalize <- function(x, ...) {
+ m <- mean(x, ...)
+ s <- sd(x, ...)
+ (x-m)/s
+ }

> normalize(c(1, 3, 6, 10, NA), na.rm=TRUE)
[1] -1.0215078 -0.5107539 0.2553770 1.2768848 NA

함수작성 예제1

mysummary <- function(x, npar=TRUE, print=TRUE) {

  if (!npar) {
    center <- mean(x); spread <- sd(x)
    } else {
      center <- median(x); spread <- mad(x)
      }
  if (print & !npar) {
    cat(" Mean=", center, "\n", "SD=", spread, "\n")
    } else if (print & npar) {
      cat(" Median=", center, "\n", "MAD=", spread, "\n")
      }
  result <- list(center=center, spread=spread)
  return(result)
  }

  > mysummary(3) # 아래와 같이 리스트로 반환해준다.
   Median= 3
   MAD= 0
  $center
  [1] 3

  $spread
  [1] 0

  > # invoking the function
  > set.seed(1234)
  > x <- rpois(500, 4)
  > y <- mysummary(x)
  Median= 4
  MAD= 1.4826

  > # y$center is the median (4)
  > # y$spread is the median absolute deviation (1.4826)
  > y <- mysummary(x, npar=FALSE, print=FALSE)
  > y
  $center
  [1] 4.052
  $spread
  [1] 2.01927
  > # no output
  > # y$center is the mean (4.052)
  > # y$spread is the standard deviation (2.01927)

재귀함수, factorial

1 * 2 * 3 * 4 * 5 * 6 = 720 의 계산 과정을 아래와 같이 작성하는 것이다.

> ftn_factorial <- function(n) {
+ if(n <= 1) {
+ return(1)
+ } else {
+ return(n * ftn_factorial(n-1))
+ }
+ }

> ftn_factorial(6)
[1] 720

재귀함수, fibonacci

피보나치 수열을 9항까지 출력하면 0, 1, 1, 2, 3, 5, 8, 13, 21와 같다.

> ftn_fibonacci <- function(n) {
+ if(n <= 1) {
+ return(n)
+ } else {
+ return(ftn_fibonacci(n-1) + ftn_fibonacci(n-2))
+ }
+ }

> nterms = as.integer(readline(prompt="How many terms? "))
How many terms? 9    

> if(nterms <= 0) {
+ print("Plese enter a positive integer")
+ } else {
+ print("Fibonacci sequence:")
+ for(i in 0:(nterms-1)) {
+ print(ftn_fibonacci(i))
+ }
+ }
[1] "Fibonacci sequence:"
[1] 0
[1] 1
[1] 1
[1] 2
[1] 3
[1] 5
[1] 8
[1] 13
[1] 21

산술 연산자

비교 연산자

논리 연산자

커스텀 연산자

아래와 같이 연산자를 따로 만둘어 낼 수도 있다.

> "%a2b%" <- function(a, b) return(a+2*b)
> 3 %a2b% 5
[1] 13

근의 공식 함수 작성해보기

$ x = \frac {-b \pm \sqrt {b^2 – 4ac} }{2a} $ 을 함수초 작성해보자.

> #Find the real root(s) of a quadratic equation
> quadratic <- function(coeff) {
+ a <- coeff[1]
+ b <- coeff[2]
+ c <- coeff[3]
+ d <- b^2 - 4*a*c
+ cat("The discriminant is: ", d, "\n")
+ if(d < 0) cat("There are no real roots. ", "\n")
+ if(d >= 0) {
+ root1 <- (-b + sqrt(d))/(2*a)
+ root2 <- (-b - sqrt(d))/(2*a)
+ cat("root1: ", root1, "\n")
+ cat("root2: ", root2, "\n")
+ }
+ }

> abc <- scan()
1: 2
2: -1
3: -8
4:
Read 3 items

> abc
[1] 2 -1 -8

> quadratic(abc)
The discriminant is: 65
root1: 2.265564
root2: -1.765564

소수 구해보기

> check_prime <- function() {
+ num = as.integer(readline(prompt="Enter a number: "))
+ flag = 0
+ if(num > 1) {
+ flag = 1
+ for(i in 2:(num-1)) {
+ if ((num %% i) == 0) {
+ flag = 0
+ break
+ }
+ }
+ }
+ if(num == 2) flag = 1
+ if(flag == 1) {
+ print(paste(num,"is a prime number"))
+ } else {
+ print(paste(num,"is not a prime number"))
+ }
+ }

> check_prime()
Enter a number: 3
[1] "3 is a prime number"

> check_prime()
Enter a number: 6
[1] "6 is not a prime number"