이동식 저장소

7. R 프로그래밍 본문

CS/R 전산실습

7. R 프로그래밍

해스끼 2020. 12. 5. 18:26

1. 사용자 정의 함수

1.1 함수 정의 방법

func = function(arg1, arg2, ...) {
    ...
}

한 줄짜리 함수는 중괄호를 안 써도 된다.

my.sum = function(x, y) x + y

1.2 매개변수

매개변수에는 디폴트 값을 줄 수 있다. 디폴트 값이 주어진 매개변수는 함수 호출 시 생략해도 된다.

func = function(x, conf=0.95) {
    ...
}

func(c(1, 2))            # OK
func(c(1, 2), conf=0.9)  # OK

기존의 함수를 이용하여 함수를 정의할 때 생략 부호 ...을 이용하면 매우 편리하다. 일반적으로 생략 부호는 함수의 마지막 매개변수로 지정한다. 함수를 호출할 때 ... 부분에 사용한 매개변수는 모두 기존 함수로 전달된다.

f = function(x, ...) {
    mean(x, ...)/sd(x, ...)
}

f(c(1, 2, NA), na.rm=TRUE)

1.3 매개변수를 직접 지정

다음의 함수가 있다고 하자.

my.power = function(x, y) x^y

함수 my.power의 두 매개변수 x, y의 값을 직접 지정할 수 있다.

my.power(2, 3)     # 8
my.power(y=3, x=2) # 8

1.4 결과 반환

R에서는 return(x)를 사용하여 x를 반환할 수 있다. return이 없는 경우 마지막 문장의 실행 결과가 반환된다. return이 없는데 마지막 문장이 할당(assign)문인 경우 아무것도 반환되지 않는다.

my.power2 = function(x, y) {
    return(x^y)
}

# x의 평균과 표준편차를 반환
my.desc = function(x, ...) {
    m = mean(x, ...)
    s = sd(x, ...)
    return(list(mean=m, sd=s))
}

2. 조건문

2.1 if

if (condition) {
    ...
}

if (cond1) {
    ...          
} else if (cond2) {

} else {
    ...          
}

닫는 중괄호 }의 위치에 주의하자.

주의사항

if()의 조건에는 하나의 논리값만 사용된다. 예를 들어 벡터 xy를 사용하여 x > y라는 조건을 만들 경우, x > y의 모든 논리값이 아닌 첫 번째 값만 사용한다. 모든 논리값을 사용하고 싶다면 다음의 함수를 사용해야 한다.

7.2 모든 논리값 사용: ifelse()

조건벡터의 값을 사용하고자 한다면 ifelse()를 사용할 수 있다.

ifelse(condition, exp1, exp2)

condition이 참이면 exp1을, 거짓이면 exp2를 벡터에 넣어 반환한다. 예를 들어 위에서 설명한 x > y의 경우 xy의 각 원소를 비교하여 x > y이면 exp1을, 그렇지 않으면 exp2를 결과 벡터에 넣는다.

7.3 switch()

switch(exp, options list)
  • exp가 숫자인 경우에는 리스트에서 해당 위치의 값이 선택된다.
  • exp가 문자인 경우에는 리스트에서 동일한 값을 갖는 항목을 선택한다. 이때 리스트에서는 "를 사용하지 않는다.
func = function(x, type) {
    switch(type, mean=mean(x), med=median(x))
}

x = c(1, 2, 3, 4, 50)
func(x, "mean") # 12
func(x, "med")  # 3

복합 조건

  • &|은 벡터의 원소별로 조건을 체크한다. 즉 두 논리벡터를 & 또는 |로 비교하면 각 원소에 대해 비교한 값이 출력된다.
  • &&||은 벡터의 첫번째 값만 체크한다. 즉 두 논리벡터를 && 또는 ||로 비교하면 논리벡터의 첫 번째 값만 비교한 결과를 반환한다.

3. 반복문

3.1 for()

기본 문법은 다음과 같다.

for (var in seq) {
    ...
}

# ex
for (i in 1:5) {
    print(i)
}

참고: 빈 벡터를 만드는 vector()

vec = vector("double", 5) # double, int...

참고: 객체를 문자열로 이어 붙이는 cat()

cat(string..., sep="") # sep: 각 객체 사이에 들어갈 문자열

print(cat("1", "2", "3", sep=", ")) # 1, 2, 3

3.2 while()

기본 문법은 다음과 같다.

while (condition) {
    ...
}

4. 함수형 프로그래밍

R에서는 함수를 매개변수로 넘겨줄 수 있다. 즉 R에서는 함수가 일급 객체이다.

사용 예시: lapply(), sapply()

lapply(x, fun, ...)
sapply(x, fun, ...)

