Apply 계열 함수는 뜻 그대로 적용시키는 기능을 가진 함수이다.
이 함수는 array(배열) 과 matrix(행렬)/data frame에 적용시킬 수 있는데
보통 특정 행(row)이나 열(column) 내부에 있는 데이터들에 어떠한 명령 적용시켜 반환하기 위해서는
보통 for문 이 많이 사용된다.
하지만 for문의 단점 중 하나는 처리할 데이터가 많을 때,
시간이 많이 소요되고 코드를 비교적 길게 써야한다는 것이다.
이 부분에 대해
매트릭스나 데이터프레임에 있는 행들이나 열들을 알아서 하나하나 차례대로 꺼내 연산 작업을 수행하도록 만들어진 함수라고 보면 된다.
우선 가장 기초가 되는 apply함수를 알아보자.
[구조] apply ( X, MARGIN, FUN, ..., simplify = T )
- X : 행렬 ( Array / Matrix )
- MARGIN : 1 (행 단위) / 2 (열 단위)
- FUN : 정한 단위의 각 값들에 적용시킬 함수/연산
- ... : 해당 apply 함수에 적용시킬 선택적 인수들 / 위 FUN의 properties 값을 입력
입력 : 행렬 ( Array )
출력 : 행렬 ( Array )
간단하게 말하자면 행렬이나 배열에 대해서 Margin으로 지정한 기준에 따라
기준에 해당하는 위치에 Function을 적용시켜주는 기능이다.
weight <- c(65.4, 55, 380, 72.2, 51, NA)
height <- c(170, 155, NA, 173, 161, 166)
gender <- c("M", "F","M","M","F","F")
df <- data.frame(w=weight, h=height)
> df
w h
1 65.4 170
2 55.0 155
3 380.0 NA
4 72.2 173
5 51.0 161
6 NA 166
이렇게 하나의 Data Frame을 생성해보자.
이곳에 "NA 값은 무시하고 각 행의 합 & 각 열의 합" 을 구하는 apply 함수를 사용해보자.
apply(df, 1, sum, na.rm=TRUE) # 각 행들의 합
[1] 235.4 210.0 380.0 245.2 212.0 166.0
apply(df, 2, sum, na.rm=TRUE) # 각 열들의 합
w h
623.6 825.0
앞서 설명했던 apply()
함수는 행렬만을 입력 받을 수 있다는 단점이 있다.
함수를 각 값에 적용시키고 싶은 대상이 벡터(Vector) 나 리스트(List) 일 경우에도 사용할 수 있는 함수들은 아래와 같다.
[구조] lapply ( X, FUN, ...)
- X : 벡터 / 리스트 / 행렬 / 배열 ( Vector / List / Matrix / Array )
- FUN : 정한 단위의 각 값들에 적용시킬 함수/연산
- ... : 해당 apply 함수에 적용시킬 선택적 인수들 / 위 FUN의 properties 값을 입력
입력 : 벡터 ( Vector ) / 리스트 ( List ) / 행렬 ( Array / Matrix / DataFrame)
출력 : 리스트 ( List )
우선 간단하게 위에서 사용했던 DataFrame을 써보자
lapply(df, sum, na.rm=TRUE)
$w
[1] 623.6
$h
[1] 825
리스트형으로 출력되는 모습이고 각 열에 대해 연산되었다는 것을 알 수 있다.
이번엔 벡터와 리스트를 입력했을 때의 결과를 살펴보자.
v<-c("abc", "DEF", "TwT") # 벡터
l<-list("abc", "DEF", "TwT") # 리스트
[[1]]
[1] "-abc-"
[[2]]
[1] "-DEF-"
[[3]]
[1] "-TwT-"
이렇게 lapply()
함수는 이름 그대로 list + apply로
벡터든 리스트든 출력은 무조건 list형식으로 한다.
따라서, 적용 후에 벡터로 변환하고 싶으면 unlist()
함수를 꼭 사용해주어야 한다.
[구조] sapply ( X , FUN , ... , simplify = T , USE.NAMES = TRUE )
- X : 리스트 / 벡터 ( list / vector )
- FUN : 정한 단위의 각 값들에 적용시킬 함수/연산
- ... : 해당 apply 함수에 적용시킬 선택적 인수들 / 위 FUN의 properties 값을 입력
입력 : 리스트( list ) / 벡터( vector )
출력 : 벡터( vector ) / 행렬 ( Array )
lapply()
는 unlist()
함수를 써서 풀어줘야 한다는 점에서 번거롭고 불편할 수 있는 함수이다.
그 점을 보완하여 벡터(Vector) / 행렬(Array)을 반환하는 '유저(user) 친화.ver' 으로
lapply 함수의 wrapper라고 볼 수 있다.
이번에도 apply함수 예시로 썼던 data frame을 사용해보자
sapply(df, sum, na.rm=TRUE)
w h
623.6 825.0
결과물을 보면 apply()
함수에서 열을 기준 (MARGIN = 2) 으로 처리했던 모습과 동일하게 리스트가 아닌 벡터(vector) 형태로 출력이 된다.
[구조] vapply ( X , FUN , FUN.VALUE , ... , USE.NAMES = TRUE )
- X : 리스트 / 벡터 ( list / vector )
- FUN : 정한 단위의 각 값들에 적용시킬 함수/연산
- FUN.VALUE : 함수 연산 결과를 어떤 데이터 타입으로 반환시킬지 설정
- ... : 해당 apply 함수에 적용시킬 선택적 인수들 / 위 FUN의 properties 값을 입력
입력 : 리스트( list ) / 벡터( vector )
출력 : 벡터( vector ) / 행렬 ( Array )
vapply()
는 위의 sapply()
와 유사한 함수이지만
FUN.VALUE 인수에 출력(output) format 을 명확히 정의해서 더 안전한 함수이다.
(가끔씩은 더 빠르다고..한다..!)
이번 예시는 R에서 빼놓을 수 없는 iris 데이터 셋을 사용하겠다
# 총 다섯개의 열 중 다섯 번째 열은 문자 데이터이기 때문에 함수 적용에 어려움이 있음. -> iris[,1:4]
# numeric
a <- vapply(iris[,1:4], function(x) {x > 5}, numeric(length(iris[, 1])))
head(a)
# 각 값에 함수를 적용한 결과로 5보다 큰 값들만 존재하기 때문에 length()를 적용했을 때, 존재 : 1 / 해당하지 않는 값 : 0 으로 출력된다.
Sepal.Length Sepal.Width Petal.Length Petal.Width
[1,] 1 0 0 0
[2,] 0 0 0 0
[3,] 0 0 0 0
[4,] 0 0 0 0
[5,] 0 0 0 0
[6,] 1 0 0 0
# 1 : TRUE / 0 : FALSE 인 점을 활용
b <- vapply(iris[,1:4], function(x) {x > 3}, logical(length(iris[, 1])))
head(b)
Sepal.Length Sepal.Width Petal.Length Petal.Width
[1,] TRUE FALSE FALSE FALSE
[2,] FALSE FALSE FALSE FALSE
[3,] FALSE FALSE FALSE FALSE
[4,] FALSE FALSE FALSE FALSE
[5,] FALSE FALSE FALSE FALSE
[6,] TRUE FALSE FALSE FALSE
위와 같이 출력되는 유형을 결정해줌으로서 안전한 함수라고 볼 수 있다.
단, 함수 적용을 모든 값들 대상으로 처리되기 때문에
하나의 열이 다른 타입의 데이터이거나
각 각의 값에 적용되지 않는 큰 범위의 함수나 출력값을 설정할 경우, 작동되지 않는다.
[구조] mapply ( FUN , ... )
- FUN : 정한 단위의 각 값들에 적용시킬 함수/연산
- ... : 해당 함수에 적용시킬 선택적 인수들
입력 : 리스트( list ) / 벡터( vector )
출력 : 벡터( vector ) / 행렬 ( Array )
구조를 살펴보면 알 수 있듯이 함수가 가장 첫 번쨰에 위치한다.
그 이후, 여러가지 처리 대상(Arguments)를 추가하여 여러 값을 기입할 수 있다.
# sprintf : 포맷팅 방식 _ 첫번째 인수 : 적용 문자열 / 이후 : 순서대로 투입될 값들
mapply(function(d, s) { sprintf("%d%s", d, s) }, 1:3, c("a", "b", "c"))
[1] "1a" "2b" "3c"
# 연산기호로 함수 적용시킬 수 있음.
mapply(`+`, 1:10, 101:110)
[1] 102 104 106 108 110 112 114 116 118 120
[구조] tapply ( X , INDEX , FUN = NULL , ... )
- X : 리스트 / 벡터 ( list / vector )
- INDEX : 앞선 X와 같은 갯수의 인덱스가 필요하며 Grouping되는 기준이 된다.
입력 : 리스트( list ) / 벡터( vector ) & 팩터( factor/level )
출력 : 벡터( vector ) / 행렬 ( Array )
tapply()
는 독특하게
값들을 "특정 기준으로 Grouping 한 후" ▶ Factor 형 ,
함수를 적용하는 방식이다.
"원소별 처리"가 아니라 그룹별 처리를 한다
# factor로 쓰일 INDEX
# M과 F (factor)
gender <- c("M", "F","M","M","F","F")
# 1:M 2:F 3:M 4:M 5:F 6:F
# M -> 1,3,4 / F -> 2,5,6 _factor끼리 함수적용
tapply(1:6, gender, sum)
F M
13 8
# factor = 1, 2
# 1:1 1:2 1:3 1:4 1:5 2:6 2:7 2:8 2:9 2:10
# 1: 1,2,3,4,5 / 2: 6,7,8,9,10
tapply(1:10, rep(c(1,2), each=5), sum)
1 2
15 40