본문 바로가기

R/R을 이용한 데이터 처리&분석 실무

apply 계열 함수 - 2

반응형

sapply()

sapply()는 lapply()와 유사하지만 리스트 대신 행렬, 벡터 등의 데이터 타입으로 결과를 반환하는 특징이 있는 함수다.

 

-sapply: 벡터, 리스트, 표현식, 데이터 프레임 등에 함수를 적용하고 그 결과를 벡터 또는 행렬로 반환한다.

sapply(    

   x,                       #벡터, 리스트, 표현식 또는 데이터 프레임

   FUN,                   #적용할 함수

   ...,                      #추가 인자. 이 인자들은 FUN에 전달된다.

)

 

예를 들어, 아이리스의 컬럼별로 평균을 구하는 경우를 살펴보자. 다음 코드에서 볼 수 있듯이 lapply()는 결과를 리스트로 반환하지만, sapply()는 벡터를 반환한다.

>lapply(iris[,1:4], mean)
$Sepal.Length
[1] 5.84333

$Sepal.Width
[1] 3.057333

$Petal.Length
[1] 3.758

$Petal.Width
[1]1.199333

>sapply(iris[,1:4], mean)
   Sepal.Length    Sepal.Width    Petal.Length    Petal.Width
        5.843333         3.057333       3.758000        1.199333
>class(sapply(iris[,1:4], mean))           #"numeric"은 숫자를 저장한 벡터를 의미함.
[1] "numeric"

 

sapply()에서 반환한 벡터는 as.data.frame()을 사용해 데이터 프레임으로 변환할 수 있다. 이때 t(x)를 사용해 벡터의 행과 열을 바꿔주지 않으면 기대한 것과 다른 모양의 데이터을 얻게 된다. 다음은 아이리스의 컬럼별 평균을 sapply()를 사용해 벡터로 구한 뒤 이를 다시 데이터 프레임으로 변환하는 예다.

>x<-sapply(iris[,1:4], mean)
>as.data.frame(x)
                             x
Sepal.Length     5.843333
Sepal.Width      3.057333
Petal.Length      3.758000
Petal.Width       1.199333

>as.data.frame(t(x))
      Sepal.Length    Sepal.Width    Petal.Length    Petal.Width
1       5.843333       3.057333        3.758000         1.199333

 

다수의 컬럼을 포함하는 데이터 프레임을 처리하다 보면 종종 각 컬럼의 데이터 타입을 알아내고 싶을 때가 있다. 예를 들면, 어떤 컬럼에 숫자가 저장되어 있는지를 판단하는 경우다. 이때 sapply()를 유용하게 사용할 수 있다. 다음은 아이리스 데이터에서 각 컬럼의 데이터 타입을 구하는 예다.

> sapply(iris,class)
       Sepal.Length    Sepal.Width    Petal.Length    Petal.Width
         "numeric"        "numeric"      "numeric"      "numeric"

 

지금까지 설명한 sapply()의 사용 예에서 FUN은 1개 값을 반환했다. 예를 들어, mean 함수의 반환 값은 1개 값이다. 이 경우 sapply()의 결과는 이들 값을 모은 벡터가 된다. 하지만 sapply()에 인자로 주어진 함수의 출력이 길이가 1보다 큰 벡터들이라면 sapply()는 행렬을 반환한다. 다음 예에서는 아이리스의 숫자형 값들에 대해 각 값이 3보다 큰지 여부를 판단한다. sapply()에 넘긴 FUN의 반환 값이 길이가 1보다 큰 벡터기 때문에 sapply()의 수행 결과는 행렬이다.

>y<-sapply(iris[,1:4], function(x) {x>3})
>class(y)
[1] "matrix"
>head(y)
           Sepal.Length  Sepal.Width   Petal.Length   Petal.Width
[1,]          TRUE           TRUE            FALSE           FALSE
[2,]          TRUE           FALSE           FALSE           FALSE
[3,]          TRUE           TRUE            FALSE           FALSE
...

 

sapply()는 한 가지 타입만 저장 가능한 데이터 타입인 벡터 또는 행렬을 반환하므로 sapply()에 인자로 준 함수 FUN의 반환 값에 여러 가지 데이터 타입이 섞여 있으면 안 된다. 만약 각 컬럼에 대해 수행한 함수의 결과 데이터 타입이 서로 다르다면, 리스트를 반환하는 lapply()나 리스트 또는 데이터 프레임을 반환할 수 있는 plyr 패키지를 사용해야 한다.

 

