재현가능한 과학적 분석을 위한 중급 R
Apply 함수
학습 목표
- apply 함수를 사용해서 작업을 효율적으로 자동화하는 방법을 학습한다.
apply
,lapply
,sapply
,tapply
,mapply
함수간 차이를 이해한다.
벡터화 작업 자동화
앞서 for
루프를 소개했다: 많은 프로그래밍 언어에서 공통된 기본 프로그래밍 구성체(construct). R에는 작업을 자동화하는 더 최적화된 방식이 있는데 for
루프보다 속도가 더 빠를 뿐만 아니라, 결과 객체를 사전에 정의해야되는 고통도 함께 가져가 버렸다.
접하게 되는 가장 흔한 함수가 lapply
가 되는데, 이 함수는 sapply
와 매우 밀접하게 연관되어 있다.
다음 for
루프를 살펴보자:
for (cc in gap[,unique(continent)]) {
popsum <- gap[year == 2007 & continent == cc, sum(pop)]
print(paste(cc, ":", popsum))
}
상기 함수는 각 대륙별로 전체 인구를 계산하고 나서 결과를 출력한다. 바로 출력하는 대신에 결과를 저장하려면, 사전에 벡터를 미리 생성하고 나서 결과를 저장하거나, apply
계열 함수 하나를 선택하면 세부적인 사항은 알아서 자동 수행한다:
results <- lapply(gap[,unique(continent)], function(cc) {
popsum <- gap[year == 2007 & continent == cc, sum(pop)]
popsum
})
names(results) <- gap[,unique(continent)]
results
$Asia
[1] 3811953827
$Europe
[1] 586098529
$Africa
[1] 929539692
$Americas
[1] 898871184
$Oceania
[1] 24549947
lapply
함수는 벡터(혹은 리스트)를 첫번째 인자(이번 경우에는 대륙명 벡터)로 받고, 두번째 인자로 함수를 받는다. lapply
함수는 첫번째 인자에 들어있는 모든 요소에 대해 연산작업을 수행한다. 이런 점은 for
루프와 매우 유사하다: 먼저 cc
변수에 첫번째 대륙명 “Asia”를 저장하고 나서, 함수 몸통에 있는 코드를 실행한다. 그리고 나서 cc
변수에 두번째 대륙명을 저장하고 함수몸통을 실행한다. 이를 모든 요소에 반복한다. 함수 몸통에 담긴 코드는 정확하게 for
루프와 동일하게 간주해도 좋다. 마지막 행 결과가 lapply
함수에 반환되는데, 이때 반환되는 결과는 리스트로 결합된다.
sapply
함수는 lapply
함수와 동일한데, 차이점은 결과객체를 단순화함에 있다. lapply
함수 대신에 sapply
함수로 동일한 코드를 실행하면, 벡터로 결과가 반환된다:
results <- sapply(gap[,unique(continent)], function(cc) {
popsum <- gap[year == 2007 & continent == cc, sum(pop)]
popsum
})
names(results) <- gap[,unique(continent)]
results
Asia Europe Africa Americas Oceania
3811953827 586098529 929539692 898871184 24549947
apply
apply
함수는 행렬 데이터에 유용하다: 행렬 행 혹은 열 방향으로 루프를 돌릴 수 있게 해 준다.
# create some dummy data
r <- matrix(rnorm(10*4), nrow=10)
colnames(r) <- letters[1:4]
rownames(r) <- LETTERS[1:10]
# 각 행에 대한 최대값을 구한다:
apply(r, 1, max)
A B C D E F
1.7186295 1.5747280 0.9143232 0.8146741 2.0507913 0.4780862
G H I J
-0.1877886 0.2429086 0.2726646 0.5065971
# 각 칼럼에 대한 최대값을 구한다:
apply(r, 2, max)
a b c d
2.050791 1.624339 1.718630 1.894572
mapply
mapply
함수를 사용해서 다른 인수 조합을 갖는 함수를 실행할 수 있다. 예제를 살펴보자:
a <- 1:4
b <- 4:1
mapply(rep, a, b)
[[1]]
[1] 1 1 1 1
[[2]]
[1] 2 2 2
[[3]]
[1] 3 3
[[4]]
[1] 4
상기 코드를 실행하면 다음과 결과가 같다:
rep(a[1], b[1])
[1] 1 1 1 1
rep(a[2], b[2])
[1] 2 2 2
rep(a[3], b[3])
[1] 3 3
rep(a[4], b[4])
[1] 4
혹은, 다음 lapply
문장과 동일하다:
lapply(1:4, function(ii) {
rep(a[ii], b[ii])
})
[[1]]
[1] 1 1 1 1
[[2]]
[1] 2 2 2
[[3]]
[1] 3 3
[[4]]
[1] 4
tapply
tapply
함수는 벡터 내부 서로 다른 그룹집단에 함수를 실행할 수 있게 해준다. 이번 학습 첫번째 예제로 되돌아 가서, tapply
함수를 사용해서 2007년 각 대륙별로 총인구를 계산할 수 있다:
gap2007 <- gap[year == 2007] # first filter by the year
tapply(
gap2007[,pop], # The column of population counts from the data frame
gap2007[,continent], # The column of continent labels for each entry
sum # The function to apply to the population vector within each continent
)
Africa Americas Asia Europe Oceania
929539692 898871184 3811953827 586098529 24549947