이번 장은 apply 함수에 모두 할애한다. 이 함수 유형에는 apply, lapply, sapply, tapply, mapply 등이 있다. 또 이들의 사촌뻘인 by와 split가 있다.
r은 이들을 사용해서 데이터처리를 일괄처리함으로써 계산을 감소해 빠르다는 장점이 있다.
요인을 통해 집단 정의하기
요인의 수준은 각 벡터 원소가 속한 집단을 판별한다. 통계에서는 집단 간 평균 비교, 집단의 비율 비교, 분산분석 수행 등 집단으로 나누어 처리해야 하는 경우가 흔히 있기 때문에 쓸모가 많은 기능이다.
벡터를 여러 집단으로 분할하기
벡터가 하나 있다고 하자. 각 벡터의 원소들은 각각 다른 집단에 속해 있다. 집단은 분류 요인에 속한다. 집단을 기준으로 원소들을 분할하려고 한다. —> split 함수 사용 : 리스트 형태로 결과가 반환된다. 각 요인들이 리스트 원소를 나누는 기준이 되고 원소들은 x의 형태에 맞춰 반환된다.
groups <- split(x, f) # x는 벡터고, f는 요인이다.
R
복사
다른 방법으로는 unstack 함수도 있다. unstack함수는 중복된 값이 있을 때 사용하면 유리하다. 쌓여져 있는 칼럼을 서로 다른 칼럼으로 분리해 준다.
groups <- unstack(data.frame(x,f)) # unstack을 쓰려면 데이터프레임으로 변환해줘야 한다.
R
복사
기본적으로 두 함수 모두 벡터로 된 리스트를 반환하는데 unstack함수는 벡터들이 모두 동일한 길이일 경우에 리스트가 아닌 데이터프레임으로 바꿔준다.
# Cars93 데이터셋의 Origin이라는 요인을 기준으로 MPG를 분할해보자.
library(MASS)
split(Cars93$MPG.city, Cars93$Origin)
$USA
[1] 22 19 16 19 16 16 25 25 19 21 18 15 17 17 20 23 20 29 23 22 17 21 18 29 20 31 23 22 22 24 15
[32] 21 18 17 18 23 19 24 23 18 19 23 31 23 19 19 19 28
$`non-USA`
[1] 25 18 20 19 22 46 30 24 42 24 29 22 26 20 17 18 18 29 28 26 18 17 20 19 29 18 29 24 17 21 20
[32] 33 25 23 39 32 25 22 18 25 17 21 18 21 20
R
복사
데이터프레임데이터셋이었지만 split함수를 이용하면서 벡터로 된 리스트를 반환했다.
위의 집단을 이용해서 MPG의 중앙값을 계산해본다.
> g<-split(Cars93$MPG.city, Cars93$Origin)
> median(g[[1]])
[1] 20
> median(g[[2]])
[1] 22
R
복사
리스트의 각 원소에 함수 적용하기
원하는 결과의 양식에 따라 lapply 또는 sapply함수 중 하나를 사용한다. lapply는 결과를 항상 리스트로 반환하는데에 반해 sapply는 가능하면 결과를 벡터로 반환한다.
lst <- lapply(lst, fun)
vec <- sapply(lst, fun)
R
복사
리스트에서 함수(fun)는 하나의 인자, 즉 리스트에 있는 원소 하나만을 인자로 받아야 한다.
sapply에서 ‘s’는 단순화를 의미한다. 즉, 결과를 벡터 또는 행렬로 단순화하려고 한다. 반환된 모든 값이 동일한 길이여야 한다. 길이가 1이면 벡터가 되고, 그렇지 않으면 행렬이 될 것이다. 만약, 길이가 동일하지 않으면 단순화할 수 없어서 리스트로 반환된다.
sapply에서 함수의 결과가 길이가 1이상인 예를 들어보면
sapply(scores, range) # range() 함수가 있다. 반환값이 1개 이상이다. 따라서 결과는 행렬이다.
R
복사
만약, 사용할 함수가 리스트와 같은 구조화된 객체를 반환하면 sapply보다는 lapply를 사용해야 한다. 구조화된 객체는 벡터로 들어갈 수 없기 때문이다.
예를들어, t-test 함수는 리스트를 반환한다. 따라서 꼭 lapply를 써야 한다.
tests <- lapply(scores,t.test)
R
복사
sapply를 사용해 t.test 결과에서 신뢰 구간의 범위와 같은 원소들을 추출할 수 있다.
sapply(tests, function(t) t$conf.int)
R
복사
모든 행에 함수 적용하기
행렬이 있다. 모든 행에 함수를 적용해서 각 행의 함수 결과를 계산하고 싶다고 하자.
apply함수를 적용한다. 두번째 인자에 1을 입력하면 행계산이고 2를 입력하면 열계산이 된다. 결과는 결과값들을 모은 벡터가 된다.
results <- apply(mat,1,fun) # mat은 행렬, fun은 적용할 함수다.
R
복사
# range함수는 반환값이 최솟값, 최댓값 이렇게 2개이므로 apply에 적용하면 결과는 행렬이 된다.
R
복사
apply함수는 데이터프레임에도 응용할 수 있는데 단, 데이터들이 동질적이어야 한다.(모두 숫자형이거나 문자형이거나)
모든 열에 함수 적용하기
행렬 또는 데이터프레임이 있고 모든 열에 함수를 적용하고 싶다고 하자.
행렬에는 행과 마찬가지 방식으로 apply를 사용한다. 이때 두번째 인자는 2다.
results <- apply(mat,2,fun)
R
복사
데이터프레임에는 lapply 또는 sapply를 사용한다.
lst <- lapply(dfrm, fun) #fun은 데이터프레임의 열을 인자로 받는다.
vec <- sapply(dfrm, fun)
R
복사
데이터프레임에도 apply를 사용할 수 있지만, 데이터프레임이 동질적일 경우에만 해당된다.
열들의 클래스를 확인할 때는 다음과 같이 한다
sapply(데이터프레임,class)
R
복사
sapply를 활용하는 방식 중에서 예측변수와 반응변수간의 상관관계를 구하는 대표적인 예시가 있다.
cors <-sapply(pred,cor,y=resp)
R
복사
상관계수의 절댓값이 중요하기 때문에 rank함수를 사용해서 가장 큰 절댓값을 가진 상관계수의 위치를 찾아낸다.
mask <- (rank(abs(cors) <= 10) # 논리값으로 된 벡터 반환
best.pred <- pred[,mask]
R
복사
이제, 반응변수를 best.pred에 대해 회귀분석하면 될 것이다.
lm(resp ~ best.pred)
R
복사
데이터 집단에 함수 적용하기
데이터를 집단에 따라 처리하고 싶다고 하자.
먼저, 집단 분류 요인을 생성한다. 그다음에는 tapply를 사용하여 각 데이터 집단에 함수를 적용한다.
tapply(x, f, fun) # x는 벡터, f는 분류 요인, fun은 적용할 함수
R
복사
함수는 인자를 하나만 받아야 하고 그 인자는 x에서 가져온 원소들의 벡터다.
suburbs라는 데이터 프레임은 시카고 대도시 지역에 있는 16개의 가장 큰 도시들의 인구를 벡터로 가지고 있다. 이 데이터셋을 이용해보자.
attach(suburbs)
pop # 인구 변수 ( 총 16개 )
R
복사
모든 도시들의 합계와 평균을 계산하자
sum(pop) # 합계
mean(pop) # 평균
R
복사
만약 위의 결과를 county별로 나누고 싶다고 하면 새로운 요인이 필요할 것이다. 이 요인은 pop과 동일한 길이일 것이며 요인의 수준들은 각각의 카운티를 나타낸다.
tapply함수는 세 개의 주요 인자가 있는데, 데이터로 된 벡터, 집단정의하는 요인, 그리고 함수다. tapply는 결과를 벡터로 반환해준다.
tapply(pop, county, sum) # county별로 인구의 합을 반환한다.
tapply(pop, county, mean) # county별로 평균 인구를 반환한다.
R
복사
각 카운티에 있는 도시들의 개수를 세려면 length 함수가 필요하다
tapply(pop, county, length)
R
복사
행 집단에 함수 적용하기
데이터 프레임 내의 행들로 된 집단에 함수를 적용하고 싶다고 하자.
먼저, 집단 분류 요인을 정의한다. 데이터 프레임의 행마다 요인의 수준 하나를 주는 것이다.
by함수는 행들을 임시 데이터 프레임으로 만든 다음에 그것을 인자로 해서 함수를 호출한다.
결과는 리스트로 반환해준다.
by(dfrm, fact, fun) # dfrm은 데이터프레임, fact는 집단 분류 요인
R
복사
by함수는 사용하는 함수가 데이터 프레임을 특별한 방식으로 다루는 경우에 유용하다. 예를들면, print, summary, mean 같은 함수 말이다.
# trials라는 데이터프레임에 성별이라는 요인이 있다고 하면
by(trials, trials$sex, summary) # 성별에 따라 데이터를 분할하고 두 집단에 대해 summary를 호출한다.
R
복사
또한, 각각의 나눠진 집단에 대해서 선형모형(함수)를 만들 수도 있다.
models <- by(trials, trials$sex, function(df) lm(post ~ pre+dose1+dose2, data=df))
R
복사
결과는 두 개의 선형모형 결과가 원소로 있는 리스트가 된다.
모형이 원소인 리스트가 있으니까 이번에는 confint 함수를 원소에 적용해서 각 모형의 계수들에 대한 신뢰구간을 구해볼 수도 있다.
lapply(models,confint)
R
복사
신뢰구간에서 0을 포함하면 유의하지 않다고 판단한다. 절편이 집단에 따라 크게 차이가 난다면 집단별로 다른 반응을 보인다고 판단할 수 있다.
병렬 벡터들 또는 리스트들에 함수 적용하기
어떤 함수가 있다고 하자. 이 함수는 단일값에는 작동하지만 벡터에는 작동하지 않는다. 하지만 벡터로 된 결과를 얻고 싶다고 하자.
이럴때 사용하는 함수가 mapply 함수다. mapply 함수는 결과를 벡터로 반환하지 않는 함수에도 벡터화를 시켜 반환시켜 준다.
mapply(f, vec1, vec2, ...)
R
복사
f가 받는 각 인자로 벡터가 하나씩 있어야 한다. 만약, 벡터 인자들의 길이가 서로 동일하지 않다면 재활용 규칙이 적용된다.
mapply 함수는 인자로 리스트도 잘 작동한다.
mapply(f, list1, list2,...)
R
복사
인자를 두 개 받는 gcd 함수를 만들어 보자. (최대공약수를 구하는 함수)
gcd <- function(a,b){
if (b==0) return (a)
else return(gcd(b, a %% b))
}
R
복사
gcd를 두 개의 벡터에 적용하면 틀린 답과 함께 에러 메세지 결과가 잔뜩 출력된다.
gcd(c(1,2,3), c(9,6,3))
R
복사
이 함수는 벡터화되지 않았지만 mapply를 사용하면 벡터화할 수 있다.
mapply를 이용해서 gcd를 하면
mapply(gcd, c(1,2,3), c(9,6,3))
R
복사
두 개의 벡터 사이에서 원소별로 gcd를 구할 수 있다.