tapply()

tapply()는 그룹별로 함수를 적용하기 위한 apply 계열 함수다.

 

-tapply : 벡터 등에 저장된 데이터를 주어진 기준에 따라 그룹으로 묶은 뒤 각 그룹에 함수를 적용하고 그 결과를 반환한다.

tapply(
   x,                 #벡터

   INDEX,          #데이터를 그룹으로 묶을 색인. 팩터를 지정해야 하며 팩터가 아닌 타입이 지정되면
                      #패팩터로 형 변환된다.
   FUN,             #각 그룹마다 적용할 함수
   ...,                 #추가 인자. 이 인자들은 FUN에 전달된.

)

 

다음과 같은 예를 생각해보자. 1부터 10까지의 숫자가 있고 이들이 모두 한 그룹에 속해 있을 때, 이 그룹에 속한 데이터의 합을 구하면 55가 될 것이다.

>tapply(1:10, rep(1,10), sum)
  1
55

 

위 코드에서 rep(1,10)은 1을 10회 반복하는 것을 의미한다. 따라서 숫자 1,2,3,... 10에 대해 동일한 소속 번호 1,1,1,... 1을 부여한 것이다. 그러므로 그룹 1에 속한 데이터의 합은 55(=1+2+3+...+10)다.

 

이번에는 1부터 10까지의 숫자를 홀수별, 짝수별로 묶어서 합을 구해보자. INDEX에 홀수와 짝수별로 다른 팩터 값이 주어지도록 %% 2 를 사용했다.

>tapply(1:10, 1:10 %% 2 == 1, sum)
FALSE TRUE
    30     25

 

수행 결과 짝수의 합이 30(=2+4+6+8+10), 홀수의 합이 25(=1+3+6+7+9)로 구해졌다. 아이리스 데이터에서 Species별 Sepal.Length의 평균을 구해보자.

>tapply(iris$Sepal.Length, iris$Species, mean)
     setosa   versicolor   virginica
     5.006     5.936         6.588

 

이번에는 조금 더 복잡한 그룹화를 다뤄보자. 계절별 성별로 정리된 판매량 데이터가 다음과 같이 주어졌다고 하자.

>m<-matrix(1:8,
+               ncol=2,
+               dimnames=list(c("spring","summer","fall","winter"),
+                                   c("male", "female")))
>m
            male   female
spring       1       5
summer     2       6
fall           3       7
winter       4       8

 

행렬의 행은 봄, 여름, 가을, 겨울을 뜻하고 열은 성별을 의미한다. 이때 반기별, 성별 셀의 합을 구해보자. 즉, 상반기(봄, 여름)의 남성 셀 합(=1+2)과 여성 셀 합(=5+6), 하반기(가을, 겨울)의 남성 셀 합(=3+4)과 여성 셀 합(=7+8)을 구하는 것이 목표다.

 

이 연산을 수행하기 위한 성별, 분기별 그룹은 다음과 같이 구성할 수 있다. 그림에서 (n,m) 값은 tapply()에 INDEX로 주어질 값들을 의미한다.

 

  male female
spring (1,1)


1
(1,2)


5
summer (1,1)


2
(1,2)


6
fall (2,1)


3
(2,2)


7
winter (2,1)


4
(2,2)


8

 

INDEX를 실제로 지정할 때는 (n,m)에서 n을 먼저 나열한 뒤 m 값을 나열한다. 즉, 그룹 (n1,m1),(n2,m2)는 list(c(n1,n2), c(m1,m2))로 표현한다. 다음은 이러한 방식으로 tapply()를 사용한 분기별, 성별 합을 구한 예다.

>tapply(m,list(c(1,1,2,2,1,1,2,2),
+                c(1,1,1,1,2,2,2,2)), sum)
   1  2
1 3  11
2 7  15

 

tapply()는 클러스터링 알고리즘을 수행한 후 같은 클러스터에 속한 데이터들의 x 좌표의 평균, y 좌표의 평균을 계산하는 데 사용할 수 있는데, 바로 이때 위와 같은 방식으로 색인을 부여한다. 따라서 조금 복잡해 보여도 여기서 배운 내용을 꼭 알고 넘어가도록 하자.

 

mapply()

