분석을 진행하다 보면 하나의 데이터 셋에서 변수를 생성, 제거, 변환하는 작업 못지않게 새로운 데이터 셋을 기존의 데이터 셋과 결합하는 작업 또한 빈번합니다.  이번 포스팅에서는 rbind(), cbind(), merge()함수를 활용해서 데이터 프레임 결합하는 방법에 대해서 알아보도록 하겠습니다.

 

예전에 포스팅 했던 R 행렬 함수(☞ 바로가기) 에서 rbind(), cbind()를 다루었던 적이 있는데요, 데이터 프레임도 행렬에서의 데이터 결합과 동일하며, 복습하는 차원에서 한번 더 짚어 보고, key값 기준으로 결합하는 merge()에 대해서 추가로 알아보도록 하겠습니다.

 

 

 R 데이터 프레임 결합 : rbind(), cbind(), merge()

 

[ rbind(), cbind(), merge() 함수 비교 ]

 

 

 

(1) 행 결합 (위 + 아래) : rbind(A, B)

 

먼저 실습에 사용할 데이터 프레임 두개(cust_mart_1, cust_mart_2)를 생성해 보겠습니다.

 

## 데이터 프레임 생성 

cust_id <- c("c01","c02","c03","c04")
last_name <- c("Kim", "Lee", "Choi", "Park")
cust_mart_1 <- data.frame(cust_id, last_name)

cust_mart_1  
# cust_id last_name 
# 1 c01 Kim 
# 2 c02 Lee 
# 3 c03 Choi 
# 4 c04 Park 



cust_mart_2 <- data.frame(
    cust_id = c("c05", "c06", "c07"), 
    last_name = c("Bae", "Kim", "Lim"))
    
cust_mart_2  
# cust_id last_name 
# 1 c05 Bae 
# 2 c06 Kim 
# 3 c07 Lim

 

다음으로 두개의 데이터 프레임(cust_mart_1, cust_mart_2)을 세로 행 결합 (위 + 아래) 해보도록 하겠습니다.

 

## (1) 행 결합 (위 + 아래) rbind(A, B) 
cust_mart_12 <- rbind(cust_mart_1, cust_mart_2) 


cust_mart_12
# cust_id last_name 
# 1 c01 Kim 
# 2 c02 Lee 
# 3 c03 Choi 
# 4 c04 Park 
# 5 c05 Bae 
# 6 c06 Kim 
# 7 c07 Lim

 

rbind()는 row bind 의 약자입니다. rbind()를 무작정 외우려고 하지 마시고, row bind의 약자라는걸 이해하시면 됩니다. 

위의 행 결합 rbind()를 하기 위해서는 결합하려는 두개의 데이터 셋의 열의 갯수와 속성, 이름이 같아야만 합니다. 

 

아래의 예시 처럼 만약 칼럼의 갯수가 서로 다르다면 (cust_mart_12는 열이 2개, cust_mart_3은 열이 3개) 열의 갯수가 맞지 않는다고 에러 메시지가 뜹니다.

 

cust_mart_3 <- data.frame(
    cust_id = c("c08", "c09"), 
    last_name = c("Lee", "Park"), 
    gender = c("F", "M")) 
    
cust_mart_3  
# cust_id last_name gender 
# 1 c08 Lee F 
# 2 c09 Park M


## -- error
rbind(cust_mart_12, cust_mart_3) 
# Error in rbind(deparse.level, ...) 
# : numbers of columns of arguments do not match

 

아래의 예처럼 칼럼의 이름(cust_mart_12 는 cust_id, last_name 인 반면, cust_mart_4는 cust_id, first_name)이 서로 다르다면 역시 에러가 납니다.

 

cust_mart_4 <- data.frame(
    cust_id = c("c10", "c11"), 
    first_name = c("Kildong", "Yongpal"))
    
cust_mart_4
# cust_id first_name 
# 1 c10 Kildong 
# 2 c11 Yongpal 


## -- error
rbind(cust_mart_12, cust_mart_4) 
# Error in match.names(clabs, names(xi)) 
# : names do not match previous names 

 

 

(2) 열 결합 (왼쪽 + 오른쪽) : cbind(A, B)

 

## -- (2) 열 결합 cbind(A, B)
cust_mart_5 <- data.frame(
    age = c(20, 25, 19, 40, 32, 39, 28), 
    income = c(2500, 2700, 0, 7000, 3400, 3600, 2900))
    
cust_mart_12  
# cust_id last_name 
# 1 c01 Kim 
# 2 c02 Lee 
# 3 c03 Choi 
# 4 c04 Park 
# 5 c05 Bae 
# 6 c06 Kim 
# 7 c07 Lim 

cust_mart_5  
# age income 
# 1 20 2500 
# 2 25 2700 
# 3 19 0 
# 4 40 7000 
# 5 32 3400 
# 6 39 3600 
# 7 28 2900


cust_mart_125 <- cbind(cust_mart_12, cust_mart_5)

cust_mart_125  
# cust_id last_name age income 
# 1 c01 Kim 20 2500 
# 2 c02 Lee 25 2700 
# 3 c03 Choi 19 0 
# 4 c04 Park 40 7000 
# 5 c05 Bae 32 3400 
# 6 c06 Kim 39 3600 
# 7 c07 Lim 28 2900

 

cbind()는 column bind의 약자입니다.   cbind()도 열 결합을 하려고 하면 서로 결합하려는 두 데이터셋의 관측치가 행이 서로 동일 대상이어야만 하고, 행의 갯수가 서로 같아야만 합니다

 

만약, cbind()를 하는데 있어 행의 갯수가 서로 다르다면 아래의 예처럼 에러 메시지가 뜹니다.

 

cust_mart_6 <- data.frame(
    age = c(34, 50), 
    income = c(3600, 5100))
    
cust_mart_6 
# age income 
# 1 34 3600 
# 2 50 5100 

## -- error: different number of rows
cbind(cust_mart_125, cust_mart_6) 
# Error in data.frame(..., check.names = FALSE) 
# : arguments imply differing number of rows: 7, 2

 

 

 

