R에서는 주로 벡터 기반으로 데이터를 처리 한다.

벡터기반 처리는 개별 요소를 for 루프 등으로 하나씩 처리하는 방식보다 빠르게 수행될 뿐만 아니라 손쉽게 병렬화가 가능하다.

따라서 대부분의 Machine Learning이나 통계 함수들은 입력값으로 벡터를 받는다.


따라서 데이터를 벡터로 변환하고 다시 벡터데이터를 가독성 높은 데이터 프레임으로 변경하는 것을 자유 자재로 할 수 있어야한다.

앞으로 보게될 많은 R 코드들은 모두 이러한 벡터 연산들을 이용하기 때문에 벡터를 다루는 함수들을 잘 알고 있어야 한다.



Apply 함수 (데이터 조작)


벡터화된 연산과 apply함수를 이용해서 일괄 실행함으로써 계산을 간소화하고 고속화를 도모 한다.

다양한 데이터를 임의의 함수에 적용하 결과를 얻는 방법으로 apply  함수들을 이용하는 방법이 있다.

이들 함수는 벡터, 행렬, 리스트, 데이터 프레임에 적용할 함수를 명시하는 형태로서, 함수형 언어 스타일에 가깝다고 볼 수 있다.



apply 함수들의 요약


apply() 

배열 또는 행렬에 주어진 함수를 적용한 뒤 그 결과를 벡터, 배열 또는 리스트로 반환

배열 또는 행렬에 적용 가능


lapply()

벡터, 리스트 또는 표현식에 함수를 적용하여 그 결과를 리스트로 반환

결과가 리스트


sapply() 

lappy()와 유사하지만 결과를 벡터, 행렬 또는 배열로 반환

결과가 벡터, 행렬 또는 배열


tapply()

벡터에 있는 데이터를 특정 기준에 따라 그룹으로 묶은 뒤 각 그룹맏 주어진 함수를 적용하고 그 결과를 반환

여러 데이터를 함수의 인자로 적용


mapply() 

sapply의 확장된 버전으로, 여러 개의 벡터 또는 리스트를 인자로 받아 함수에 각 데이터의 첫째 요소들을 적용한 결과,

둘째 요소들을 적용한 결과, 셋째 요소들을 적용한 결과 등을 반환

여러 데이터를 함수의 인자로 적용 가능



▣ Apply 사용법


apply: 배열 또는 행렬에 함수 f를 d방향으로 적용하여 결과를 벡터, 배열 또는 리스트로 반환 한다.

apply( X, d, f)

X #배열 또는 행렬

d # 함수를 적용하는 방향, 1은 행방향, 2는 열 방향

  # c(1,2)는 행과 열 방향 모두를 의미

FUN # 적용할 함수


예제

#합 구하는 함수
> sum(1:10)
[1] 55
# 메트릭스 생성
> d <- matrix(1:9, ncol=3)
> d
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
# 각각의 행을 더해서 벡터로 반환 한다.
> apply(d,1,sum)
[1] 12 15 18
# 각각의 열을 더해서 벡터로 반환 하다.
> apply(d,2,sum) 
[1]  6 15 24


> head(iris[c(1,2,3,4)])
  Sepal.Length Sepal.Width Petal.Length Petal.Width
1          5.1         3.5          1.4         0.2
2          4.9         3.0          1.4         0.2
3          4.7         3.2          1.3         0.2
4          4.6         3.1          1.5         0.2
5          5.0         3.6          1.4         0.2
6          5.4         3.9          1.7         0.4
# 벡터 스타일의 선택
> apply(iris[c(1,2,3,4)],2,sum)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
       876.5        458.6        563.7        179.9 
# 메트릭스 스타일의 선택
> apply(iris[,1:4],2,sum)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
       876.5        458.6        563.7        179.9




▣ lapply 사용법


리스트로 반환하는 특징이 있는 apply 계열 함수이다.


