이전 포스팅들 중에 tapply(), sapply()에 대해서 다른 함수를 설명하는 와중에 은근슬쩍 짧게 소개를 한적이 있습니다.

 

그런데 그때는 다른 함수를 설명하는 것이 주된 목적이다보니 tapply()만 따로 한두줄 소개하고 말고, 또는 sapply()만 따로 한두줄 설명하고 마는 식이었습니다. 

 

이번에는 복습도 할겸, 또 apply() 삼총사인 tapply(), sapply(), lapply() 가 각 각 뭐가 다르고, 무슨 특징이 있고, 어떤 때 쓰는 것인지에 대해서 비교해가면서 중점적으로 살펴보도록 하겠습니다.

 

 간략히 요약해서 비교하자면 아래와 같습니다.

 

함수 

 사용 목적

사용 형태

    결과 

 tapply()

요인(factor)의 수준(level)별로

특정 벡터에 함수 명령어를

동시에 적용

 tapply(벡터, 요인, 함수)

 벡터 또는 행렬

 sapply()

데이터 프레임 여러 변수에 함수

명령어 동시에 적용

 sapply(데이터 프레임, 함수)

 lapply(데이터 프레임, 함수)

 벡터 또는 행렬

 lapply()

 리스트

 

tapply()가 다른 두 함수와 다른 점은 tapply()는 요인(factor) 변수를 기준으로 해서 그룹별로 나누어서 통계 분석을 하고자 할 때 유용하게 쓸 수 있는 함수입니다.  아래 예시를 보면 좀더 직관적으로 이해할 수 있을 겁니다.

 

sapply()와 lapply()는 사용 목적이나 사용 형태는 동일합니다만, 차이점이 있다면 결과가 sapply()는 벡터 또는 행렬로 나오는 반면에, lapply()는 결과가 리스트로 나온다는 점입니다. 하나씩 예를 들어 설명해보도록 하겠습니다.

 

 

(1) tapply() : 요인의 수준별로 특정 벡터에 함수 명령어를 동시에 적용

 

MASS 패키지에 내장되어 있는 Cars93 데이터를 가지고 차량 유형(Type)별 고속도록 연비(MPG.highway)의 평균과 표준편차를 tapply()를 활용해 구해보겠습니다.  차량 유형별(Type)은 Compact, Large, Midsize, Small, Sporty, Van 의 6개의 수준(Level)로 구성된 요인(factor)입니다.

 

 

> library(MASS) > str(Cars93) 'data.frame': 93 obs. of 27 variables: $ Manufacturer : Factor w/ 32 levels "Acura","Audi",..: 1 1 2 2 3 4 4 4 4 5 ... $ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 9 1 6 24 54 74 73 35 ... $ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 1 3 3 3 2 2 3 2 ... $ Min.Price : num 12.9 29.2 25.9 30.8 23.7 14.2 19.9 22.6 26.3 33 ... $ Price : num 15.9 33.9 29.1 37.7 30 15.7 20.8 23.7 26.3 34.7 ... $ Max.Price : num 18.8 38.7 32.3 44.6 36.2 17.3 21.7 24.9 26.3 36.3 ... $ MPG.city : int 25 18 20 19 22 22 19 16 19 16 ... $ MPG.highway : int 31 25 26 26 30 31 28 25 27 25 ... $ AirBags : Factor w/ 3 levels "Driver & Passenger",..: 3 1 2 1 2 2 2 2 2 2 ... $ DriveTrain : Factor w/ 3 levels "4WD","Front",..: 2 2 2 2 3 2 2 3 2 2 ... $ Cylinders : Factor w/ 6 levels "3","4","5","6",..: 2 4 4 4 2 2 4 4 4 5 ... $ EngineSize : num 1.8 3.2 2.8 2.8 3.5 2.2 3.8 5.7 3.8 4.9 ... $ Horsepower : int 140 200 172 172 208 110 170 180 170 200 ... $ RPM : int 6300 5500 5500 5500 5700 5200 4800 4000 4800 4100 ... $ Rev.per.mile : int 2890 2335 2280 2535 2545 2565 1570 1320 1690 1510 ... $ Man.trans.avail : Factor w/ 2 levels "No","Yes": 2 2 2 2 2 1 1 1 1 1 ... $ Fuel.tank.capacity: num 13.2 18 16.9 21.1 21.1 16.4 18 23 18.8 18 ... $ Passengers : int 5 5 5 6 4 6 6 6 5 6 ... $ Length : int 177 195 180 193 186 189 200 216 198 206 ... $ Wheelbase : int 102 115 102 106 109 105 111 116 108 114 ... $ Width : int 68 71 67 70 69 69 74 78 73 73 ... $ Turn.circle : int 37 38 37 37 39 41 42 45 41 43 ... $ Rear.seat.room : num 26.5 30 28 31 27 28 30.5 30.5 26.5 35 ... $ Luggage.room : int 11 15 14 17 13 16 17 21 14 18 ... $ Weight : int 2705 3560 3375 3405 3640 2880 3470 4105 3495 3620 ... $ Origin : Factor w/ 2 levels "USA","non-USA": 2 2 2 2 2 1 1 1 1 1 ... $ Make : Factor w/ 93 levels "Acura Integra",..: 1 2 4 3 5 6 7 9 8 10 ... >
> # 차량 Type별 고속도로 연비 평균
>
with(Cars93, tapply(MPG.highway, Type, mean)) Compact Large Midsize Small Sporty Van 29.87500 26.72727 26.72727 35.47619 28.78571 21.88889 >
> 차량 Type별 고속도로 연비 표준편차