(3) 동일 key 값 기준 결합 : merge(A, B, by='key)

 

두개의 데이터셋을 열 결합할 때 동일 key 값을 기준으로 결합을 해야 할 때가 있습니다.  cbind()의 경우 각 행의 관찰치가 서로 동일 대상일 때 그리고 갯수가 같을 때 가능하다고 했는데요, 만약 각 행의 관찰치가 서로 동일한 것도 있고 그렇지 않은 것도 섞여 있다면 그때는 cbind()를 사용하면 안됩니다.  이때는 동일 key 값을 기준으로 결합을 해주는 merge(A, B, by='key')를 사용해야만 합니다.

 

아래의 cbind()의 잘못된 예를 하나 보시겠습니다.

 

cust_mart_12  
# cust_id last_name 
# 1 c01 Kim 
# 2 c02 Lee 
# 3 c03 Choi 
# 4 c04 Park 
# 5 c05 Bae 
# 6 c06 Kim 
# 7 c07 Lim 


cust_mart_7 <- data.frame(
    cust_id = c("c03", "c04", "c05", "c06", "c07", "c08", "c09"), 
    buy_cnt = c(3, 1, 0, 7, 3, 4, 1))
    
cust_mart_7
# cust_id buy_cnt 
# 1 c03 3 
# 2 c04 1 
# 3 c05 0 
# 4 c06 7 
# 5 c07 3 
# 6 c08 4 
# 7 c09 1


cust_mart_127_cbind <- cbind(cust_mart_12, cust_mart_7)

cust_mart_127_cbind  
cust_id last_name cust_id buy_cnt 
# 1 c01 Kim c03 3 
# 2 c02 Lee c04 1 
# 3 c03 Choi c05 0 
# 4 c04 Park c06 7 
# 5 c05 Bae c07 3 
# 6 c06 Kim c08 4 
# 7 c07 Lim c09 1

 

 

cust_mart_12 와 cust_mart_7 의 두 개의 데이터 프레임의 관측치가 서로 같은 것(cust_id 가 c03 ~ c07)도 있는 반면, 서로 다른 것(cust_id 가 c01~c02, c08~c09)도 있습니다.  이런 데이터 셋을 cbind()로 결합시켜버리면 엉뚱한 데이터 셋이 생성되어 버립니다. Oh no~!!!!!

 

이런 경우에는 동일한 key 값을 기준으로 결합을 시켜주는 merge(A, B, by='key')가 답입니다.

SQL에 익숙한 분들은 잘 아시겠지만, merge에는 기준을 어느쪽에 두고 어디까지 포함하느냐에 따라 Inner Join, Outer Join, Left Outer Join, Right Outer Join 등의 4가지 종류가 있습니다.  이를 도식화하면 아래와 같습니다.

 

[ merge() 함수의 join 종류 ]

 

 

위에 제시한 4가지 join 유형별로 merge() 함수 사용예를 들어보겠습니다.

 

 

(3-1) merge() : Inner Join 

 

## -- (3-1) merge() : Inner Join

cust_mart_127_innerjoin <- merge(
    x = cust_mart_12, 
    y = cust_mart_7, 
    by = 'cust_id') 
    

cust_mart_127_innerjoin
# cust_id last_name buy_cnt 
# 1 c03 Choi 3 
# 2 c04 Park 1 
# 3 c05 Bae 0 
# 4 c06 Kim 7 
# 5 c07 Lim 3

 

 

(3-2) merge() - Outer Join

 

## -- (3-2) merge() : Outer Join

cust_mart_127_outerjoin <- merge(
    x = cust_mart_12, 
    y = cust_mart_7, 
    by = 'cust_id', 
    all = TRUE)
    
    
cust_mart_127_outerjoin
# cust_id last_name buy_cnt 
# 1 c01 Kim NA 
# 2 c02 Lee NA 
# 3 c03 Choi 3 
# 4 c04 Park 1 
# 5 c05 Bae 0 
# 6 c06 Kim 7 
# 7 c07 Lim 3 
# 8 c08 <NA> 4 
# 9 c09 <NA> 1

 

 

 

(3-3) merge() : Left Outer Join

 

## -- (3-3) merge() : Left Outer Join 

cust_mart_127_leftouter <- merge(
    x = cust_mart_12, 
    y = cust_mart_7, 
    by = 'cust_id', 
    all.x = TRUE)
    
    
cust_mart_127_leftouter  
# cust_id last_name buy_cnt 
# 1 c01 Kim NA 
# 2 c02 Lee NA 
# 3 c03 Choi 3 
# 4 c04 Park 1 
# 5 c05 Bae 0 
# 6 c06 Kim 7 
# 7 c07 Lim 3 

 

 

(3-4) merge() : Right Outer Join

 

## -- (3-4) merge : Right Outer Join 

cust_mart_127_rightouter <- merge(
    x = cust_mart_12, 
    y = cust_mart_7, 
    by = 'cust_id', 
    all.y = TRUE)
    

cust_mart_127_rightouter
# cust_id last_name buy_cnt 
# 1 c03 Choi 3 
# 2 c04 Park 1 
# 3 c05 Bae 0 
# 4 c06 Kim 7 
# 5 c07 Lim 3 
# 6 c08 <NA> 4 
# 7 c09 <NA> 1 

 

 

이상 merge() 함수의 4가지 유형의 join 에 대하여 알아보았습니다.  마지막으로, merge() 함수는 2개의 데이터 셋의 결합만 가능하며, 3개 이상의 데이터 셋에 대해서 key 값 기준 merge() 결합을 하려고 하면 에러가 나는 점 유의하시기 바랍니다.

 

## -- error

merge(cust_mart_12, cust_mart_5, cust_mart_7, by = 'cust_id') 
# Error in fix.by(by.x, x) 
# : 'by' must specify one or more columns as numbers, names or logical

 

따라서 데이터 프레임 2개씩을 key 값 기준으로 순차적으로 merge() 해나가야 합니다.

 

dplyr 패키지의 Mutating Joins (inner, left, right, full), Filtering Joins (semi, anti), Nesting Joins(nest) 방법은 rfriend.tistory.com/625 를 참고하세요. 

 

이상으로 데이터 프레임의 결합에 대해서 마치도록 하겠습니다. 

많은 도움 되었기를 바랍니다.

 

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

 

728x90
반응형
Posted by Rfriend
,

R 의 벡터나 데이터 프레임을 특정 기준에 따라서 정렬하는 방법에 대해여 알아 보도록 하겠습니다.  SAS를 사용해본 분석가라면 두개 이상의 데이터 셋을 특정 기준으로 merge() 하기 전에 정렬 sort 를 실행해봤을 겁니다.  데이터셋 사이즈가 커지면 merge 하기 전 sort 하느라 시간 많이 잡아먹곤 해서 퇴근하기 전이나 점심먹으러 가기 전에 sorting 돌려놓고 갔던 경험이 있지 않을까 추측해봅니다.  (참고로, R에서는 merge 할때 사전 sorting이 필요 없음)

 

R에서는 데이터 정렬을 위해 sort()와 order() 두개의 함수를 제공하는데요, sort()는 정렬된 값을 순서대로 보여주는 반면에, order()는 데이터 크기의 색인을 제공합니다.  order()가 색인을 제공한다는게 무슨 말인지 잘 이해가 안될 수도 있는데요, 아래 예시를 보면서 설명드리겠습니다.

 

 

 R 벡터, 데이터 프레임 정렬 : sort(), order()

 

 

예시를 위해 세개의 벡터(숫자형 2개, 문자형 1개)와 한개의 데이터 프레임을 만들어보겠습니다.

 

> v1 <- c(40, 30, 50, 50, 90, 40, 50)
> v2 <- c(5100, 6500, 2000, 2000, 9000, 4500, 3000)
> v3 <- c("A", "B", "A", "B", "A", "A", "B")
> v123 <- data.frame(v1, v2, v3)
> v123
  v1   v2 v3
1 40 5100  A
2 30 6500  B
3 50 2000  A
4 50 2000  B
5 90 9000  A
6 40 4500  A
7 50 3000  B

 

 

벡터의 정렬

 

 (1) 숫자 자체 정렬 sort()

 

> v1 [1] 40 30 50 50 90 40 50 >
>
sort(v1) # 오름차순 정렬 [1] 30 40 40 50 50 50 90 >

> sort(v1, decreasing = TRUE) # 내림차순 정렬 [1] 90 50 50 50 40 40 30

 

sort()의 디폴트 정렬순은 오름차순이 되겠습니다.  내림차순으로 하려면 decreasing = TRUE 라는 옵션을 붙여주면 됩니다.

 

 

(2) 정렬 색인 값 order()

 

> v1
[1] 40 30 50 50 90 40 50
> 
> order(v1)
[1] 2 1 6 3 4 7 5
> 

> v1[ order(v1) ] # sort(v1)과 결과 동일 [1] 30 40 40 50 50 50 90

 

 

order(v1) 했을 때 나오는 색인 숫자들 [1] 2 1 6 3 4 7 5 는 무슨 뜻이냐 하면요, v1 중 가장 작은 값(30)이 두번째에 있고, 두번째로 작은값(40)이 첫번째에 있고, 세번째로 작은 값(40)이 여섯번째에 있고.... 이런 뜻입니다.

 

따라서 v1[ order(v1) ] 처럼 v1의 요소를 order(v1)에서 제시한 정렬 색인으로 indexing을 해오면 (1)번의 sort(v1)과 동일한 결과를 얻을 수 있습니다.

 

그러면, 결과가 같은면 그냥 sort(v1)을 쓰면 되지 왜 굳이 order()를 구분해서 사용하고 또 배워야 하는지 의아할 수도 있겠습니다.  order()는 아래의 데이터 프레임에서의 정렬에서 사용하게 되며, sort()는 데이터 프레임에서는 사용할 수 없다는점 때문에 두개 다 배워두어야 합니다.

 

 

데이터 프레임의 정렬

 

> rm(v1, v2, v3) # 벡터 v1, v2, v3 제거
> attach(v123) # 데이터 프레임 활성화
> 

> # v123 데이터 프레임의 전체 행을 v1 오름차순, v2 내림차순, v3 오름차순의 순서대로 정렬

> v123_order <- v123[ order(v1, -v2, v3), ] >

> v123 # 원래 데이터셋 v1 v2 v3 1 40 5100 A 2 30 6500 B 3 50 2000 A 4 50 2000 B 5 90 9000 A 6 40 4500 A 7 50 3000 B >

> v123_order  # 정렬된 후의 데이터 셋
  v1   v2 v3
2 30 6500  B
1 40 5100  A
6 40 4500  A
7 50 3000  B
3 50 2000  A
4 50 2000  B
5 90 9000  A
> 
> detach(v123) 

 

위 예제에서 데이터 프레임 v123 의 행 전체를 v1 오름차순, v2 내림차순(변수 앞에 - 부호), v3 오름차순(문자형도 알파벳순 정렬 가능)의 순서대로 정렬하였습니다.

 

정렬된 후의 데이터 프레임 v123_order 의 제일 왼쪽의 row.names 가 order(v1, -v2, v3)의 색인 결과와 같게 정렬이 되어 있음을 알 수 있습니다. 

 

> order(v1, -v2, v3) [1] 2 1 6 7 3 4 5 >
>
row.names(v123_order) [1] "2" "1" "6" "7" "3" "4" "5" 

 

다시 한번 정리하자면, 데이터 프레임에서 정렬할 때는 order()로 정렬한 색인을 가져다가 index의 행의 위치 ( [, 열] 에 집어 넣고, 열 자리에는 비워둠으로써 [order(), ] 모든 열을 가져오게끔 해서 정렬을 키는 원리입니다.



plyr 패키지arrange() 함수를 사용해서 정렬하는 방법도 있습니다. 내림차순으로 정렬하고자 할 경우에는 desc() 옵션을 추가하면 됩니다.  arrange(data.frame, var1, desc(var2), ...) 이런 형식으로 사용하면 되겠습니다.  아래 예제는 위의 order와 indexing을 사용한 것과 동일한 경과를 얻었음을 알 수 있습니다. 


> library(plyr)

> arrange(v123, v1, desc(v2), v3)

  v1   v2 v3

1 30 6500  B

2 40 5100  A

3 40 4500  A

4 50 3000  B

5 50 2000  A

6 50 2000  B

7 90 9000  A 



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

 

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

 

728x90
반응형
Posted by Rfriend
,

R 에서 데이터 분석을 하다보면 데이터셋 에서 필요한 부분만 선별적으로 취사선택해서 별도로 분석 마트를 만드는 경우가 다반사입니다.  따라서 이번 데이터 프레임에서의 변수 선택 방법을 잘 알아두시면 데이터셋을 떡주무르듯이 가지고 노는데 아주 유용할 것입니다.

 

R에서 데이터를 선별하는 방법으로 indexing 에 대해서 이전에 소개해드린적이 있는데요(☞ R indexing 바로가기), 선별 조건이 까다로워질수록 indexing 프로그램(index[]와 which() 함수 사용)이 복작해해지는 반면, subset() 함수는 상대적으로 깔끔한 면이 있습니다.  아래 두개의 기법별 예제를 보시고 사용하기에 편한 기법을 이용하시면 되겠습니다.  

 

실습을 위해서 mtcars 데이터 프레임을 활용하겠습니다.  mtcars는 자동차 관련된 11개 변수, 32개 관측치로 구성된 데이터 프레임이 되겠습니다. 이번 실습에는 아래 색칠해 놓은 연비(mpg), 실린더 개수(cyl), 변속기(am) 의 세개 변수를 사용하겠습니다.

 

> help(mtcars)
mtcars

Format

A data frame with 32 observations on 11 variables.

[, 1] mpg Miles/(US) gallon
[, 2] cyl Number of cylinders
[, 3] disp Displacement (cu.in.)
[, 4] hp Gross horsepower
[, 5] drat Rear axle ratio
[, 6] wt Weight (lb/1000)
[, 7] qsec 1/4 mile time
[, 8] vs V/S
[, 9] am Transmission (0 = automatic, 1 = manual)
[,10] gear Number of forward gears
[,11] carb Number of carburetors

Source

Henderson and Velleman (1981), Building multiple regression models interactively. Biometrics, 37, 391–411.

 

> str(mtcars)
'data.frame':	32 obs. of  11 variables:
 $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
 $ cyl : num  6 6 4 6 8 6 8 4 4 6 ...
 $ disp: num  160 160 108 258 360 ...
 $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
 $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
 $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
 $ qsec: num  16.5 17 18.6 19.4 17 ...
 $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
 $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
 $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
 $ carb: num  4 4 1 1 2 1 4 2 2 4 ...
> 
>
head(mtcars) mpg cyl disp hp drat wt qsec vs am gear carb Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1

 

만약 아래의 요건으로 데이터 마트를 구성해야 한다고 해봅시다.

 

"변속기가 자동(am == 0)이고 & 실린더가 4개 또는 6개 (cyl == 4 or cyl == 6) 인
자동차들의
연비(mpg) 평균(mean())는?"

 

"변속기가 수동(am == 1)이고 & 실린더가 4개 또는 6개 (cyl == 4 or cyl == 6)) 인 자동차들의
연비(mpg) 평균(mean())는?"

 

 

(1) indexing & which() 함수를 활용한 특정 조건을 만족하는 변수, 관측치 선택


> attach(mtcars) >

> # (a) 변속기가 자동이고 & 실린더가 4개, 6개인 자동차의 연비, 실린더, 자동/수동 변수 선별
> mtcars_mart_0 <- mtcars[ which( am == 0 & cyl %in% c(4, 6)), c("mpg", "cyl", "am")] > mtcars_mart_0 mpg cyl am Hornet 4 Drive 21.4 6 0 Valiant 18.1 6 0 Merc 240D 24.4 4 0 Merc 230 22.8 4 0 Merc 280 19.2 6 0 Merc 280C 17.8 6 0 Toyota Corona 21.5 4 0

> > mean(mtcars_mart_0$mpg) [1] 20.74286

> # (b) 변속기가 수동이고 & 실린더가 4개, 6개인 자동차의 연비, 실린더, 자동/수동 변수 선별 > mtcars_mart_1 <- mtcars[ which( am == 1 & cyl %in% c(4, 6)), c("mpg", "cyl", "am")] > mtcars_mart_1 mpg cyl am Mazda RX4 21.0 6 1 Mazda RX4 Wag 21.0 6 1 Datsun 710 22.8 4 1 Fiat 128 32.4 4 1 Honda Civic 30.4 4 1 Toyota Corolla 33.9 4 1 Fiat X1-9 27.3 4 1 Porsche 914-2 26.0 4 1 Lotus Europa 30.4 4 1 Ferrari Dino 19.7 6 1 Volvo 142E 21.4 4 1

> > mean(mtcars_mart_1$mpg) [1] 26.02727 > > detach(mtcars)

 

attach()와 detach()로 데이터 프레임을 활성화해놓고 indexing을 했음에 유의하세요.

 

위의 indexing 에서 변수를 선택할 때 c("mpg", "cyl", "am")이라고 변수명을 직접 입력했는데요, 열의 위치를 숫자로 c(1, 2, 9) 라고 입력해도 동일한 결과가 나옵니다.

 

> mtcars_mart_9 <- mtcars[ which( am == 0 & cyl %in% c(4, 6)), c(1, 2, 9)] > mtcars_mart_9 mpg cyl am Hornet 4 Drive 21.4 6 0 Valiant 18.1 6 0 Merc 240D 24.4 4 0 Merc 230 22.8 4 0 Merc 280 19.2 6 0 Merc 280C 17.8 6 0 Toyota Corona 21.5 4 0

 

 

 (2) subset(Data 이름, select = c(변수명), subset = (선별 조건)) 변수, 관측치 선택

 

> # (a) 변속기가 자동이고 & 실린더가 4개 or 6개인 자동차의 연비, 실린더, 자동/수동 변수 선별

> mtcars_subset_0 <- subset(mtcars, + select = c(mpg, cyl, am), + subset = (am == 0 & cyl %in% c(4, 6))) > mtcars_subset_0 mpg cyl am Hornet 4 Drive 21.4 6 0 Valiant 18.1 6 0 Merc 240D 24.4 4 0 Merc 230 22.8 4 0 Merc 280 19.2 6 0 Merc 280C 17.8 6 0 Toyota Corona 21.5 4 0 > mean(mtcars_subset_0$mpg) [1] 20.74286 >

> # %in% 대신 수직바 '|' (or) 를 써서 할 수도 있음

> subset(mtcars, + select = c(mpg, cyl, am), + subset = ((am == 0 & cyl == 4) | (am == 0 & cyl == 6))) mpg cyl am Hornet 4 Drive 21.4 6 0 Valiant 18.1 6 0 Merc 240D 24.4 4 0 Merc 230 22.8 4 0 Merc 280 19.2 6 0 Merc 280C 17.8 6 0 Toyota Corona 21.5 4 0
>

>

> # (b) 변속기가 수동이고 & 실린더가 4개 or 6개인 자동차의 연비, 실린더, 자동/수동 변수 선별 > mtcars_subset_1 <- subset(mtcars, + select = c(mpg, cyl, am), + subset = (am == 1 & cyl %in% c(4, 6))) > mtcars_subset_1 mpg cyl am Mazda RX4 21.0 6 1 Mazda RX4 Wag 21.0 6 1 Datsun 710 22.8 4 1 Fiat 128 32.4 4 1 Honda Civic 30.4 4 1 Toyota Corolla 33.9 4 1 Fiat X1-9 27.3 4 1 Porsche 914-2 26.0 4 1 Lotus Europa 30.4 4 1 Ferrari Dino 19.7 6 1 Volvo 142E 21.4 4 1 > mean(mtcars_subset_1$mpg) [1] 26.02727

 

 

만약, 데이터프레임에서 1개의 변수만을 indexing & which() 함수로 해서 새로운 객체에 할당하면 vector로 생성이 됩니다.  반면에, 데이터프레임에서 1개의 변수만을 subset() 함수로 해서 새로운 객체에 할당하면 dataframe 으로 생성이 되는 차이가 있습니다.  따라서, 사용 목적/용도가 뭐냐에 따라서 그에 맞는 방법을 사용하시기 바랍니다. 

 

 

아래에는 연속 선택 c(1:5), 혹은 제외 -c(1:5)를 하는 팁을 소개하였습니다.  indexing 기법에서도 동일합니다.  여러개의 변수를 순서에 따라서 일괄 선택할 때는 일일이 변수를 나열하지 않고 몇번째에서 몇번째까지 숫자나 혹은 변수명을 : 을 사용해서 지정해주면 되니 편하겠지요.  제외하려면 - 를 사용하면 끝.  편하죠?!

 

> # 연속 선택 : c(1:5) > mtcars_subset_1_5 <- subset(mtcars, + select = c(1:5) + ) > > head(mtcars_subset_1_5) mpg cyl disp hp drat Mazda RX4 21.0 6 160 110 3.90 Mazda RX4 Wag 21.0 6 160 110 3.90 Datsun 710 22.8 4 108 93 3.85 Hornet 4 Drive 21.4 6 258 110 3.08 Hornet Sportabout 18.7 8 360 175 3.15 Valiant 18.1 6 225 105 2.76 > > # 제외 : -c() > mtcars_subset_6_11 <- subset(mtcars, + select = -c(1:5) + ) > > head(mtcars_subset_6_11) wt qsec vs am gear carb Mazda RX4 2.620 16.46 0 1 4 4 Mazda RX4 Wag 2.875 17.02 0 1 4 4 Datsun 710 2.320 18.61 1 1 4 1 Hornet 4 Drive 3.215 19.44 1 0 3 1 Hornet Sportabout 3.440 17.02 0 0 3 2 Valiant 3.460 20.22 1 0 3 1

 

 



(3) dplyr 패키지의 select() 로 변수 선택, filter() 로 조건에 맞는 관측치 선택, 

     summarize() 요약 통계량 계산



> install.packages("dplyr")

> library(dplyr)

> # (a) 변속기가 자동(am == 0)이고 & 실린더가 4개 or 6개인 자동차의 평균 연비

> mtcars %>% select(mpg, cyl, am) %>% filter(am == 0 & cyl %in% c(4, 6))

   mpg cyl am

1 21.4   6  0

2 18.1   6  0

3 24.4   4  0

4 22.8   4  0

5 19.2   6  0

6 17.8   6  0

7 21.5   4  0

> mtcars %>% select(mpg, cyl, am) %>% filter(am == 0 & cyl %in% c(4, 6)) %>% summarise(mean(mpg))

  mean(mpg)

1  20.74286

> # (b) 변속기가 수동(am == 1)이고 & 실린더가 4개 or 6개인 자동차의 평균 연비

> mtcars %>% select(mpg, cyl, am) %>% filter(am == 1 & cyl %in% c(4, 6))

    mpg cyl am

1  21.0   6  1

2  21.0   6  1

3  22.8   4  1

4  32.4   4  1

5  30.4   4  1

6  33.9   4  1

7  27.3   4  1

8  26.0   4  1

9  30.4   4  1

10 19.7   6  1

11 21.4   4  1

> mtcars %>% select(mpg, cyl, am) %>% filter(am == 1 & cyl %in% c(4, 6)) %>% summarise(mean(mpg))

  mean(mpg)

1  26.02727

 



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

 


728x90
반응형
Posted by Rfriend
,

R 의 데이터 구조에는 스칼라, 벡터, 행렬, 요인, 데이터 프레임, 리스트가 있습니다.  이중에서 벡터와 데이터 프레임이 통계 분석 시에 가장 많이 사용됩니다. 

 

이번 포스티에서는 데이터 프레임에서 신규 변수를 생성하는 두 가지 방법에 대해서 알아보겠습니다.  (1) 첫번째 방법은 'dataframe$variable' 처럼 '$'를 사용하는 것이며, (2) 두번째 방법으로는 transform() 함수를 사용하는 것입니다.

 

transform() 함수와 함께 within()함수를 사용해서 연속형 변수를 범주형 변수로 변환하는 방법에 대해서는 이전 포스팅 (☞ 바로가기) 을 참고하시기 바랍니다.

 

 

 R 데이터 프레임 신규 변수 생성 : dataframe$variable, transform()

 

(1) dataframe$variable

 

성인의 키와 몸무게를 가지고 비만도를 나타내는 지수인 체질량 지수(體質量指數, body mass index, BMI)를 신규로 생성하여 보도록 하겠습니다.

 

먼저 가상으로 성인 10명의 키와 몸무게로 구성된 데이터 프레임을 만들어보겠습니다.

 

> height <- c(175, 159, 166, 189, 171, 173, 179, 167, 182, 170)
> weight <- c(62, 55, 59, 75, 61, 64, 63, 65, 70, 60)
> h_w_d.f <- data.frame(height, weight)
> h_w_d.f
   height weight
1     175     62
2     159     55
3     166     59
4     189     75
5     171     61
6     173     64
7     179     63
8     167     65
9     182     70
10    170     60

 

체질량 지수(BMI)를 구하는 공식은 키가 t 미터, 몸무게가 w 킬로그램일 때 BMI = w/t^2 입니다. (키 단위는 미터 임에 주의)

 

이번에는 위의 체질량 지수(BMI) 공식에 따라 dataframe$variable를 이용하여 데이터 프레임에 BMI 변수를 신규로 생성해 보도록 하겠습니다.

 

> options(digits=4) # 숫자 개수 지정해주는 옵션. 이거 지정 안해주면 소숫점 5~6자리까지 나옴 > h_w_d.f$bmi_1 <- h_w_d.f$weight/(h_w_d.f$height/100)^2 > h_w_d.f height weight bmi_1 1 175 62 20.24 2 159 55 21.76 3 166 59 21.41 4 189 75 21.00 5 171 61 20.86 6 173 64 21.38 7 179 63 19.66 8 167 65 23.31 9 182 70 21.13 10 170 60 20.76

 

 

위에서 보시는 것처럼 매번 dataframe$variable 을 입력해줘야만 하는게 꽤 불편합니다.  신규 변수 생성 하나 하고 말거면 뭐 그럭저럭 쓸 수도 있겠읍니다만, 다수 변수를 이용해서 다수 변수를 신규 생성해야 하는 경우라면 아무래도 손이 많이 가는 방법이라고 하겠습니다.

 

손, 발이 편하고자 하는 분이라면 아래의 transfrom() 함수를 이용해보시기 바랍니다.

 

 

(2) transform(dataframe, new_variable = 수식)

 

> ## transform()
> h_w_d.f <- transform(h_w_d.f, 
+                      bmi_2 = weight/(height/100)^2)
> 
> h_w_d.f
   height weight bmi_1 bmi_2
1     175     62 20.24 20.24
2     159     55 21.76 21.76
3     166     59 21.41 21.41
4     189     75 21.00 21.00
5     171     61 20.86 20.86
6     173     64 21.38 21.38
7     179     63 19.66 19.66
8     167     65 23.31 23.31
9     182     70 21.13 21.13
10    170     60 20.76 20.76

 

(1)번의 dataset$variable 에서 매번 '$'를 입력해줘야하는 번거로움 대비 transform()은 정말 깔끔 그 자체임을 알 수 있습니다. 

거기다가 한꺼번에 여러개의 변수를 생성하는 잇점도 있답니다.  아래 예제를 보시지요.

 

> options(digits=3)
> h_w_d.f <- transform(h_w_d.f, 
+                      bmi_sqrt = sqrt(bmi_2), 
+                      bmi_log10 = log10(bmi_2)
+                      )

 

> View(h_w_d.f)
 

 

 

transform() 함수와 함께 within()함수를 사용해서 연속형 변수를 범주형 변수로 변환하는 방법에 대해서는 이전 포스팅 (☞ 바로가기) 을 참고하시기 바랍니다.

 

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

 

728x90
반응형
Posted by Rfriend
,

R에서 데이터 분석을 한다고 했을 때 대부분의 데이터 형식은 데이터 프레임일 것입니다.  R을 처음 교육 받을 때는 벡터를 가지고 주로 실습을 하다가, 실전으로 넘어오면 데이터 프레임을 가지고 데이터 탐색, 전처리를 하게 되다 보니 데이터 구조 (스칼라, 벡터, 행렬, 요인, 데이터 프레임, 리스트)에 대해서 명확한 이해를 하지 않는 분들의 경우 헷갈리고 어려워하기도 합니다.

 

그래서 앞으로 서너번에 나누어서 데이터 프레임에서 사용할 수 있는 데이터 전처리/변환에 대한 함수들을 알아보도록 하겠습니다.

 

이번 포스팅에서는 먼저 데이터 프레임에서  names(), rename() 함수를 사용해서 변수명 변경하기를 해보겠습니다.

 

 

 데이터 프레임 변수명 변경 names(), rename()

 

 

(1) 데이터 프레임 변수명 변경 names()

 

먼저 MASS 패키지에 있는 Cars93 데이터 프레임 내 1~5번째 변수만 선택해서, base 패키지에 있는 names() 함수로 변수명을 변경해보겠습니다.

 

> ## 데이터 프레임 변수명 변경 rename()
> library(MASS)
> # Cars93 데이터 프레임 내 변수명 확인
> names(Cars93)
 [1] "Manufacturer"       "Model"              "Type"               "Min.Price"          "Price"             
 [6] "Max.Price"          "MPG.city"           "MPG.highway"        "AirBags"            "DriveTrain"        
[11] "Cylinders"          "EngineSize"         "Horsepower"         "RPM"                "Rev.per.mile"      
[16] "Man.trans.avail"    "Fuel.tank.capacity" "Passengers"         "Length"             "Wheelbase"         
[21] "Width"              "Turn.circle"        "Rear.seat.room"     "Luggage.room"       "Weight"            
[26] "Origin"             "Make"              
> 

>
> Cars93 데이터 프레임의 1~5번째 변수만 선택한 후 names()로 변수명 변경 > Cars93_subset <- Cars93[,c(1:5)] > names(Cars93_subset) [1] "Manufacturer" "Model" "Type" "Min.Price" "Price" >

 

 


> names(Cars93_subset) <- c("V1", "V2", "V3", "V4", "V5")
> names(Cars93_subset)
[1] "V1" "V2" "V3" "V4" "V5"
 

 

 

 

(2-1) 데이터 프레임 변수명 변경 : reshape 패키지의 rename() 함수

 

다음으로 reshape 패키지에 들어있는 rename() 함수에 대해서 알아보겠습니다.  reshape 패키지는 install.packages("reshape") 으로 새로 설치 후에 library(reshape)로 호출해서 사용해야 합니다.

 

> # rename() 
> install.packages("reshape")
> library(reshape)
> 
> Cars93_subset <- rename(Cars93_subset, 
+                         c(V1 = "V1_Manufacturer", 
+                           V2 = "V2_Model", 
+                           V3 = "V3_Type", 
+                           V4 = "V4_Min.Price", 
+                           V5 = "V5_Price"))
 

 

 

 

(2-2) 데이터 프레임 변수명 변경 : plyr 패키지의 rename() 함수

 

데이터 전처리에 plyr 패키지도 많이 사용되는데요, 변수명 변경에 rename() 함수명은 똑같구요, 다만 변경하고자 하는 old 변수명에도 큰따옴표 ""를 사용한다는 것이 위의 reshape패키지의 rename()함수와 다른 점이 되겠습니다.

 

> install.packages("plyr")
> library(plyr)
> Cars93_subset <- rename(Cars93_subset, 
+                         c("V1_Manufacturer" = "Manufacturer", 
+                           "V2_Model" = "Model", 
+                           "V3_Type" = "Type", 
+                           "V4_Min.Price" = "Min.Price", 
+                           "V5_Price" = "Price"))
> View(Cars93_subset)
 

 

 

 

 

 

(2-3) 데이터 프레임의 변수명 변경 : dplyr 패키지의 rename() 함수

 

데이터 프레임의 데이터 전처리에 막강한 기능을 제공하는 dplyr 패키지에도 변수명 변경을 위한 rename() 함수를 제공합니다.  dplyr 패키지는 plyr 패키지와 친척 관계이지만 rename() 함수의 문법은 차이가 많습니다. 헷갈리지 않도록 조심하시기 바랍니다.

 

위이 plyr 패키지의 rename() 함수와 비교해서 dplyr 패키지의 rename() 함수의 차이점을 정리해보자면,

 

  - 새로운 변수명(new_var)이 앞에 나오고, 이전 변수명(old_var)이 뒤에 나옵니다

  - 큰 따옴표("") 안씁니다.

  - 바꾸고자 하는 변수가 여러개 있을 때 c() 로 안묶어주며, ","(comma)로 나열해줍니다.

 

# dplyr package, rename(dataframe, new_var1 = old_var1, new_var2 = old_var2, ...)
install.packages("dplyr")
library(dplyr) 

 

> Cars93_2 <- Cars93[ ,c(1:3)]
> names(Cars93_2)
[1] "Manufacturer" "Model"        "Type"
> Cars93_3 <- rename(Cars93_2, 
+                    New_Manufacturer = Manufacturer,
+                    New_Model = Model, 
+                    New_Type = Type)
> 
> names(Cars93_3)
[1] "New_Manufacturer" "New_Model"        "New_Type"

 

 

 

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

 

728x90
반응형
Posted by Rfriend
,

선형대수, 통계분석, 데이터마이닝, 최적화 등을 수행할 때 행렬을 많이 사용합니다. 분석에 필요한 변수가 많아질 수록 변수들의 계수를 행렬로 해서 수식을 표현하고 컴퓨터에게 연산을 시키는 것이 편리하기 때문입니다.

 

선형대수(Linear Algebra)를 배우지 않은 분들께서는 행렬연산이 좀 낯설텐데요, 행렬연산에 대해 좀더 깊이 들어가는 부분은 일단 이번 포스팅에서는 생략하겠으며, 앞으로 특정 분석 주제에 대한 포스팅에서 기회가 되면 다루도록 하겠습니다.

 

대신, 이번 포스팅에서는 행렬 연산을 위한 R의 함수 중에서 특히 행렬, 데이터 프레임에서 데이터 전처리 하는데 있어 활용도가 높은 함수들 위주로 몇 가지를 살펴보겠습니다.  데이터 분석 쪽으로 진로를 잡으려고 생각하는 분이라면 선형대수는 꼭 배워두시면 기초를 다잡을 수 있을 거라서 추천드립니다.

 

 

[ m*n 행렬 (m by n matrix) ]

 

 

 

 

이번에 살표볼 R 행렬 연산 함수로 +, -, *, /, ^, %*%, cbind(), rbind(), colMeans(), rowMeans(), colSums(), rowSums(), t() 등을 순서대로 예를 들어 설명하겠습니다.

 

(참고로, R에서 배열, 행렬도 모양이 조금 다른 벡터입니다.  따라서 벡터의 명령어가 배열, 행렬에도 적용된다고 보면 되겠습니다.)

 

 

 R 행렬 연산 : +, -, *, /, ^, %*%, cbind(), rbind(),

                   colMeans(), rowMeans(), colSums(), rowSums(), t()

 

(1) 행렬 내 각 숫자끼리의 연산 : +, -, *, /, ^

 

> ## 행렬 X와 행렬 Y 생성

> X <- matrix(1:4, nrow=2, ncol=2, byrow=FALSE, dimnames = NULL)
> X
     [,1] [,2]
[1,]    1    3
[2,]    2    4
>
> Y <- matrix(5:8, nrow=2, ncol=2, byrow=TRUE, dimnames = NULL)
> Y
     [,1] [,2]
[1,]    5    6
[2,]    7    8

 

예전 데이터 구조에 대한 포스팅에서 행렬 생성에 대해 다루었었는데요, matrix()함수와 각 옵션에 대해서 한번 더 복습해 보겠습니다.  ncol 은 칼럼 갯수, nrow 는 행의 갯수, byrow=FALSE 는 X 행렬 예에서 처럼 위에서 아래로 byrow=TRUE는 Y 행렬 예시 처럼 왼쪽에서 오른쪽으로 행렬이 생성됩니다.

 

> ## 행렬 X와 행렬 Y의 각 숫자끼리의 연산: +, -, *, /, ^ > X + Y [,1] [,2] [1,] 6 9 [2,] 9 12 > > X - Y [,1] [,2] [1,] -4 -3 [2,] -5 -4 > > X * Y [,1] [,2] [1,] 5 18 [2,] 14 32 > > X / Y [,1] [,2] [1,] 0.2000000 0.5 [2,] 0.2857143 0.5 > > X ^ Y [,1] [,2] [1,] 1 729 [2,] 128 65536

 

숫자형으로 구성된 두 행렬에 대해 +, -, *, /, ^ 연산을 하게 되면 같은 위치에 있는 숫자끼리 연산을 하게 됩니다.  (1)번 X * Y 곱셉의 경우 아래 (2)번 예시의 X %*% Y 와 어떻게 다른지 유심히 살펴보시기 바랍니다.  선형대수를 공부하신 분이라면 (1)번 X * Y 곱셈 결과를 보고 '이거 뭐지?' 하고 의아해 하실 것 같은데요, (1) 번 형식의 X * Y 는 각 구성 원소를 순서대로 그냥 곱한 겁니다.  선형대수에서 배웠던 행렬과 행렬의 곱셉은 아래 (2) 번 X %&% Y 형식의 명령문을 사용하게 됩니다.

 

 

(2) 행렬 X와 행렬 Y의 곱 : X %*% Y

 

> X %*% Y
     [,1] [,2]
[1,]   26   30
[2,]   38   44

 

통계, 머신러닝, 최적화 등에서 사용하는 곱셉은 아래 (2)번 X %*% Y 곱셉인 경우가 많을 텐데요, 분석 목적에 맞게 선택해서 사용하시기 바랍니다.

 

 

(3) 행렬 세로 결합 cbind(), 행렬 가로 결합 rbind()

 

> ## 행렬 세로 결합 cbind() : column bind
>
cbind(X, Y) [,1] [,2] [,3] [,4] [1,] 1 3 5 6 [2,] 2 4 7 8 >
> ## 행렬 가로 겹합 rbind() : row bind
>
rbind(X, Y) [,1] [,2] [1,] 1 3 [2,] 2 4 [3,] 5 6 [4,] 7 8

 

두 행렬을 cbind(), rbind()가 행끼리 결합하는 건지, 열끼리 결합하는 건지 헷갈릴 수 도 있는데요, cbind()는 column bind, rbind()는 row bind 로 해서 기억하시면 이해하기 쉽겠지요?

 

 

(4) 행렬 X의 각 열의 평균값으로 구성된 벡터 colMeans(X), 행렬 Y의 각 행의 평균값으로 구성된 벡터 rowMeans(Y)

 

> ## colMeans(), rowMeans()
> colMeans(X)
[1] 1.5 3.5
> rowMeans(X)
[1] 2 3
> 
> colMeans(Y)
[1] 6 7
> rowMeans(Y)
[1] 5.5 7.5

 

colMeans()의 경우 데이터 프레임에서 특정 변수를 '$'로 지정해놓고 mean() 함수를 실행하면 동일한 값을 구할 수 있습니다.  데이터 프레임에서는 보통 열(변수)를 기준으로 통계 분석을 실시하므로, 만약 열을 기준으로 요약 통계를 보려면 colMeans(), 혹은 아래 colSums() 함수는 알아두면 편하겠지요. 

 

참고로, 보통은 행(row) 데이터에 대해서 분석을 하려면 (6)번의 전치 t() 함수나 melt(), cast()함수로 데이터를 열(column)으로 재구성해서 colMeans(), colSums() 나 그 밖의 통계함수를 써서 분석을 합니다.

 

 

(5)  행렬 X의 각 열의 합계로 구성된 벡터 colSums(X), 행렬 Y의 각 행의 합계로 구성된 벡터 rowSums(Y)

 

> ## colSums(), rowSums()
> colSums(X)
[1] 3 7
> rowSums(X)
[1] 4 6
> 
> colSums(Y, na.rm = TRUE)
[1] 12 14
> rowSums(Y, na.rm = TRUE)
[1] 11 15

 

na.rm = TRUE 는 행렬 연산 시에 결측값이 있으면 포함하지 말고 계산하라는 뜻입니다.  예전 포스팅에서 결측값 확인/처리 (☞ 바로 가기) 에 대해서 다룬 적이 있는데요, 아래에 Cars93 데이터 프레임을 가지고 na.omit() 함수와 동일하게 행 내에 결측값이 있으면 그 행 전체를 삭제하는 방법을  rowSums() 함수와 is.na() 함수를 사용해서 수행하는 방법을 알아보겠습니다.

 

> 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 ...
> sum(is.na(Cars93))
[1] 13
> 
> Cars93_na.omit <- na.omit(Cars93)
> sum(is.na(Cars93_na.omit))
[1] 0
> 
> Cars93_rowSums <- Cars93[rowSums(is.na(Cars93)) == 0, ]
> sum(is.na(Cars93_rowSums))
[1] 0

 

na.omit()함수가 훨씬 수월하므로 굳이 dataset[rowSums(is.na(dataset)) == 0, ] 처럼 프로그래밍을 할 필요가 있을까 싶기는 합니다만, rowSums() 함수를 이렇게도 이용할 수 있구나 정도로 알아두시면 좋겠습니다.

 

 

(6) 행렬 X의 전치 t(X)

 

> ## 행렬의 전치 t()
> X
     [,1] [,2]
[1,]    1    3
[2,]    2    4
> 
>
t(X) [,1] [,2] [1,] 1 2 [2,] 3 4 >
> > Y [,1] [,2] [1,] 5 6 [2,] 7 8 >
>
t(Y) [,1] [,2] [1,] 5 7 [2,] 6 8 > > Z <- matrix(1:6, nrow=2, ncol=3) > Z [,1] [,2] [,3] [1,] 1 3 5 [2,] 2 4 6 >
>
t(Z) [,1] [,2] [1,] 1 2 [2,] 3 4 [3,] 5 6

 

t(X)로 행렬을 전치하면 위의 예에서 보는 것처럼 행과 열이 서로 바뀌게 됩니다.  통계분석의 행과 열 기준을 바꾸고 싶거나, 그래프 그릴 때 가로와 세로를 바꾸고 싶을 때 t() 함수로 전치를 해서 쓰면 되겠지요.

 

한번더 부언하자면, 선형대수에 나오는 행렬 연산 전부를 다루지는 않았습니다만, 데이터 분석 쪽으로 계속 공부하려는 분이라면 선형대수 공부는 몸에 좋은 밑거름이 될것이니 따로 공부해보시길 권합니다.

 

 

행렬에 대해 소개한 포스팅을 아래에 링크 걸어놓습니다. 참고하세요.

 

행렬 기본 이해

특수한 형태의 행렬 (zero matrixtranspose matrixsymmetric matrixupper triangular matrixlower triangular matrixdiagonal matrixidentity matrix, I, or unit matrix, U)

가우스 소거법을 활용한 역행렬 계산 (Invertible matrix, Gauss-Jordan elimination method)

여인수를 활용한 역행렬 계산 (Invertible matrix, by using cofactor)

벡터의 기본 이해와 연산 (vector: addition, subtraction, multiplication by scalar)

벡터의 곱 (1) 내적 (inner product, dot product, scalar product, projection product)

벡터의 곱 (2) 외적 (outer product, cross product, vector product, tensor product)

 

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

 

 

728x90
반응형
Posted by Rfriend
,

LA다저스의 클레이튼 커쇼가 7월24일 메이저리그 뉴욕 메츠와의 경기에서 선발 등판해 9이닝 3피안타 무실점 11탈삼진으로 호투하며 팀의 3-0 승리를 이끌었습니다. 올해 두번째 완봉승입니다.

 

6회말까지 매츠 타선을 꽁꽁 묶으며 매 회 삼진을 잡아냈고 1루로 단 한명의 타자도 내보내지 않은 완벽한 투구를 선보였습니다. 올 초반의 부진을 씻고 완벽하게 LA다저스의 에이스로서 부활한 모습을 유감없이 보여준 경기였습니다.

 

2015년 7월 25일 기준으로 커쇼의 내셔널리그 시즌 성적을 보면 '탈삼진 185'개로 '1등'을 달리고 있습니다.

 

승/패 

 평균 자책점

탈삼진 

 자책점

 8승 6패

 2.51 (7위)

 185 (1위)

 39

* 시즌성적 출처 : Daum 인물검색

 

 

커쇼가 탈삼진 1위를 할 수 있는 데에는 당연히 야구공을 자유자재로 제어할 수 있는 제구력이 뒷받침 될 것입니다. 하지만 제구력만 가지고는 탈삼진 1등을 할 수는 없을 것입니다.  메이저리그의 타자들 또한 난다 긴다하는 강타자들이기 때문입니다.  탈삼진 1등을 하기 위해서는 제구력과 함께 "게임이론", "확률통계"에 대한 특출남 또한 반드시 필요합니다.  오늘 포스팅은 바로 이 후자에 대한 이야기가 되겠습니다.

 

커쇼 정도의 에이스가 던지는 야구공은 시속 150km 까지도 속도가 나옵니다.  이 정도 속도라면 투수가 공을 던지고 나서 0.45초~0.5초 사이에 포수의 글러브에 공이 꽂힙니다.  타자 입장에서는 스윙을 하는데 약 0.2초 정도의 시간이 걸리므로, 이 시간을 빼면 결국 공이 투수의 손을 떠난 순간부터 해서 약 0.2초~0.25초 만에 공의 구질, 속도, 궤적 등을 판단해서 스윙을 해야 한다는 얘기가 됩니다.  즉, 공을 보고 난 후에 공을 파악해서 그에 맞게 스윙을 한다는게 거의 불가능하다는 소리가 되겠습니다.  이 말인 즉슨, 커쇼 같이 강속구를 뿌리는 에이스에게는 타자는 공을 "예측"해서 때려야 한다는 뜻입니다.  이것은 마치 축구 시합에서 패널티킥을 찰 때 축구공이 너무 빠르므로, 골키퍼는 공이 오는 것을 보고 나서 판단 후에 몸을 날리는 것이 아니라 미리 마음 속으로 공이 어디로 올지를 "예측"한 후에 몸을 날려야 겨우 공을 막을까 말까한 상황과 같다고 할 수 있겠습니다.

 

* 사진출처: 데일리안 스포츠

 

게임이론에서는 이러한 유형의 게임을 "제로섬 게임(zero-sum game)", "동시 의사결정 게임"이라고 합니다.

 

투수는 타자가 홈런이나 안타를 치지 못하도록 공의 높이(상, 중, 하)와 방향(좌, 중, 우), 구위(속구, 커브, 슬라이더, 스크루볼), 그리고 속도(빠른 볼, 중간 속도 볼, 느린 볼) 등을 조합하여 다양한 배합의 볼을 뿌립니다. 

 

이에 대항해 타자는 투수가 던질 볼의 궤적, 구위, 속도를 예측하여 방망이를 휘두르게 됩니다.  결국, 투수와 타자는 제로섬 게임에서 누가 "상대방의 예측을 허물어 트리느냐"에 따라 승패가 좌우됩니다.

 

게임이론에서는 이러한 "제로섬 동시의사결정 게임"에서 이기기 위해서는 상대방이 자신의 "다음번 수"를 알아채지 못하도록 하기 위해 "무작위 전략(Random Strategy)"을 쓰라고 합니다.  만약에 투수가 볼 배합에 있어 무작위 전략을 사용하지 않는다면 상대방 타자는 특정한 패턴을 감지하고, 다음번 공의 구위에 대해 예측하여 방망이를 휘두른다면 홈런이나 안타를 치고 나갈 가능성이 높아지겠지요. 

 

가령, 느린 변화구 다음에는 몸쪽 바짝 붙은 빠른 속구를 던지더라는 패턴, 체인지업 유인구 다음에는 바깥쪽 꽉찬 슬라이더를 던지더라는 패턴...등 등이 비록 투수는 의식적으로 인지하지는 못하더라도 만약에 투수의 볼 배합 간에 무의식적으로 작용한 패턴이 있다면, 무작위(Random)가 아니었다면, 상대팀은 그동안의 과거 투구 이력 데이터를 모두 모아서 통계적으로 투구 볼 배합 간의 교차표를 구해보고, 볼 구위/궤적/속도 간 서로 독립적인지 아닌지 유의성을 검정해본다든지, 아니면 데이터마이닝의 순차분석(sequence analysis)를 통해서 볼 구위/궤적/속도 간에 자주 발생하는(support가 높은) 패턴을 찾는다든지 해서, 확률적으로 가능성이 높은 패턴을 다음 투구 볼 배합을 예측하는데 활용할 수 있을 것입니다.  투수 입장에서는 상대팀이 간파하지 못하도록 "무작위 전략"을 사용하는게 쉽지 많은 않은 일입니다.  불완전한 인간이기 때문이지요.  머리 속으로는 무작위로 볼 배합을 섞는다고 노력을 한다고 해도, 만약 어떤 볼 배합 패턴이 상대 상자에게 잘 먹혀들면 아무래도 무의식적으로 투수는 소위 잘 먹히는 패턴에 안주할 가능성이 높습니다.  그렇다고 투수가 호주머니에 통계의 난수표를 넣어가지고 다니거나 투구 전에 동전 던지기를 할 수도 없는 노릇이지요.

 

투수가 공만 잘 던진다고 삼진왕이 될 수 없는 이유가 바로 여기에 있습니다.  야구는 투수 혼자 하는게 아니라 공을 쳐내려고 눈에 쌍심지를 켠 상대방 타자가 있기 때문이며, 상대방 타자는 투수가 무슨 공을 던질지 예측하고 준비하고 있기 때문이고, 투수는 이러한 "상대 타자의 예측"을 예측해서 "상대 타자의 예측을 허물어뜨릴 때" 비로소 삼진을 잡아낼 수 있기 때문입니다.  그리고 상대 타자가 바깥쪽 공을 밀어치기를 잘하는 선수인지, 안쪽 공을 끌어 당겨치기를 잘하는 선수인지, 홈런을 잘치는지 안타를 잘치는지, 이전 타석에서는 어떤 성적과 패턴을 보여주였는지 등 등...상대 타자에 대한 정보를 잘 알고 있어야 하고, 상대 타자에 맞춤형으로 약점을 파고 들 수 있는 볼 배합 전략을 수립할 수 있어야 합니다. 

 

다시 요약하자면, 투수는 공의 제구력이 좋아야 하는 것은 기본이고, 머리가 좋아야 하고, 게임이론과 확률통계의 최고수가 되어야 하는 이유가 여기에 있습니다.  (투수가 공 제구력만 좋고 머리가 나쁘면, 그때는 포수의 역할이 아주 중요해지겠지요.  포수랑 투수랑 볼 배합 가지고 싸인 주고 받는 모습 방송 중계 많이 보셨지요?)

 

참고로, 게임이론에서는 각 플레이어가 취할 수 있는 의사결정의 경우의 수별로 각 플레이어간의 페이오프 표를 만들고, 여기에 각 의사결정의 경우의 수별로 확률을 구한 후에, 자신과 경쟁자간에 의사결정에 따른 상호작용을 고려한 효용 혹은 비용을 고려하여 최종적으로 플레이어 상호 간에 의사결정을 바꾸지 않을 내쉬 균형(Nash equilibrium)을 찾습니다.  말이 어렵지요. ^^;  영화 뷰티풀 마인드가 존 내쉬를 주인공으로 삼은 영화인데요, 참고하시기 바라며 더 이상 이론적으로 깊이 들어가지는 않겠습니다.  경우의 수가 많아지면 확률과 기대값 계산을 손으로 하는게 무척 힘이 듭니다만, 요즘은 컴퓨터가 좋아서 이런 내쉬 균형 찾는 계산은 컴퓨터가 해줍니다. 좋은 세상에 살고 있습니다. ㅎㅎ

 

참고로 하나더, 커쇼는 제가 개인적으로도 좋아하는 투수인데요, 커쇼의 선행에 대한 신문기사가 있어서 아래에 소개하는 것으로 이번 포스팅을 마칠까 합니다.

 

 

 

 

"어머니로부터 희생을, 엘런으로부터 사랑을 배운 커쇼는 반듯한 길로만 걸었다. 커쇼는 지난 20일 지금은 아내가 된 엘런과 비행기에 올랐다. 아프리카의 오지 잠비아로 봉사활동을 떠난 것. 잠비아는 2010년 결혼한 두 사람의 신혼여행지이기도 하다. 독실한 크리스찬인 엘런은 달콤한 신혼여행 대신 따뜻한 봉사여행을 제안했다. 커쇼는 잠비아에서 에이즈에 걸린 11세 소년 호프(Hope)를 만난 뒤 고아원 '희망의 집(Hope's home)'을 잠비아의 수도 루사카에 지었다. 커쇼 부부는 매년 시즌이 끝나면 잠비아로 날아가 어린이들과 놀아주며 선교활동을 한다.

그는 삼진 하나를 잡을 때마다 500달러를 적립해 잠비아 어린이들의 교육사업에 보탠다. 매년 10만달러 이상을 적립하고 있고 올해는 '선수들이 뽑은 최우수선수상'을 받은 기념으로 26만 달러를 추가로 기부했다. 그가 선수로 뛰는 LA와 그의 고향 댈러스에서도 봉사활동을 한다. 커쇼는 학생들을 위한 스포츠 프로그램을 만들어 야구를 직접 가르친다. 다저스가 내셔널리그 챔피언십 시리즈까지 진출한 올해도 시즌이 끝나자마자 댈러스로 달려가 봉사활동을 했다. 그라운드 밖에서도 그는 에이스의 품격을 보여주고 있다. ...  이하 생략... "

* 출처: LA중앙일보, 2013.11.27 

728x90
반응형
Posted by Rfriend
,

데이터는 크게 (1) 명목형 또는 순서형의 범주형 데이터 (categorical data)와 (2) 연속형 데이터 (continuous data) 로 구분할 수 있습니다.  R에서는 범주형 데이터를 요인(factor)형 데이터 구조라고 부르고 있으며, 순서(order)가 있는 경우는 순서형 요인(ordered factor)라고 해서 구분하기도 합니다.

 

분석하고자 하는 데이터 셋을 받으면 제일 먼저 데이터 구조와 데이터 형태를 탐색하게 됩니다.  그리고 분석 목적과 시나리오에 따라서 변수를 변환하게 되지요.  이번 포스팅에서는 연속형 변수를 범주형 변수로 변환하는 3가지 방법에 대해서 알아보도록 하겠습니다.  통계기법 중 도수분포표, 교차분할표, 카이제곱 검정이라든지, 로지스틱회귀분석, 그래프 중 막대그림, 원그림, 점그림 등의 경우 범주형 변수로 변환을 해야만 하며, 데이터 탐색 시에도 범주형 변수로 변환하여 분포 형태나 집단 간 비교를 하게 되므로 이번 포스팅은 활용도가 매우 높다고 하겠습니다.

 

cut() 함수, ifelse() 함수, within() 함수를 이용해서 아래 예를 들어 설명하도록 하겠습니다.

 

 

 연속형 변수를 범주형 변수로 변환하기: cut(), ifesle(), within()

 

(1) cut()

 

> ## 통계시험 점수 (stat_score) > student_id <- c("s01", "s02", "s03", "s04", "s05", "s06", "s07", "s08", "s09", "s10") > stat_score <- c(56, 94, 82, 70, 64, 82, 78, 80, 76, 78) > mean(stat_score) [1] 76 > hist(stat_score)

 

 

 

> # 데이터 프레임 생성
> score_d.f <- data.frame(student_id, stat_score)
> score_d.f
   student_id stat_score
1         s01         56
2         s02         94
3         s03         82
4         s04         70
5         s05         64
6         s06         82
7         s07         78
8         s08         80
9         s09         76
10        s10         78
 
> rm(student_id, stat_score)

 

 

위의 통계시험 성적을 가지고 cut() 함수를 이용하여 "수", "우", "미", "양", "가" 등급을 매겨보도록 하겠습니다.

right = TRUE 옵션을 주면 a < x <= b  와 같이 오른쪽 숫자까지 포함하여 해당 등급을 부여하게 됩니다.

right = FALSE 옵션을 주면 a<= x <b 의 조건으로 등급을 부여하며, include.lowest = TRUE 옵션을 주면 구성요소 값이 최소값과 같아도 변환을 시키게 됩니다.

 

> ## (1) cut()
> score_d.f <- transform(score_d.f, 
+                  stat_score_1 = cut(stat_score, breaks = c(0, 60, 70, 80, 90, 100), 
+                                     include.lowest = TRUE, 
+                                     right = FALSE, 
+                                     labels = c("가", "양", "미", "우", "수")
+                                     ), 
+                  stat_score_2 = cut(stat_score, breaks = c(0, 60, 70, 80, 90, 100), 
+                                     include.lowest = FALSE, 
+                                     right = FALSE, 
+                                     labels = c("가", "양", "미", "우", "수")
+                                     ),
+                  stat_score_3 = cut(stat_score, breaks = c(0, 60, 70, 80, 90, 100), 
+                                     include.lowest = FALSE, 
+                                     right = TRUE, 
+                                     labels = c("가", "양", "미", "우", "수")
+                                     ), 
+                  stat_score_4 = cut(stat_score, breaks = c(0, 60, 70, 80, 90, 100), 
+                                     include.lowest = TRUE, 
+                                     right = TRUE, 
+                                     labels = c("가", "양", "미", "우", "수")
+                                     )
+                        )
> 
> score_d.f
   student_id stat_score stat_score_1 stat_score_2 stat_score_3 stat_score_4
1         s01         56           가           가           가           가
2         s02         94           수           수           수           수
3         s03         82           우           우           우           우
4         s04         70           미           미           양           양
5         s05         64           양           양           양           양
6         s06         82           우           우           우           우
7         s07         78           미           미           미           미
8         s08         80           우           우           미           미
9         s09         76           미           미           미           미
10        s10         78           미           미           미           미

 

그런데 사용하다 보면 right 옵션, include.right 옵션, 그리고 labels 부여하는 순서도 그렇고, 머리속이 복잡해집니다. 아래의 ifelse()나 within() 함수는 위의 cut()보다는 수식의 부호를 직접 입력한다는 측면에서 사용하기에 더 편하고 직관적인 면이 있습니다.

 

 

(2) ifelse()

 

> attach(score_d.f)

> score_d.f <- transform(score_d.f, + stat_score_5 = ifelse(stat_score < 60, "가", + ifelse(stat_score >= 60 & stat_score < 70, "양", + ifelse(stat_score >= 70 & stat_score < 80, "미", + ifelse(stat_score >= 80 & stat_score < 90, "우", "수" + )))) + ) > detach(score_d.f) > score_d.f

 

 

 

 

 

> class(score_d.f$stat_score_5)
[1] "character"
 

 

위 표의 제일 오른쪽에 'stat_score_5' 변수가 ifelse() 함수를 이용해서 만든 범주형 변수가 되겠습니다.  cut() 대비 수식 등호, 부등호를 직접 입력하니 직관적으로 분석가가 원하는 범주로 수식을 적을 수 있는 장점이 있습니다만, 범주의 수준(level)이 많아질 수록 괄호 열고 닫는데 유의해야 합니다.  위의 예제의 경우 5개 범주로 나누는데 괄호 열고 "(((("  닫는 것이 "))))" 총 4개가 사용이 되었네요.  갯수 조심하지 않으면 콘솔 창에 에러날거예요.  RStudio 사용하면 ifelse() 괄호 하나씩 더해갈 때 마다 괄호 닫는것도 저절로 생기니 차근 차근 하시면 될겁니다.

 

그리고 stat_score_5 의 속성(class)이 요인(factor)이 아닌 문자(character)로 되어 있습니다.  만약 요인별로 통계 분석을 하고자 한다면 as.factor() 함수로 문자형을 요인형으로 먼저 변환을 시킨 후에 분석을 진행해야 합니다.

 

 

(3) within()

 

> ## within()
> score_d.f <- within( score_d.f, {
+   stat_score_6 = character(0) 
+   stat_score_6[ stat_score < 60 ] = "가" 
+   stat_score_6[ stat_score >=60 & stat_score < 70 ] = "양" 
+   stat_score_6[ stat_score >=70 & stat_score < 80 ] = "미" 
+   stat_score_6[ stat_score >=80 & stat_score < 90 ] = "우" 
+   stat_score_6[ stat_score >=90 ] = "수" 
+   
+   stat_score_6 = factor(stat_score_6, level = c("수", "우", "미", "양", "가"))
+ })
> 
> score_d.f$stat_score_6
 [1] 가 수 우 미 양 우 미 우 미 미
Levels: 수 우 미 양 가

 

 

 

within() 함수는 먼저 새로 만들 변수 stat_score_6 = character(0)  이라고 해서 문자형 변수라고 신규생성/지정을 해주고 시작합니다.

수식 등호, 부등호로 구간 설정하구요, 제일 마지막 줄에 factor() 함수로 해서 level = c("수", "우", "미", "양", "가") 라고 해서 수준을 지정해 줄 수 있습니다.  성적은 순서(order)가 있으므로 level 에 지정한 순서가 stat_score_6 요인 변수의 level 순서가 되겠습니다.

 

score_d.f$stat_score_6  라고 해서 indexing을 해서 보면 제일 아랫줄에 "Levels: 수 우 미 양 가" 라고 해서 순서가 제대로 인식되어 있음을 알 수 있습니다.  개인적으로 within() 함수를 순서형 요인변수 만들 때 위 셋 중에서 가장 많이 사용하는 편입니다.

 

아래는 제일 오른쪽에 within()함수로 만든 stat_score_6 변수까지 모두 한꺼번에 열어본 score_d.f 데이터 프레임이 되겠습니다. 

 

 

 

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

 

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

 

728x90
반응형
Posted by Rfriend
,

그동안 R 에서 숫자형 벡터의 처리에 대한 여러가지 함수를 알아 보았습니다.  벡터는 R의 똘망똘망한 일꾼이자 무기라고 말씀드렸는데요, 이번 포스팅에서는 숫자형 벡터 외에 문자형 벡터를 가지고 떡 주무르듯이 가지고 놀 수 있는 문자형 함수들을 알아보도록 하겠습니다.

 

R 문자형 벡터를 다루는 함수로는 nchar(), substr(), paste(), strsplit(), sub(), gsub(), grep(), regexpr(), gregexpr() 등이 있습니다.  아래에 예제를 들어가면서 하나씩 설명 드리도록 하겠습니다.

 

R을 활용한 텍스트 마이닝은 별도로 나중에 분석 주제로 들어가면 그때 설명드리도록 하겠습니다. 

 

 

 R 문자함수 nchar(), substr(), paste(), strsplit(), sub(), gsub()

 

(1) nchar(x) : 문자형 벡터 x의 구성요소 개수 구하기

 

> # nchar()
> x <- c("Seoul", "New York", "London", "1234")
> nchar(x)
[1] 5 8 6 4

 

"New York"의 경우 중간에 스페이스바 공간 하나가 있는데요, 이것도 '1'개로 count해서 '8'로 계산했다는 점은 유의하기 바랍니다.

 

 

(2) substr(x, start, stop) : 문자형 벡터 x의 start에서 부터 stop 까지만 잘라오기 (부분 선택)

 

> # substr()로 문자형 벡터 부분 선택하기 > time_stamp <- c("201507251040", "201507251041", "201507251042", "201507251043", "201507251044") > t_yyyymm <- substr(time_stamp, 1, 6) > t_yyyymm [1] "201507" "201507" "201507" "201507" "201507" >

> # 데이터 프레임에서 transfrom()함수와 substr()함수로 부분 선택한 내용으로 새로운 변수 만들기
>
gas_temp <- c(145.0, 145.1, 145.5, 150.1, 150.6) > ts_gas_temp <- data.frame(time_stamp, gas_temp) > ts_gas_temp time_stamp gas_temp 1 201507251040 145.0 2 201507251041 145.1 3 201507251042 145.5 4 201507251043 150.1 5 201507251044 150.6 > > ts_gas_temp <- transform(ts_gas_temp, mmdd = substr(time_stamp, 5, 8)) > ts_gas_temp <- transform(ts_gas_temp, hhmm = substr(time_stamp, 9, 12)) > > ts_gas_temp time_stamp gas_temp mmdd hhmm 1 201507251040 145.0 0725 1040 2 201507251041 145.1 0725 1041 3 201507251042 145.5 0725 1042 4 201507251043 150.1 0725 1043 5 201507251044 150.6 0725 1044

 

첫번째 예제는 time_stamp 라는 벡터를 가지고 부분 선택하는 것이고, 두번째 예제는 ts_gas_temp라는 데이터 프레임에서 특정 변수를 선택해서 transfrom()이라는 함수와 substr()함수를 사용해서 부분 선택한 내용으로 새로운 변수를 만들어 보는 예제가 되겠습니다.

 

 

(3) paste(x, y, sep = " ") : 문자형 벡터 x와 y를 붙이기

 

> # 문자형 벡터의 객체들을 paste()로 하나로 붙이기 > paste("I", "Love", "New York", sep = "") [1] "ILoveNew York" > paste("I", "Love", "New York", sep = " ") [1] "I Love New York" > paste("I", "Love", "New York", sep = "_") [1] "I_Love_New York" >

> # 데이터 프레임에서 transform()함수와 paste()함수로 두개의 변수를 하나로 합쳐서 새로운 변수 만들기 > ts_gas_temp <- transform(ts_gas_temp, mmddhhmm = paste(mmdd, "일_", hhmm, "분", sep="")) > ts_gas_temp time_stamp gas_temp mmdd hhmm mmddhhmm 1 201507251040 145.0 0725 1040 0725일_1040분 2 201507251041 145.1 0725 1041 0725일_1041분 3 201507251042 145.5 0725 1042 0725일_1042분 4 201507251043 150.1 0725 1043 0725일_1043분 5 201507251044 150.6 0725 1044 0725일_1044분

 

첫번째 예제는 문자형 벡터의 객체들을 paste() 함수를 사용해 하나로 붙인 것인데요, sep="", sep=" ", sep="_" 등 sep에 무엇을 넣느냐에 따라 결과가 달라지는 것을 알 수 있습니다.

 

두번째 예제는 데이터 프레임에서 transfrom()과 paste()함수를 사용해 두개 이상의 문자형 벡터 변수를 합쳐서 새로운 문자형 변수를 만드는 예제입니다.  실전에서는 데이터 프레임 구조의 데이터 셋을 가지고 작업을 많이 하므로 알아두면 유용하겠지요.

 

 

(4-1) strsplit(x, split= ",") : 문자형 벡터 x를 split 기준으로 해서 나누기

 

> # strsplit() 으로 분리하기
> name <- c("Chulsu, Kim", "Younghei, Lee", "Dongho, Choi")
> name_split <- strsplit(name, split=",")
> name_split
[[1]]
[1] "Chulsu" " Kim"  

[[2]]
[1] "Younghei" " Lee"    

[[3]]
[1] "Dongho" " Choi" 

> 

> # indexing 해오기 > last_name <- c(name_split[[1]][2], name_split[[2]][2], name_split[[3]][2]) > last_name [1] " Kim" " Lee" " Choi" > > first_name <- c(name_split[[1]][1], name_split[[2]][1], name_split[[3]][1]) > first_name [1] "Chulsu" "Younghei" "Dongho" > > # last_name과 first_name, name을 데이터 프레임으로 묶기

> name_d.f <- data.frame(last_name, first_name, name)
> name_d.f
  last_name first_name          name
1       Kim     Chulsu   Chulsu, Kim
2       Lee   Younghei Younghei, Lee
3      Choi     Dongho  Dongho, Choi

 

strsplit()함수는 split="any" 의 큰따옴표 안에 들어가는 구분자 기준에 따라서 문자열을 분리해주는 함수입니다.

첫번째 예제 strsplit()함수로 name 문자형 벡터를 나누어보니 결과가 리스트(list) 구조로 나왔습니다.

 

두번째 예제는 리스트(list) 결과에서 Indexing해오는 방법을 소개하여보았습니다.  Indexing은 데이터 처리, 프로그래밍할 때 정말 많이 쓰고 반드시 알아두어야 하는 핵심 개념 중의 하나입니다. Indexing에 관한 자세한 내용은 이전 포스팅을 참고하세요 (☞ 바로가기)

 

세번째 예제는 strsplit()함수로 분리한 개별 벡터들을 하나의 데이터 프레임으로 묶는 방법이 되겠습니다. 세트로 알아두면 좋겠지요?

 

 

 

(4-2) 데이터프레임에서 문자열을 구분자 기준으로 나누는 방법은 아래 예제를 참조하세요.

        (spliting character in dataframe by delimeter)

         :  data.frame(do.call('rbind', strsplit(as.character(df$var), split='delimeter', fixed=T))) 

 

> ##############################################
> ## split character in dataframe by delimeter
> ##############################################
> 
> # example data frame
> name_df <- data.frame(ID = c(1:3), name = c("Chulsu/Kim", "Younghei/Lee", "Dongho/Choi"))
> name_df
  ID         name
1  1   Chulsu/Kim
2  2 Younghei/Lee
3  3  Dongho/Choi
> 
> 
> # strsplit character in dataframe by delimeter
> name_strsplit <- data.frame(do.call('rbind', 
+                                     strsplit(as.character(name_df$name), 
+                                              split = '/', 
+                                              fixed = TRUE)))
> name_strsplit
        X1   X2
1   Chulsu  Kim
2 Younghei  Lee
3   Dongho Choi
> 
> 
> # changing name
> # install.packages("reshape")
> library(reshape)
> name_strsplit <- rename(name_strsplit, 
+                         c(X1 = "First_Name", 
+                         X2 = "Last_Name"))
> 
> name_strsplit
  First_Name Last_Name
1     Chulsu       Kim
2   Younghei       Lee
3     Dongho      Choi

 

 

 

 

(5) sub(old, new, x): 문자형 벡터 x에서 처음 나오는 old문자를 new문자로 한번만 바꾸기

(6) gsub(old, new, x): 문자형 벡터 x 내에 모든 old 문자를 new 문자로 모두 바꾸기

 

> # sub()는 처음 나오는 old 문자만 new 문자로 한번만 바꿈 > z <- c("My name is Chulsu. What's your name?") > sub("name", "first name", z) [1] "My first name is Chulsu. What's your name?" >

> # gsub()는 모든 old 문자를 new 문자로 바꿈
> gsub("name", "first name", z)
[1] "My first name is Chulsu. What's your first name?"
> 

> # new 자리에 ""를 넣으면 없애는 효과

> sub("My name is Chulsu. ", "", z)
[1] "What's your name?"

 

첫번째 예제 sub()함수에서는 "name"을 "first name"으로 바꾸라는 명령문입니다.  필자가 이해를 돕기 위해 name에다가 밑줄을 그어놓았는데요, z 벡터에는 name이 두번 나오는데 sub()함수로 바꾸기를 했더니 처음 나오는 "name"은 "first name"으로 바뀌었지만 두번째 나오는 "name"은 그대로인 것을 알 수 있습니다.

 

반면에 두번째 예시에서 gsub()는 첫번째 나오는 "name"을 "first name"으로 바꾸었을 뿐만 아니라, 두번째 나오는 "name" 또한 "first name"으로 바꾸었습니다.  따라서 분석 목적에 따라서 sub()와 gsub()를 선택적으로 사용하시면 되겠습니다.

 

 아래 예제는 데이터 프레임에서 transform()함수로 sub()함수를 사용해서 특정 변수 내 특정 문자열을 old -> new로 바꾸는 내용이 되겠습니다. 

 

> cust_id <- c("c1", "c2", "c3", "c4", "c5", "c6")
> size <- c("XS", "L", "M", "XS", "XL", "S")
> cust_db <- data.frame(cust_id, size)
> cust_db
  cust_id size
1      c1   XS
2      c2    L
3      c3    M
4      c4   XS
5      c5   XL
6      c6    S
>

> # size 변수 ㄴ "XS" 사이즈를 "S" 사이즈로 바꿔서 size_1 이라는 새로운 변수에 저장(생성)해라 > cust_db <- transform(cust_db, size_1 = sub("XS", "S", size)) > cust_db cust_id size size_1 1 c1 XS S 2 c2 L L 3 c3 M M 4 c4 XS S 5 c5 XL XL 6 c6 S S 

 

예제의 경우 만약 'old'에서 'new'로 바꿔야 하는 조건이 2개 이상이 되면 ifelse() 라든지 within() 함수 등을 사용해야 하는데요, 이것은 나중에 새로운 범주형 변수 만들기에서 별도로 소개해드리도록 하겠습니다.

 


문자열에 포함되어 있는 모든 점(".", point)을 없애려면 gsub(".", "", col, fixed=TRUE) 라고 하거나, 혹은 정규 표현식을 이용해서 gsub("[.]", "", col) 이라고 해주면 됩니다. 


> # how to remove a point "." in a string

> id <- c("a", "b", "c", "c")

> col <- c("11.23", "64.12", "931.01", "3.3.0.4.1.2")

> df <- data.frame(id, col)

> df

  id         col

1  a       11.23

2  b       64.12

3  c      931.01

4  c 3.3.0.4.1.2

> df <- transform(df, 

+                 col_2 = gsub(".", "", col, fixed=TRUE))

> df

  id         col  col_2

1  a       11.23   1123

2  b       64.12   6412

3  c      931.01  93101

4  c 3.3.0.4.1.2 330412

> df <- transform(df, 

+                 col_3 = gsub("[.]", "", col))

> df

  id         col  col_2  col_3

1  a       11.23   1123   1123

2  b       64.12   6412   6412

3  c      931.01  93101  93101

4  c 3.3.0.4.1.2 330412 330412


 

(7) grep(pattern, x) : 문자열 벡터에서 특정 부분 문자열 패턴 찾기

 

> grep("1010", c("1001", "1010", "1110", "101000"))
[1] 2 4
> 
> grep("1010", c("1001", "1009", "1110", "100000"))
integer(0) 

 

위의 첫번째 예는 문자열 "1010"이라는 패턴이 2번째와 4번째 원소에 들어있다는 뜻입니다.

두번째 예에서는 문자열 "1010"이라는 패턴이 하나도 안들어 있다는 뜻이 되겠습니다.

 

 

(8) regexpr() : text 내에서 패턴이 가장 먼저 나오는 위치 찾기

 

> regexpr("NY", "I love NY and I'm from NY")
[1] 8
attr(,"match.length")
[1] 2
attr(,"useBytes")
[1] TRUE


"NY"이라는 패턴이 8번째 (스페이스 포함) 자리에서 처음으로 나왔다는 뜻입니다. 

 

 

(9) gregexpr() : text 내에서 패턴이 나오는 모든 위치를 찾기

 

> gregexpr("NY", "I love NY and I'm from NY")
[[1]]
[1]  8 24
attr(,"match.length")
[1] 2 2
attr(,"useBytes")
[1] TRUE 

 

"NY"이라는 패턴이 8번째, 그리고 24번째 자리에서 나왔다는 뜻입니다. 

 

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

 

728x90
반응형
Posted by Rfriend
,

 

터미네이터 1편, 2편을 재미있게 봤었던 저로서는 테미네이터 제니시스(Terminator Genisys)를 예고편을 보고나니 개봉일이 기다려지더군요. 

 

3편은 졸작이었고, 4편은 그럭저럭이었고, Daum 영화 코너에서 이번 제니시스는 평점이 6.6점으로 낮은 편에 속하기에 기대치를 너무 높지는 않게 잡고 극장으로 향했습니다.   터미네이터 2나 아바타, 매드맥스 분노의 도로 등의 영화처럼 눈이 번쩍 뜨이고 입이 쩍 벌어지는 영화는 아니었습니다만, 그래도 나름 재미있게 보았습니다. 

 

저는 10점 만점에 8.5점 주겠습니다.  6.6점은 아무래도 너무 박한 점수 같네요. 사라코너를 맥아리없는 여전사로 묘사한 블로그글도 봤는데요, 전 이번 제니시스 사라코너도 매력있게 봤습니다. ㅎㅎ

 

이번 포스팅은 영화 내용 중에서 '시간여행'에 대한 논리가 앞뒤가 맞지 않는 점을 짚어보도록 하겠습니다.  타임킬링용 영화에 무슨 논리 타령인가 하고 의아해하실 수도 있는데요, 영화 보는 내내 시간여행 스토리를 배배 꼬아 놔서 '아, 뭔가 좀 이상한데...어...헷갈리네...근데 뭔가 앞뒤가 안맞는거 같은데...음...'하고 속으로 불편해하셨던 분들이 계셨을 것 같습니다.  오늘 글은 그런 분들, 뭔가 의심이 가고 혹은 머리가 혼란스럽고 내용 이해가 잘 안되셨던 분들을 위해서 논리적으로 한번 정리해가면서 따져보는 글이 되겠습니다. 

* 그림 출처: Daum 영화 포스터 (http://movie.daum.net/moviedetail/moviedetailMain.do?movieId=62713)

 

물론, 순도 99%의 고농축 스포일러가 들어있으므로, 영화를 아직 안보신 분들은 나중에 영화 다 보신 후에 아래 글 읽어보시기를 권합니다. 

(이렇게 써놓으면... 더 읽고 싶어지는게 사람 심리지요. ㅋㅋ   이글 보고 난 후에 영화 보신 후 재미없다고 저한테 후회하심 안됩니다. ^^;)

 

 

 터미네이터, 시간여행 논리가 순 엉터리인 이유

> 이야기는 터미네이터 1편으로 거슬러 올라갑니다.  1편 부터 시간여행 스토리 논리가 요상스럽습니다. 

    아래 터미네이터 1 시간여행 순서도를 보면서 설명드리겠습니다.

     

    [ 터미네이터 1 시간여행 순서도 ]

     

     

     

     

    숫자를 따라가면 사건이 벌어진 순서를 한번 더 복기해보도록 하겠습니다.

     

    ① 스카이넷이 2029년에 존 코너가 이끄는 인간 반란군한테 지게 생겼으니깐 터미데이터를 1994년 LA로 보내서 존 코너의 엄마 사라 코너를 죽이려고 합니다. 

     

    ② 이를 알아챈 존 코너 인간 반란군 대장은 오른팔 심복 카일 리스를 타임머신에 태워 1994년 LA로 보내면서 자신의 엄마 사라 코너를 지키라고 하지요.

     

    ③ 카일 리스는 타임머신을 타고 1994년 LA로 시간여행을 떠나옵니다.

     

    ④ 카일 리스는 목숨을 아끼지 않고 존 코너의 미래의 엄마 (이때는 존 코너는 태어나지 않았음) 사라 코너를 지킵니다.  그러다가 둘이 그만 사랑에 빠집니다.  그리고 사랑을 나눕니다.

     

    ⑤ 카일 리스는 터미네이터랑 싸우다가 죽고, 터미네이터도 프레스에 눌러 망가지고, 사라 코너는 아기를 임신한 사실을 알게 되는데...그게 바로 훗날, 2편에 에드워드홀롱이 열연한 존 코너가 됩니다.

     

    우리가 생각해보아야 할 문제는,

    • 만약 2029년에 존 코너가 자기의 아버지가 될 카일 리스를 과거 1994년으로 보내지 않았더라면 존 코너는 존재하지 않게 되는 것인가?
    • 2029년 존 코너가 카일 리스를 과거로 보내기 전까지의 기간 동안, 즉 1994년~2028년까지는 카일 리스는 1994년의 시간 & LA라는 공간에 없었는데 그러면 사라 코너는 카일리스를 못 만난 것이기 때문에 남편 없이 존 코너를 임신할 수 없으므로 존 코너는 존재하지 않게 되는 것이 아닌가?
    • 존재하지도 않는 존 코너가 어떻게 자기 아버지가 될 카일리스를 과거로 보낼 수 있나?
    • 카일리스를 과거로 보낼 수 없으면 존 코너는 잉태되지도 않았을 텐데.... ?

    와 같이 꼬리에 꼬리를 물며 순환논리오류에 빠지게 됩니다.  1탄 부터 시간여행 논리에 문제가 있었던 겁니다.

     

     

    > 터미네이터 제니시스는 시간여행 논리가 터미네이터 1보다 더 엉터리로 완전 꼬여있습니다. 

     

    [ 터미네이터 5 제니시스 시간여행 순서도 ]

     

     

     

     

    터미네이터 제니시스의 시간여행 순서를 역시 복기해보자면,

     

    ① 제니시스(스카이넷)이 존 코너 인간 반란군에게 질 것 같자 타임머신으로 터미네이터를 1994년 LA로 보냄

    ② 존 코너가 이를 알아채고 카일리스에게 과거로 가서 자신의 엄마 사라 코너를 지켜달라고 명함

    ③ 카일리스는 2029년에서 1994년으로 터미네이터가 간 시간과 공간으로 시간여행을 떠남

    ④ 제니시스의 탄생을 막기 위해 카일리스와 사라코너는 2017년 미래로 시간여행을 떠남

        (※ 아놀드슈왈드제네거가 역을 맡은 착한 터미네이터 팝스는 사라 코너와 카일리스에게 "짝짓기"(ㅋㅋ)를 하라고 보채지만 사라 코너와 카일리스는 "짝짓기" 없이 2017년으로 타임머신을 타고 날아감.  이거 중요함!!!)

    ⑤ 존 코너가 제니시스의 공격으로 오염이 되어 나노 터미네이터 T-3000 이 되어 제니시스의 탄생이 순조롭게 되도록, 즉 사라코너와 카일리스를 막기 위해 2017년으로 과거 시간여행을 떠남.  엄마와 아빠와 아들이 한자리에서 만남.

    ⑥ 존 코너는 죽고, 사라코너와 카일리스는 사랑에 빠졌는지는 잘 모르겠음.  썸타는 듯한 분위기는 있었으나, 팝스 말마따나 "짝짓기"는 안함.

     

    터미네이터 1보다 더 노골적으로,

    • 사라코너와 카일리스가 사랑을 나누지 않았음에도 존 코너는 어떻게 2029년에 존재할 수 있는가? 
      (④ 번 순서 다시 한번 보면 문제가 뭔지 알 수 있음)
    • 만약 ⑥번에서 사라코너와 카일리스가 사랑을 나누고 존 코너가 태어난다고 가정했을 때, 그러면 2029년이 되었을 때 존 코너는 11살 소년이 되어있을 것인데...

    마지막에 성인 카일리스가 꼬맹이 카일리스에게 가서 손바닥에 손을 그으면서 몇 마디(뒤돌아보지 말고 가라. 스카이넷은 제니시스다.. 등) 해주는 것으로 마치 이야기의 초반부의 사라코너와 카일리스의 긴장국면에 대한 근거를 제시해주는 것처럼 마무리를 합니다만, 아버지 없이 아들이 어째 태어날 수 있냐는 황당한 시간 논리로 인해서 터미네이터 제니시스는 시간여행이라는 관점에서 보면 순 엉터리라고 밖에 말할 수 없습니다.

     

    인터스텔라에서는 상대성이론과 웜홀이라는 과학적 근거를 가지고 과학적으로 그럴싸한 이야기인 것처럼 포장을 하는데 성공했다고 생각합니다. 

     

    예전 영화 백투더퓨쳐에서도 주인공이 과거로 갔다가 자신의 엄마와 아빠가 사랑에 빠지지 않을 것처럼 상황이 전개되자 주인공의 형상이 점점 흐려지는 것으로 '과거와 미래의 인과/연결고리'를 풀어놓았기에 논리적으로 그럴싸했습니다.

     

    엣지오브투마로우도 보면 시간여행의 돌고 돔 속에 마지막 부분의 여운, 어 이거 또 처음으로 돌아가나...하는 뒷맛이 괜찮았습니다.

     

    인셉션의 마지막 장면에서 팽이가 쓰러질려나? ...어 이거 혹시 꿈인가?.... 어, 이거 뭐지?  하면서 이리저리 생각하게 하는 뒷맛이 또한 괜찮았습니다.

     

    근데, 터미네이터 제니시스는 시간여행 논리면에서는 그냥 꽝이네, 역시 액션, CG 등 눈요기감 보고 예전의 1, 2의 향수 느끼는 것으로 만족해야겠구나 싶습니다.  서두에서 밝혔듯이 저는 나름 터미네이터 제니시스 재미있게 봤습니다. ^^'

     

     

    마지막으로, 시간여행(타임머신)은 가능한가에 대해 과학은 어떠 입장인지 살펴보자면,

     

    스티븐호킹이 그랬다지요. "아직까지 미래에서 온 사람이 없으니 타임머신은 존재하지 않는다"라구요.  (물론, 정신병원에 가면 미래에서 왔다고 하는 사람들이 간혹 있기는 하지만요 ^^;)

     

    아인쉬타인의 상대성이론에 의하면 "빛보다 빨리 갈 수 있어야지 만이 시간여행이 가능한데, 빛보다 빠른 것은 없으므로 시간여행을 불가능하다"고도 하지요.

     

    열역학 제2법칙인 "엔트로피의 법칙"에 의하면 "우주의 모든 에너지는 엔트로피가 증가하는 일방향으로만 흐르므로 엔트로피가 감소하는 과거로의 시간여행을 불가능"하다고도 하구요.

     

    고로, 타임머신, 시간여행 나오는 영화는 눈에 쌍심지 켜고 논리 따지면서 영화볼 일은 아니라고 말씀드릴 수 있겠습니다.  저처럼 논리 따지고 들자면 영화 재미없어지는 수가 있으니 저같은 오류는 피하시길 바래요. ^^;;;

     

    며칠전에 '타임 패러독스'라는 영화를 봤습니다.  이번 터미네이터의 시간여행 논리에 대해 쓴 패러독스를 정면 주제로 삼아 만든 영화입니다.  마지막에 엔딩 크레딧이 올라가기 직전에 가서야 영화의 전체 반전에 입이 쩍 벌어지는 영화였습니다.  한국에서는 흥행에 참패한 영화였는데요, 제가 보기엔 아마 영화 끝부분의 반전을 제대로 이해 못한 관객이 많아서 그런게 아닌가 추측해 봅니다.  영화의 반전을 알고 나면 정말 이런 시나리오를 쓸 생각을 한 작가가 정말 대단하다는 생각이 들거든요.  '타임 패러독스'는 이번 포스팅처럼 영화 내용을 가지고 글을 썼다가는 스포일러가 되어 나중에 감당 못한 원성과 원망을 살 것이므로 더 이상은 언급하지 않겠습니다.  반전, 아주 짜릿하다는 점만 한번더 말씀드리지요. ^_-

    728x90
    반응형
    Posted by Rfriend
    ,