lapply (X, FUN, ...#추가인자) 

반환 값은 X와 같은 길이의 리스트이다.

X는 벡터, 리스트, 표현식, 데이터 프레임을 받을 수 있다.


리스트를 다른 타입으로 변환 하는 방법 

unlist( x, recursive=FALSE, use.name=TRUE )

반환 값은 벡터이다.


do.call # 함수를 리스트로 주어진 인자에 적용하여 결과를 반환한다.

(적용할 함수, 데이터 리스트)

반환 값은 함수 호출 결과다.



# 벡터 입력을 받아서 리스트로 반환하는 코드이다.
> result <- lapply(1:3, function(x){x*2})
> result
[[1]]
[1] 2

[[2]]
[1] 4

[[3]]
[1] 6

> result[[1]]
> unlist(result) # list 구조를  벡터로 변경 한다.
[1] 2 4 6


입력을 list로 받을 수도 있다.

> x <- list(a=1:3, b=4:6)
> x
$a
[1] 1 2 3

$b
[1] 4 5 6

> lapply(x,mean)
$a
[1] 2

$b
[1] 5


# iris의 데이터 구조를 보여줌
> head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

# list로 반환됨 각각의 열들의 평균을 구한다음
> lapply(iris[,1:4],mean)
$Sepal.Length
[1] 5.843333

$Sepal.Width
[1] 3.057333

$Petal.Length
[1] 3.758

$Petal.Width
[1] 1.199333

#각 열에 대한 평균을 구하게 된다.
> colMeans(iris[,1:4])
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333


데이터 프레임을 처리한 결과를 리스트로 얻은 뒤 해당 리스트를 다시 데이터 프레임으로 변활할 필요가 있다. 

이 변환은 몇 단계를 거쳐서 처리해야 한다.

  • unlist()를 통해 리스트를 벡터로 변환한다.
  • matrix()를 사용해 벡터를 행렬로 변환한다.
  • as.data.frame()을 사용해 행렬을 데이터 프레임으로 변환한다.
  • names()를 사용해 리스트로부터 변수명을 얻어와 데이터 프레임의 각 컬럼에 이름을 부여한다.


iris 데이터의 각 열의 평균을 구한다음

이 결과는 list로 반환 되므로 이것을 다시 데이터 프레임으로 변경해서 출력하는 예제이다.

># 데이터 프레임으로 변겨하는 코드이다.
# matrix()로 먼저 변환 하는 것은 vector를 바로 data frame으로 변경하면 단일 열로 밖에 변경을 못하기 때문이다.
 d <- as.data.frame(matrix(unlist(lapply(iris[,1:4],mean)),ncol=4,byrow=TRUE))
> d
        V1       V2    V3       V4
1 5.843333 3.057333 3.758 1.199333
> d <- as.data.frame(matrix(unlist(lapply(iris[,1:4],mean)),ncol=4,byrow=TRUE))
> d
        V1       V2    V3       V4
1 5.843333 3.057333 3.758 1.199333
#위와 같이 이름이 자동으로 설정되므로 가독성을 위해서 이름을 변경해 준다.
> names(d) <- names(iris[,1:4])
> d
  Sepal.Length Sepal.Width Petal.Length Petal.Width
1     5.843333    3.057333        3.758    1.199333



unlist 방식의 경우 벡터로 바꾸기 때문에 데이터 타입이 서로 다를 경우 엉뚱한 값으로 바꿔버리게 된다.

> x <- list(data.frame(name="foo",value=1),data.frame(name="bar",value=2))
> x
[[1]]
  name value
1  foo     1

[[2]]
  name value
1  bar     2

> unlist(x)
 name value  name value 
    1     1     1     2 

이렇게 서로 다른 데이터로 구성된 list를 데이터 프레임으로 변경 해야한다면,

do.call() 함수를 이용 해야 한다.

> do.call(rbind,x)
  name value
1  foo     1
2  bar     2

하지만, 해당 방식은 속도면에서 문제가 발생 한다.

그래서 rbindlist()를 사용하게 된다.




▣ sapply 사용법


lapply의 사용자 친화 버전이다. 해당 함수는 리스트 대신에 
행렬과 벡터 등의 데이터 타입으로 결과를 반환하는 특징이 있는 함수 이다.

벡터 리스트 표현식 데이터 프레임등에 함수를 적용하고 그 결과를 벡터 또는 행렬로 반환한다.
sapply {
X, # 벡터, 리스트, 표현식 또는 데이터 프레임
FUN, # 적용할 함수
....., # 추가 인자, 이 인자들은 FUN에 전달된다.
}

> 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))
[1] "numeric"