> with(Cars93, tapply(MPG.highway, Type, sd))
 Compact    Large  Midsize    Small   Sporty      Van 
2.941088 1.272078 2.510584 5.609091 3.641187 1.452966 

 

만약 tapply()를 활용하지 않는다면

 - Cars93을 Type별로 MPG.highway를 쪼개서 (split()함수 또는 subset() 함수를 활용해서)

 - 각 수준(level)별로 쪼개진 벡터에다가 개별적으로 평균, 표준편차 함수를 일일이 적용한 후에 ...(만약 수준이 100개면 100번 반복작업)

 - 각 결과치를 indexing 해와서 cbind()혹은 rbind()로 묶어서 결과를 취합

하는 단순 반복 작업을 진행해야 합니다. tapply()가 손발의 고생을 덜어주는 유용한 함수라는 것을 알 수 있을 것입니다.

 

 

(2) sapply() : 데이터 프레임 여러 변수에 함수 명령어 동시 적용 
    (결과는 벡터 또는 행렬)

 

sapply()함수를 활용하여 Cars93의 27개 변수 각각의 속성(class)를 알아보도록 하겠습니다.

 

> sapply(Cars93, class)
      Manufacturer              Model               Type          Min.Price              Price          Max.Price 
          "factor"           "factor"           "factor"          "numeric"          "numeric"          "numeric" 
          MPG.city        MPG.highway            AirBags         DriveTrain          Cylinders         EngineSize 
         "integer"          "integer"           "factor"           "factor"           "factor"          "numeric" 
        Horsepower                RPM       Rev.per.mile    Man.trans.avail Fuel.tank.capacity         Passengers 
         "integer"          "integer"          "integer"           "factor"          "numeric"          "integer" 
            Length          Wheelbase              Width        Turn.circle     Rear.seat.room       Luggage.room 
         "integer"          "integer"          "integer"          "integer"          "numeric"          "integer" 
            Weight             Origin               Make 
         "integer"           "factor"           "factor"

 

 

만약 sapply()함수를 사용하지 않는다면,

 - class(Cars93$Manufacturer); class(Cars93$Model); class(Cars93$Type);   ...(중략).... ; class(Cars93$Make)

처럼 변수의 갯수만큼 (여기서는 27번) 단순 반복 작업을 해야합니다.

sapply()는 한줄이면 될 것을 말입니다.

 

 

(3) lapply() : 데이터 프레임 여러 변수에 함수 명령어 동시 적용
    (결과는 리스트)
 

 

 이번에는 lapply()함수로 Cars93 내 27개 변수의 속성(class)을 알아보도록 하겠습니다.  명령문 순서는 sapply()와 lapply()가 동일합니다만, 결과가 나오는 형태가 서로 다름을 확인할 수 있습니다. lapply()는 아래처럼 list 형태로 결과가 나옵니다. 필요한 부분 indexing 하기에 편리하겠지요.

 

> lapply(Cars93, class)
$Manufacturer
[1] "factor"

$Model
[1] "factor"

$Type
[1] "factor"

$Min.Price
[1] "numeric"

$Price
[1] "numeric"

$Max.Price
[1] "numeric"

$MPG.city
[1] "integer"

$MPG.highway
[1] "integer"

$AirBags
[1] "factor"

$DriveTrain
[1] "factor"

$Cylinders
[1] "factor"

$EngineSize
[1] "numeric"

$Horsepower
[1] "integer"

$RPM
[1] "integer"

$Rev.per.mile
[1] "integer"

$Man.trans.avail
[1] "factor"

$Fuel.tank.capacity
[1] "numeric"

$Passengers
[1] "integer"

$Length
[1] "integer"

$Wheelbase
[1] "integer"

$Width
[1] "integer"

$Turn.circle
[1] "integer"

$Rear.seat.room
[1] "numeric"

$Luggage.room
[1] "integer"

$Weight
[1] "integer"

$Origin
[1] "factor"

$Make
[1] "factor"

 

 



lapply() 와 람다 함수 function(x) 를 같이 응용하면 다양한 아이디어를 내서 재미있고 유용한 것들을 할 수 있습니다. 가령, 데이터프레임 칼럼의 이름이 "var_"로 시작하면 이 부분을 "x_"로 칼럼 이름을 일괄 변경하는 작업을 lapply()와 function(x) {gsub("var_", "x_", x)} 를 사용하여 해보겠습니다. 



> var_1 <- c(1:3)

> var_2 <- c(4:6)

> var_3 <- c(7:9)

> df <- data.frame(var_1, var_2, var_3)

> df

  var_1 var_2 var_3

1     1     4     7

2     2     5     8

3     3     6     9


> # change all column names from "var_" to "x_" using lapply() & lambda function

> colnames(df) <- lapply(colnames(df), function(x) {gsub("var_", "x_", x)})

> df

  x_1 x_2 x_3

1   1   4   7

2   2   5   8

3   3   6   9

 



이상으로 apply() 삼총사, 명령문 단순 동일 반복을 한방에 해결할 수 있는, 그래서 손발의 수고를 덜어주는 tapply(), sapply(), lapply()에 대해서 알아보았습니다.  

 

도움이 되었기를 바랍니다.

 

이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

 

728x90
반응형
Posted by Rfriend
,