이제 apply() 계열 함수의 마지막 변형으로 mapply()에 대해 살펴보자. mapply()는 sapply()와 유사하지만 다수의 인자를 함수에 넘긴다는 점에서 차이가 있다. 주요 사용 목적은 다수의 인자를 받는 함수 FUN()이 있고 FUN()에 넘겨줄 인자들이 데이터로 저장되어 있을 때, 데이터에 저장된 값들을 인자로 하여 함수를 호출하는 것이다. 다음에 mapply()에 대해 보였다.

 

-mapply : 함수에 리스트 또는 벡터로 주어진 인자를 적용한 결과를 반환한다.

mapply(

   FUN,   # 실행할 함수

   ...,      # 적용할 인자

)

 

여기에서는 정규 분포를 따르는 난수를 생성하는 rnorm() 함수에 mapply()를 적용하는 예를 살펴볼 것이다. R에는 다양한 난수 생성 함수가 있다. 이 함수들은 'r{분포명}()' 형태를 띤다.

 

함수 의미
rnorm(n,mean=0, sd=1) 평균이 n, 표준 편차가 sd인 정규 분포를 따르는 난수 n개 발생
runinf(n,min=0, max=1) 최솟값이 min, 최댓값이 max인 균등 분포를 따르는 난수 n개 발생
rpois(n,lambda) 람다 값이 lambda인 포아송 분포를 따르는 난수 n개 발생
rexp(n,rate=1) 람다가 rate인 지수 분포를 따르는 난수 n개 발생

 

mapply()를 사용해 rnorm()을 홍출하는 방법에 대해서 알아보자. rnorm()은 n, mean, sd 세 인자를 받는 함수다. 예를 들어, 다음은 평균 0, 표준 편차 1을 따르는 난수 10개를 발생시킨다.

>rnorm(10, 0, 1)
[1] -0.1537857 0.9516890 -0.6393049 0.0864833 0.173538 -2.5103284
[7] 1.6078990 0.3964567 1.3365991 0.4620919

 

rnorm()을 다음 세 가지 조합에 대해 호출할 필요가 있다고 해보자.

n mean sd
1 0 1
2 10 1
3 100 1

 

이를 수행하기 위해 rnorm()을 세 번 호출해도 되지만 또 다른 방법은 mapply()를 활용하는 것이다. mapply()는 인자로 주어진 데이터들의 첫 번째 요소들을 묶어서 FUN을 호출하고, 두 번째 인자들을 묶어 FUN을 호출하는 식으로 동작한다. 따라서 다음과 같이 mapply()를 사용할 수 있다.

 

>mapply(rnorm,
+          c(1,2,3),           #n
+          c(0,10,100),      #mean
+          c(1,1,1))          #sd

[[1]]
[1] -0.1700506

[[2]]
[1] 8.321710  7.564312

[[3]]
[1] 99.45474  100.10412  100.800828

 

위 실행 결과에서 [[1]]은 rnorm(n=1, mean=0, sd=1), [[2]]는 rnorm(n=2, mean=10, sd=1), [[3]]은 rnorm(n=3, mean=100, sd=1)에 해당한다.

 

또 다른 예로 아이리스의 각 컬럼의 평균을 구하는 경우를 살펴보자.

>mapply(mean, iris[, 1:4])
Sepal.Length  Sepal.Width  Petal.Length  Peta.Width
    5.84333      3.057333       3.758000      1.199333

 

mapply()의 인자로 iris[, 1:4]가 주어졌다. 따라서 mapply()에는 iris의 모든 행이 나열되어 인자로 주어졌다고 볼 수 있다. mapply()가 주어진 인자들을 하나씩 묶어 mean()을 호출해주므로 각 행의 첫 번째 컬럼끼리 묶어 평균을 구하고, 다시 두 번째 컬럼끼리 묶어 평균을 구하는 작업을 반복하게 된다. 그 결과 모든 컬럼의 평균이 계산된다.

 

R을 이용한 데이터 처리&분석 실무 中

반응형

'R > R을 이용한 데이터 처리&분석 실무' 카테고리의 다른 글

데이터 정렬  (0) 2020.02.03
데이터 분리 및 병합  (0) 2020.02.03
apply 계열 함수 - 1  (0) 2020.02.01
데이터 프레임의 행과 컬럼 합치기  (0) 2020.01.31
파일 입출력  (0) 2020.01.31