sapply로 연산하고 벡터를 data frame으로 변경하는 방법이다.

> 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))
[1] "numeric"
> 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.758    1.199333


sapply를 이용해서 각 컬럼의 데이터 타입을 구할 수도 있다.

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

다음은 조건문을 이용해서 iris 데이터 집합중에서 3보다 큰지 여부를 판단하는 코드이다.

반환값은 행렬이 된다.

> 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
[4,]         TRUE        TRUE        FALSE       FALSE
[5,]         TRUE        TRUE        FALSE       FALSE
[6,]         TRUE        TRUE        FALSE       FALSE

주의해야할 사항은 벡터를 반환 하기 떄문에 sapply를 사용할 때는 데이터 타입을 혼합하여 처리하면 안된다.

데이터 타입을 혼합해야 할 경우에는 lapply를 사용하거나 plyr 페키지에 있는 ddply를 사용해야 한다.


▣ tapply() 사용법


tapply{

X 벡터

Index 데이터를 그룹으로 묶을 색인, 팩터를 지정해야 하며 팩터가 아닌 타입이 지정되면 팩터로 형 변환된다.

FUN 각 그룹마다 적용 할 함수

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

}

반환 값은 배열이다.


아래의 코드는 간단한 하나의 그룹 1에 대한 합을 나타낸다.

# rep는 1을 10번 반복하라는 뜻이다. 이렇게해서 1:10 까지의 벡터에 factor 데이터 1을 모두 맵핑해서 하나의 그룹으로 만들어 준다.
> rep(1,10)
 [1] 1 1 1 1 1 1 1 1 1 1
> tapply(1:10, rep(1,10),sum)
 1 
55


# 홀수 짝수를 나누어서 연산하는 것이다.
tapply(1:10,1:10%%2 ==1,sum)
FALSE  TRUE 
   30    25 


조금더 복잡한 연산으로 iris 데이터들중에서 length의 평균을 구하는데 그것을 specie에 따라서 구하는 것이다.

> head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
> tapply(iris$Sepal.Length, iris$Species, mean)
    setosa versicolor  virginica 
     5.006      5.936      6.588 


이제 더 복잡한 예제를 살펴보자. 아래의 예제는 추후에 클러스터링 알고리즘을 적용한 다음에 (k-mean clustering)

같은 cluster에 속한 데이터들의 x와 y 좌표의 각가그이 평균을 구하는 용도로 사용 될 수 있다.

#데이터를 아래와 같이 생성 한다.
> 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이다.


Index를 설정하기 위해서 (n,m)에서 n을 먼저 나열한 뒤 m 값을 나열한다. 

즉, 그룹 (n1, m1), (n2, m2)는 list( c(n1,n2), c(m1,m2))로 표현된다.

> 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


▣ mapply() 사용법


sapply와 유사하지만 다수의 인자를 함수에 넘긴다는 점에서 차이가 존재 한다.

sapply 처럼 행렬과 벡터반환 하게 된다.


1) 여러개의 인자를 보내야 하는 경우에 사용 한다.

2) 어떤 함수에 여러개의 벡터를 입력으로 주고 싶은 경우

a,b를 받아서 처리하는 함수가 있을때

c(....), c(....) 이런식으로 2개의 벡터를 입력으로 보내서 연속적인 처리를 할 수 있다.



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

Fun #실행할 함수

... # 적용할 인자



rnorm의 경우 n개의 데이터를 mean에 대해서 sd인 정규 분포를 생성 한다.


그냥 rnorm을 세번 호출하면 되지만 편의를 위해서 mapply를 이용했다.

세개인자를 rnorm함수로 전달해서 연살을 수행하게 된다.

> mapply(rnorm, c(1,2,3),c(0,10,100),c(1,1,1))
[[1]]
[1] 0.5746612

[[2]]
[1] 10.026820  9.574186

[[3]]
[1]  99.53101  98.92185 101.26264

#iris 데이터의 경우 아래와 같이 수행 된다.
> mapply(mean,iris[,1:4])
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333 

> sapply(iris[,1:4],mean)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333 

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

$Sepal.Width
[1] 3.057333

$Petal.Length
[1] 3.758

$Petal.Width
[1] 1.199333




+ Recent posts