lapply()x의 각 요소에 fun을 적용한 결과를 리스트로 반환한다. sapply()lapply()와 같지만 벡터 또는 행렬을 반환한다. x는 벡터, 리스트 등이 될 수 있다. 즉 lapplysapplyx의 각 원소 xx에 대해 fun(xx, ...)을 실행한 결과를 반환한다고 생각하면 된다.

x = list(a1=1:5, a2=rnorm(5), a3=c(T, T, F, T))

lapply(x, mean, na.rm=TRUE)
# 결과
$a1
[1] 3

$a2
[1] 0.6190589

$a3
[1] 0.75

5. 함수형 프로그래밍으로 데이터 다루기

5.1 행렬에 적용하기

행렬에는 apply()를 적용할 수 있다.

apply(x, dir, fun, ...)
  • x: 행렬 또는 배열
  • dir: 함수를 적용할 방향. 1: 행에 적용, 2: 열에 적용
  • fun: 적용할 함수
  • ...: fun의 매개변수

5.2 데이터프레임에 적용하기

5.2.1 tapply()

tapply(x, index, fun, simplify=TRUE)
  • x: 벡터
  • index: 그룹화할 기준, factor여야 함
  • fun: 함수
  • simplify: TRUE이면 벡터를, FALSE이면 리스트를 반환
# Origin별 MPG.city의 평균 비교
with(Cars93, tapply(MPG.city, Origin, mean))

5.2.2 split(), lapply() 활용

# split(A, B): A를 요인 B의 값에 따라 분리한 리스트 반환
grouped = with(Cars93, split(MPG.city, Origin)) 
lapply(grouped, mean)

5.2.3 group_by(), summarise() 활용

이건 뭐 맨날 하던 거니까.

Cars93 %>%
    group_by(Origin) %>%
    summarise(m=mean(MPG.city), n=n())

5.2.4 모든 변수에 적용하려면?

사실 데이터프레임도 내부적으로는 리스트로 저장된다. 따라서 lapply() 또는 sapply()를 사용하여 각 변수마다 함수를 적용할 수 있다.

sapply(data, fun, ...)

위의 코드는 데이터프레임 data의 각 변수 col에 대해 fun(col, ...)을 실행한다.

7.6 purrr 프로그래밍

purrrtidyverse의 하위 패키지로, 함수형 프로그래밍을 도와주는 많은 함수를 제공한다.

map

map(.x, .f, ...)
  • .x: 벡터 또는 리스트
  • .f: .x의 각 원소에 적용할 함수
  • ...: .f의 추가 매개변수
  • 반환값: 리스트

결과값을 벡터로 출력하고 싶다면 결과값의 유형에 따라 map_lgl(논리값), map_int(정수형), map_dbl(실수형), map_chr(문자열)을 사용하면 된다.

x <- list(a1=1:5, a2=rnorm(5), a3=c(TRUE, FALSE, TRUE, TRUE))
map_dbl(x, mean)
         a1          a2          a3 
 3.00000000 -0.02400819  0.75000000 

map에서 사용자 정의 함수 사용하기

map 에서 사용될 함수를 구문 안에서 정의할 수 있다.

airquality %>%
    select(-c(Month, Day)) %>%
    map_dbl(function(x) sum(x, na.rm=TRUE)/sum(!is.na(x)))
     Ozone    Solar.R       Wind       Temp 
 42.129310 185.931507   9.957516  77.882353 

사용자 정의 함수의 시작을 function 대신 ~로 사용할 수도 있다. 단 이 경우 매개변수를 하나밖에 사용할 수 없으며, 매개변수의 이름은 . 또는 .x로 사용해야 한다.

airquality %>%
    select(-c(Month, Day)) %>%
    map_dbl(~ sum(.x, na.rm=TRUE)/sum(!is.na(.x)))

map에서 .f에 함수 대신 숫자 또는 문자를 입력하면

원소를 선택하는 인덱싱이 실행된다.

df = list(x1=1:3, x2=2:4, x3=3:5)
map_int(df, 2)
x1 x2 x3 
 2  3  4 

df의 두 번째 원소 x2를 선택하는 것이 아니라 df의 각 원소의 2번째 원소를 고르는 것이다.

map2

map2(.x, .y, .f, ...)
  • .x, .y: 벡터
  • .f: 함수
  • ...: .f의 추가 매개변수

map2.x.y의 각 원소를 각각 .f의 첫 번째와 두 번째 매개변수로 사용하여 .f를 실행한다. 자세한 사항은 강의자료 629p를 참고하자.

'CS > R 전산실습' 카테고리의 다른 글

5. ggplot2  (0) 2020.12.03
4. dplyr  (0) 2020.12.02
Comments