R의 연산자 중에 %any% 식으로 해서 %가 들어간 특이한 경우를 본 적이 있으신지요?  혹시 사용자 정의 함수나 루프 연산 예제로 '홀수' 나 '짝수' 개수 구하기 등의 예제를 본 적이 있다면 '%%' 연산자를 보았을 수도 있겠습니다.  알고나면 사실 별거 아닌데요, 모르면 당최 이게 무슨 뜻일까 가늠이 안되는 연산자이기도 합니다. 그러니 한번은 봐두는게 좋겠습니다.

 

%any% 연산자의 예로 (1) 나머지 연산자 %%, (2) 정수 나누기 연산자 %/%, (3) 행렬 곱하기 연산자 %*%, (4) 벡터 내 특정 값 포함 여부 확인 연산자 %in% 의 4가지 연산자에 대해서 하나씩 예를 들어가면서 알아보도록 하겠습니다.

 

저는 앞의 3개는 사용해본적이 아직까지는 없는데요, 그래도 %in%는 나름 유용하게 잘 써먹고 있습니다.

 

 

 R %any% 특별연산자

(1) 나머지 연산자 %%

 

> # (1) 나머지 연산자 %% > x <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) > y <- c(5) > x %% y [1] 1 2 3 4 0 1 2 3 4 0 >
> # 5의 배수만 indexing 해오기 > x[x %% y == c(0)] [1] 5 10

 

x %% y 를 하게 되면 x가 y로 나누어지는 경우는 나머지가 '0'이고, 그 외에는 나머지 값이 숫자로 나오게 됩니다.  x가 1~10까지 정수이고 y가 5라고 했을 때 5의 배수만 indexing해오는 방법으로 x[x %% y == c(0)] 를 사용했는데요, x %% y == c(0) 하게 되면 5의 배수인 5와 10일때만 TRUE 가 되고, 이를 indexing해오면 5의 배수가 되겠지요.

 

 

(2) 정수 나누기 연산자 %/%

 

> x %/% y
 [1] 0 0 0 0 1 1 1 1 1 2

 

위 (1)번의 x와 y 벡터를 가지고 (2)번 예를 들어보았습니다.  %/% 연산자는 나누었을 때 '정수 몫' 만을 가져다가 결과로 나타내 줍니다. 5로 1~4까지 나누면 소수점 자리수 이므로 0, 5~9까지는 소수점 이하는 버리고 정수 몫만 취하므로 '1', 10을 5로 나누면 '2'가 되는 것이지요.

 

 

(3) 행렬 곱하기 연산자 %*%

 

> x %*% y
Error in x %*% y : non-conformable arguments
> c(1, 2, 3) %*% c(4, 5, 6)
     [,1]
[1,]   32

 

위 (1)번 x, y 벡터를 가지고 행렬 곱하기 %*%하면 'non-conformable arguments'라는 에러 메시지가 뜹니다. 행렬 곱하기를 하려고 하는데 갯수가 서로 안맞아서 그렇습니다.  구성요소가 각 각 3개씩인 두개의 벡터를 곱하려면 c(1, 2, 3) %*% c(4, 5, 6) 이렇게 입력하면 됩니다.  그러면 순서대로 곱하고 더해서, 즉, (1*4 + 2*5 + 3*6) = 4 + 10 + 18= 32 가 됩니다.

 

 

(4) 벡터 내 특정 값 포함 여부 확인 연산자 %in%

 

> # (4) 벡터 내 특정 값 포함 여부 확인 %in%
> x %in% y
 [1] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
> 

> # x 내에 y 값이 포함된 개수의 합

> sum( x %in% y)
[1] 1

 

마지막으로 벡터 내 특정 값이 포함되었는지 여부를 확인하는 %in%는 SQL이나 SAS에서 %like% 와 유사한 연산자라고 보면 되겠습니다.  R은 %in% 연산자의 결과로 TRUE, FALSE 논리형 벡터를 출력합니다.  위 (4)번 예에서는 x의 5번째 자리에 y 값 5가 하나 들어있어서 TRUE로 나왔음을 알 수 있습니다.  위의 예는 x가 단지 10개 뿐이어서 눈으로도 확인할 수 있지만 구성요소 갯수가 수천, 수만, 수백만개면 눈으로 일일이 확인하는 것은 불가능합니다.  이럴 때 sum(x %in% y) 함수를 사용하면 x 에 y가 총 몇개나 들어있는지 금방 확인할 수가 있답니다.

 

%any% 연산자에 대해서 알아보았습니다.  도움이 되었기를 바랍니다.

 

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

 

728x90
반응형
Posted by Rfriend
,

외부 텍스트 파일로 대용량의 데이터 셋을 R로 불러들이고 나면 가장 먼저 하는 것이 str()로 데이터 구조 파악하기, head(), tail()로 데이터 몇 개 미리보기, 그 다음에 하는 것이 바로 결측값 확인 및 처리, 특이값/영향치 확인 및 처리 등의 탐색적 데이터 분석입니다.

 

R에서 결측값이 들어있는 상태에서 통계 분석을 진행하면 NA 라는 결과가 나올 뿐, 원하는 결과를 얻지 못합니다.  그리고 대부분의 R 통계 함수에는 옵션으로 "na.rm = TRUE" 라는 옵션을 제공해서 결측값을 통계량 계산할 때 포함하지 말지를 선택할 수 있게 해줍니다.

 

 

이번 포스팅에서는 데이터 셋에 (1) 결측값이 포함되어 있는지 확인하는 방법, (2) 결측값이 들어있다면 결측값을 제거하는 방법, (3) 결측값을 다른 값으로 대체하는 방법에 대해서 알아보도록 하겠습니다.

 

 

 

 

 R 결측값 확인 및 처리: is.na(), na.omit(), complete.cases()

 

 

(1) 결측값이 포함되어 있는지 확인하는 방법: is.na()

 

> x <- c(1, 2, 3, 4, NA, 6, 7, 8, 9, NA)
> is.na(x)
 [1] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE

 

위의 벡터처럼 구성요소 갯수가 몇 개 안될 경우 is.na() 한 후에 TRUE, FALSE 논리형 값을 눈으로 보고 확인할 수 있습니다. 하지만 아래의 Cars93 데이터 프레임처럼 변수 갯수도 많고, 관측치 갯수도 많은 경우 (대부분의 실무에서 쓰는 데이터 셋은 이처럼 변수도 많고 관측치도 많지요) is.na() 함수만 가지고서는 아무래도 결측치 현황을 파악하는데 무리가 있습니다.

 

 

> library(MASS) > is.na(Cars93) Manufacturer Model Type Min.Price Price Max.Price MPG.city MPG.highway AirBags DriveTrain Cylinders EngineSize 1 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 2 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 3 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 4 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 5 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 6 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 7 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 8 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 9 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 10 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 11 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 12 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 13 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 14 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 15 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 16 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 17 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 18 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 19 FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE

..... (뒤에 계속 있으며, 너무 많아서 이쯤에서 중략) .....

 

 

(2) 결측값이 총 몇 개인지 계산하는 방법: sum(is.na())

 

> sum(is.na(x))
[1] 2

 

 

 

> 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 ...
 

> # Cars93 데이터 프레임에 결측값 갯수 총 합계 구하기

> sum(is.na(Cars93))
[1] 13
> 
> # Cars93 의 각 변수별로 결측값 개수 구하기 (27개 중에서 4개만 예시로 함)
> sum(is.na(Cars93$Manufacturer)) 
[1] 0
> sum(is.na(Cars93$Price))
[1] 0
> sum(is.na(Cars93$Rear.seat.room))
[1] 2
> sum(is.na(Cars93$Luggage.room))
[1] 11

 

sum(is.na()) 함수를 이용하니 변수가 많거나 관측값이 많은 경우도 결측값 현황을 금방 파악할 수 있습니다.  R은 TRUE 를 '1'로, FALSE 를 '0'으로 인식하기 때문에 sum(is.na())를 하게 되면 TRUE 값을 '1'로 해서 합계를 내기 때문에 가능한 함수가 되겠습니다.



colSums() 함수를 사용하면 데이터 프레임 내 다수 변수들에 대해서 한번에 각 개별 변수별 결측값의 개수 합계를 구할 수 있습니다. 바로 위에서 개별 함수별로 일일이 sum(is.na(Cars93$Manufacturer))...이런 식으로 변수의 개수만큼 쓰는 것을 colSums() 함수로는 한줄이면 해결할 수 있으니 훨씬 편합니다. 


> colSums(is.na(Cars93))

      Manufacturer              Model               Type          Min.Price              Price 

                 0                  0                  0                  0                  0 

         Max.Price           MPG.city        MPG.highway            AirBags         DriveTrain 

                 0                  0                  0                  0                  0 

         Cylinders         EngineSize         Horsepower                RPM       Rev.per.mile 

                 0                  0                  0                  0                  0 

   Man.trans.avail Fuel.tank.capacity         Passengers             Length          Wheelbase 

                 0                  0                  0                  0                  0 

             Width        Turn.circle     Rear.seat.room       Luggage.room             Weight 

                 0                  0                  2                 11                  0 

            Origin               Make 

                 0                  0  


 

 

(3) 결측값을 통계 분석 시 제외(미포함): na.rm = TRUE

 

> sum(x)
[1] NA
> mean(x)
[1] NA
> sum(x, na.rm = TRUE)
[1] 40
> mean(x, na.rm = TRUE)
[1] 5

 

결측값이 들어있는 벡터에 대해서 sum(), mean(), sd(), min(), max(), range() 등의 통계 함수를 적용하면 'NA'만 나오게 됩니다. 결측값을 포함하지 말고 통계 함수 계산을 하라는 옵션이 na.rm = TRUE 가 되겠습니다.

 

> sum(Cars93$Luggage.room)
[1] NA
> mean(Cars93$Luggage.room)
[1] NA
> 
> sum(Cars93$Luggage.room, na.rm = TRUE)
[1] 1139
> mean(Cars93$Luggage.room, na.rm = TRUE)
[1] 13.89024

 

위 예제는 결측값이 포함된 데이터 프레임의 특정 변수에 대해 indexing을 해서 통계 함수를 적용해본 경우 입니다. 역시 na.rm = TRUE 옵션을 설정해 주어야 통계 계산이 제대로 됨을 알 수 있습니다.  na.rm =  FALSE 가 디폴트이다보니 na.rm=TRUE를 설정하지 않는 경우 결측값이 포함되어 있으면 NA가 결과로 나타나게 됩니다.

 

 

 

(4) 결측값이 들어있는 행 전체를 데이터 셋에서 제거: na.omit()

 

 na.rm = TRUE 옵션은 원래의 데이터 셋은 그대로 둔채 통계량 계산할 때만 포함하지 않게 됩니다. 따라서 다수의 통계 함수 혹은 다수의 변수에 통계 함수를 사용해야 하는 경우 매번 na.rm = TRUE 옵션을 설정해주는게 번거로울 수 있겠지요?  차라리 원래 데이터 셋에서 결측값을 제거해버리면 되겠다는 생각이 드셨을 겁니다.

 

결측값이 들어있는 행을 통째로 무식하게 제거하는 함수가 na.omit()이며, 좀더 예리하게 특정 행과 열을 지정해서 그곳에 결측값이 있는 경우만 메스로 정밀 수술하는 함수가 complete.cases()가 되겠습니다.

 

> Cars93_1 <- na.omit(Cars93)
> str(Cars93_1)
'data.frame':	82 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 ...
 - attr(*, "na.action")=Class 'omit'  Named int [1:11] 16 17 19 26 36 56 57 66 70 87 ...
  .. ..- attr(*, "names")= chr [1:11] "16" "17" "19" "26" ..

 

 

처음에 Cars93이  "'data.frame': 93 obs. of  27 variables:", 즉 93개의 관측치가 있었는데요,

na.omit(Cars93) 함수를 적용한 후에 Cars93_1 이라는 이름으로 새로 저장해서 str()로 데이터 구조를 보니 "'data.frame': 82 obs. of  27 variables:", 즉 82개 관측치로 총 11개 관측치가 줄어들었음을 알 수 있습니다. 결측값이 하나라도 들어있는 행 11개가 통째로 삭제되어 버렸기 때문입니다.

 

위의 예처럼 결측값이 들어있는 행을 통째로 삭제할 때는 만약의 사태를 대비해서 원본은 그대로 유지하고, 행을 삭제한 데이터 셋을 별도의 이름으로 저장해서 분석을 진행하는 것을 추천합니다.

 

 

 

(5) 특정 행과 열에 결측값이 들어있는 행을 데이터 셋에서 제거 : complete.cases()

 

> sum(is.na(Cars93))
[1] 13
> 

> # Cars93 데이터 프레임의 "Rear.seat.room" 칼럼 내 결측값이 있는 행 전체 삭제

> Cars93_2 <- Cars93[ complete.cases(Cars93[ , c("Rear.seat.room")]), ] > sum(is.na(Cars93_2)) [1] 9 >

> # Cars93 데이터 프레임의 23~24번째 칼럼 내 결측값이 있는 행 전체 삭제

> Cars93_3 <- Cars93[ complete.cases(Cars93[ , c(23:24)]), ] > sum(is.na(Cars93_3)) [1] 0 

> 
> dim(Cars93_3) # 관측값이 82개로서 11개 줄어듬
[1] 82 27

> str(Cars93_3) # 관측값이 82개로서 11개 줄어듬 'data.frame': 82 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 ...

 

 

(6) 결측값을 다른 값으로 대체: dataset$var[is.na(dataset$var)] <- new_value

 

> Cars93$Luggage.room [1] 11 15 14 17 13 16 17 21 14 18 14 13 14 13 16 NA NA 20 NA 15 14 17 11 13 14 NA 16 11 11 15 12 12 13 12 18 NA 18 [38] 21 10 11 8 12 14 11 12 9 14 15 14 9 19 22 16 13 14 NA NA 12 15 6 15 11 14 12 14 NA 14 14 16 NA 17 8 17 13 [75] 13 16 18 14 12 10 15 14 10 11 13 15 NA 10 NA 14 15 14 15 > sum(is.na(Cars93$Luggage.room)) [1] 11

> 

> # Luggage.room 변수 내 결측값을 '0'으로 대체

> Cars93_4 <- Cars93
> Cars93_4$Luggage.room[is.na(Cars93_4$Luggage.room)] <- 0
> Cars93_4$Luggage.room
 [1] 11 15 14 17 13 16 17 21 14 18 14 13 14 13 16  0  0 20  0 15 14 17 11 13 14  0 16 11 11 15 12 12 13 12 18  0 18
[38] 21 10 11  8 12 14 11 12  9 14 15 14  9 19 22 16 13 14  0  0 12 15  6 15 11 14 12 14  0 14 14 16  0 17  8 17 13
[75] 13 16 18 14 12 10 15 14 10 11 13 15  0 10  0 14 15 14 15

 

 

 

sum(is.na(Cars93$Luggage.room)) 함수를 사용해 Cars93의 Luggage.room 변수에 보면 11개의 결측값이 있음을 알 수 있습니다.  Luggage.room 변수 내 결측값을 indexing 기법을 활용해서 '0'로 대체하는 방법이 위의 예제가 되겠습니다.

 

물론 '0'이 아니라 다른 값으로도 대체가 가능합니다.  아래의 예제에서는 결측값을 미포함(na.rm = TRUE)했을 때의 Luggage.room의 평균값으로 결측값을 대체하여 보도록 하겠습니다.

 

> Cars93_5 <- Cars93
> Cars93_5$Luggage.room[is.na(Cars93_5$Luggage.room)] <- mean(Cars93_5$Luggage.room, na.rm = TRUE)
> sum(is.na(Cars93_5$Luggage.room))
[1] 0
> Cars93_5$Luggage.room
 [1] 11.00000 15.00000 14.00000 17.00000 13.00000 16.00000 17.00000 21.00000 14.00000 18.00000 14.00000 13.00000
[13] 14.00000 13.00000 16.00000 13.89024 13.89024 20.00000 13.89024 15.00000 14.00000 17.00000 11.00000 13.00000
[25] 14.00000 13.89024 16.00000 11.00000 11.00000 15.00000 12.00000 12.00000 13.00000 12.00000 18.00000 13.89024
[37] 18.00000 21.00000 10.00000 11.00000  8.00000 12.00000 14.00000 11.00000 12.00000  9.00000 14.00000 15.00000
[49] 14.00000  9.00000 19.00000 22.00000 16.00000 13.00000 14.00000 13.89024 13.89024 12.00000 15.00000  6.00000
[61] 15.00000 11.00000 14.00000 12.00000 14.00000 13.89024 14.00000 14.00000 16.00000 13.89024 17.00000  8.00000
[73] 17.00000 13.00000 13.00000 16.00000 18.00000 14.00000 12.00000 10.00000 15.00000 14.00000 10.00000 11.00000
[85] 13.00000 15.00000 13.89024 10.00000 13.89024 14.00000 15.00000 14.00000 15.00000

 

 

 

 

 

(7) 데이터프레임의 모든 행의 결측값을 특정 값(가령, '0')으로 일괄 대체 

: dataset[is.na(dataset)] <- 0

 

> Cars93_6 <- Cars93
> # counting the number of missing values in Cars93 dataset
> sum(is.na(Cars93_6)) # 13
[1] 13
> 
> # converting the missing value in Cars93 dataframe to '0'
> Cars93_6[is.na(Cars93_6)] <- 0
> 
> # counting the number of missing values in Cars93 dataset
> sum(is.na(Cars93_6)) # 0
[1] 0

 

 

 

(8) 데이터프레임의 각 변수의 결측값을 각 변수 별 평균값으로 일괄 대체

: sapply(dataset, function(x) ifelse(is.na(x), mean(x, na.rm=TRUE), x)) 

 

위의 (6)번에서 결측값을 그 열의 평균으로 대체하는 방법을 소개했는데요, 만약 결측값을 포함한 열이 매우 많다면 일일이 변수이름을 지정해가면서 나열해서 입력하기가 번거롭습니다. 이럴 때 sapply() 함수를 사용하면 일괄로 결측값을 포함한 변수에 대해서는 해당 변수의 평균으로 대체하라고 프로그래밍할 수 있습니다. sapply()를 적용하면 matrix를 반환하므로 dataframe으로 만들기 위해서 앞에 data.frame() 을 추가로 붙여주었습니다.

 

 

> # converting the missing value in Cars93 dataframe to each column's mean value
> Cars93_7 <- Cars93[1:20,c("Rear.seat.room", "Luggage.room")]
> 
> colSums(is.na(Cars93_7))
Rear.seat.room   Luggage.room 
             1              3 
> 
> Cars93_7
   Rear.seat.room Luggage.room
1            26.5           11
2            30.0           15
3            28.0           14
4            31.0           17
5            27.0           13
6            28.0           16
7            30.5           17
8            30.5           21
9            26.5           14
10           35.0           18
11           31.0           14
12           25.0           13
13           26.0           14
14           25.0           13
15           28.5           16
16           30.5           NA
17           33.5           NA
18           29.5           20
19             NA           NA
20           31.0           15
> 
> sapply(Cars93_7, function(x) mean(x, na.rm=T))
Rear.seat.room   Luggage.room 
      29.10526       15.35294 
> 
> 
> Cars93_7 <- data.frame(sapply(Cars93_7, 
+                               function(x) ifelse(is.na(x), 
+                                                  mean(x, na.rm = TRUE), x)))
> 
> Cars93_7
   Rear.seat.room Luggage.room
1        26.50000     11.00000
2        30.00000     15.00000
3        28.00000     14.00000
4        31.00000     17.00000
5        27.00000     13.00000
6        28.00000     16.00000
7        30.50000     17.00000
8        30.50000     21.00000
9        26.50000     14.00000
10       35.00000     18.00000
11       31.00000     14.00000
12       25.00000     13.00000
13       26.00000     14.00000
14       25.00000     13.00000
15       28.50000     16.00000
16       30.50000     15.35294
17       33.50000     15.35294
18       29.50000     20.00000
19       29.10526     15.35294
20       31.00000     15.00000

 

 

 


(9) 그룹 별 평균값으로 결측값 대체하기 (filling missing values by group mean)


base 패키지를 사용할 수도 있긴 한데요, dplyr 패키지의 group_by 와 ifelse 조건문을 사용한  mutate 함수로 그룹 별 평균값으로 결측값 채우는 방법을 소개하겠습니다. 


> # make a sample DataFrame

> grp <- c(rep('a', 5), rep('b', 5))

> val <- c(1, 2, 3, NaN, 6, 2, 4, NaN, 10, 8)

> df <- data.frame(grp, val)

> df

   grp val

1    a   1

2    a   2

3    a   3

4    a NaN

5    a   6

6    b   2

7    b   4

8    b NaN

9    b  10

10   b   8



결측값을 제외하고 그룹 'a'와 그룹 'b' 별 평균을 계산해보겠습니다. 


> # mean value by group 'a' and 'b'

> library(dplyr)

> df %>% group_by(grp) %>% summarise(grp_mean = mean(val, na.rm = TRUE))

# A tibble: 2 x 2

  grp   grp_mean

  <fct>    <dbl>

1 a            3

2 b            6



그룹 'a'의 평균은 3, 그룹 'b'의 평균은 6이군요. 그러면 4번째 행에 있는 그룹 'a'의 'val' 칼럼 결측값을 그룹 'a'의 평균 3으로 대체(replace) 또는 채워넣기(fill in)를 해보겠습니다. 그리고 8번째 행에 있는 그룹 'b'의 'val' 칼럼 결측값을 그룹 'b'의 평균 6으로 대체해보겠습니다. 


> df %>% 

+   group_by(grp) %>% 

+   mutate(val = ifelse(is.na(val), mean(val, na.rm=TRUE), val))

# A tibble: 10 x 2

# Groups:   grp [2]

   grp     val

   <fct> <dbl>

 1 a         1

 2 a         2

 3 a         3

 4 a         3

 5 a         6

 6 b         2

 7 b         4

 8 b         6

 9 b        10

10 b         8 


 

--------------------

 

우에서 NA(Not Available)에 대해서 소개를 했는데요, 참고로 벡터에서 NaN (Not a Number), 무한값 Inf (Infinity), 유한값 finite 확인하는 방법도 마저 소개하겠습니다.

 

유의할 것은, is.na()에서 NA 뿐만 아니라 NaN 도 TRUE 를 반환합니다.

 

 

> my_data <- c(-1, 0, 10, NA, NaN, Inf)
>
> my_data
[1]  -1   0  10  NA NaN Inf
>
> is.finite(my_data)
[1]  TRUE  TRUE  TRUE FALSE FALSE FALSE
>
> is.na(my_data)
[1] FALSE FALSE FALSE  TRUE  TRUE FALSE
>
> is.nan(my_data)
[1] FALSE FALSE FALSE FALSE  TRUE FALSE
>
> is.infinite(my_data)
[1] FALSE FALSE FALSE FALSE FALSE  TRUE

 

 


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


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

 


728x90
반응형
Posted by Rfriend
,

이전 포스팅들 중에 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
,

R 데이터 구조(structure), 유형(class) 에 대해서 예전에 포스팅을 했던 적이 있습니다.

 

R 데이터 구조(structure)에는 스칼라(scala), 벡터(vector), 요인(factor), 행렬(matrix), 데이터프레임(dataframe), 리스트(list) 가 있다고 소개를 해드렸습니다. (☞ R 데이터 구조 포스팅 보러가기)

 

그리고 R 데이터 유형(class)에는 숫자형 (numeric), 문자형 (character), 요인형 (factor) 등이 있다고 말씀드렸습니다. 오늘 포스팅에서는 R 데이터 객체의 유형을 (1) 확인, (2) 전환 하는 함수에 대해서 알아보도록 하겠습니다.

 

숫자형, 문자형에 대해서는 쉽게 알 수 있으므로, 요인형(factor)이 뭐고 왜 필요한 지에, 어디에 써 먹는지에 대해서 먼저 간략히 소개를 드린 후에 객체 유형 확인, 전환 함수로 넘어가도록 하겠습니다.  왜 공부해야 하는지도 모르면서 진도를 빼면 다 배우고 나서 'so what?' 하겠지요?  (물론, 나중에 통계분석, 그래프 쪽 가면 그때 가서 '아, 이래서 이거 배웠었구나...'하고 한번 더 복습하시면 되겠습니다.)

 

 

요인(factor)은 무엇이고, 어디에 써먹나? 

 

일단, 요인(factor)은 "~별 숫자형 벡터의 평균, ~별 숫자형 벡터의 합계" 등과 같이 요약 통계량을 집단이나 특성에 따라서 분석할 때 사용합니다. OLAP으로 치자면 '차원(dimension)' 역할을 하는 것이 되겠습니다.

 

MASS 패키지에 내장된 Cars93 데이터 프레임 내 변수들을 가지고 예를 들어보겠습니다.  

자동차 Type(Levels: Compact, Large, MidsizeSmall, Sporty, Van) 요인별로 고속도로 연비(MPG.highway)의 평균(mean)을 계산해보도록 하겠습니다.

 

> library(MASS) > str(Cars93) # 데이터 구조 데이터 프레임, 관측치 93개, 변수 27개 '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 ...




 
> head(Cars93) # 상위 6개 관측치
  Manufacturer   Model    Type Min.Price Price Max.Price MPG.city MPG.highway            AirBags DriveTrain
1        Acura Integra   Small      12.9  15.9      18.8       25          31               None      Front
2        Acura  Legend Midsize      29.2  33.9      38.7       18          25 Driver & Passenger      Front
3         Audi      90 Compact      25.9  29.1      32.3       20          26        Driver only      Front
4         Audi     100 Midsize      30.8  37.7      44.6       19          26 Driver & Passenger      Front
5          BMW    535i Midsize      23.7  30.0      36.2       22          30        Driver only       Rear
6        Buick Century Midsize      14.2  15.7      17.3       22          31        Driver only      Front
  Cylinders EngineSize Horsepower  RPM Rev.per.mile Man.trans.avail Fuel.tank.capacity Passengers Length Wheelbase
1         4        1.8        140 6300         2890             Yes               13.2          5    177       102
2         6        3.2        200 5500         2335             Yes               18.0          5    195       115
3         6        2.8        172 5500         2280             Yes               16.9          5    180       102
4         6        2.8        172 5500         2535             Yes               21.1          6    193       106
5         4        3.5        208 5700         2545             Yes               21.1          4    186       109
6         4        2.2        110 5200         2565              No               16.4          6    189       105
  Width Turn.circle Rear.seat.room Luggage.room Weight  Origin          Make
1    68          37           26.5           11   2705 non-USA Acura Integra
2    71          38           30.0           15   3560 non-USA  Acura Legend
3    67          37           28.0           14   3375 non-USA       Audi 90
4    70          37           31.0           17   3405 non-USA      Audi 100
5    69          39           27.0           13   3640 non-USA      BMW 535i
6    69          41           28.0           16   2880     USA Buick Century
>

> > # 함수 명령문 형식: with(dataset, tapply(numeric vector, factor, function)) > 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

 

 

 

그래프를 그릴 때도 요인(factor)별로 구분해서 그린 후에 비교를 해보면 인사이트를 뽑아내는데 유용하게 사용할 수 있습니다. ggplot2 패키지를 이용해서 자동차 Type(Levels: Compact, Large, MidsizeSmall, Sporty, Van) 요인별로 고속도로 연비(MPG.highway)의 히스토그램을 그려보도록 하겠습니다.

 

> install.packages("ggplot2") # ggplot2 패키지 설치

> library(ggplot2) # ggplot2 패키지 호출

> # 명령식 형식: qplot(numeric vector, data=dataset, facets=factor~. , binwidth=n) > qplot(MPG.highway, data=Cars93, facets = Type~. , binwidth=2)

 

 

 

 

 그래프의 오른쪽에 보면 6개의 자동차 Type별로 구분이 되어서 히스토그램이 그려졌음을 알 수 있습니다.

 

위의 2개의 예에서 보는 것처럼 요인(factor)이 통계분석, 그래프 분석에 유용하게 쓰이므로, 반드시 요인(factor)으로 데이터 객체를 관리해야 하는것이 생깁니다. '요인'이 들어와 있어야 할 자리에 '숫자형'이나 '문자형'이 들어와 있으면 '요인형'을 넣어서 써야 하는 함수 명령문이 안 먹겠지요.  반면에, 문자(character)나 숫자(numeric)로 인식을 해야 하는게 있는데 R에 요인(factor)로 잘못 인식이 된 경우도 있을 수 있으며, 이런 경우 에러가 나는 주요 범인이 되곤 합니다. 

 

그래서 데이터셋을 받으면 제일 처음 하는 일이 str(), head()로 데이터셋의 구조, 변수, 관측치 등을 살피고, 그 다음으로 class()로 데이터 객체별 속성을 확인해보는 것이 반드시 필요합니다. 그리고 분석가가 원하는 속성으로 데이터가 안들어가 있다면 속성을 분석 목적에 맞게 전환해줄 필요가 생깁니다.

 

 

 

데이터 속성 확인 및 속성 전환 

 

 데이터 속성 확인 및 속성 전환에 대해 알아보기 위해 아래의 텍스트 파일을 불러와서 예를 들어보겠습니다.

 

(실습 파일 아래 다운로드 ☞ ) 

cust_profile_2.txt

 

불러오고 나면 이렇게 생긴 데이터 프레임이 되겠습니다

 

데이터셋의 성격/분석 목적 상 'cust_id'는 character, 'last_name'도 character, 'age'는 numeric, 'gender'는 factor 로 입력되어야 합니다.

그런데 데이터셋이 실제로 어떻게 입력되었는지 2개의 case로 나누어서 확인해보도록 하겠습니다.

 

 

 (1) stringsAsFactor = FALSE   로 한 경우 (요인으로 인식하지 말라는 옵션)

 

> # 외부 텍스트 파일 불러오기 (위에 파일 링크 다운로드 해서 사용 가능)

> cust_profile_1 <- read.table("C:/Users/user/Documents/R/cust_profile_2.txt", + header = TRUE, + sep = ",", + stringsAsFactor = FALSE, + na.strings = "") >

> is.character(cust_profile_1$cust_id) # 문자형 여부 속성 확인 [1] FALSE

> is.character(cust_profile_1$last_name) [1] TRUE > is.numeric(cust_profile_1$age) # 숫자형 여부 속성 확인 [1] FALSE > is.factor(cust_profile_1$gender) # 요인형 여부 속성 확인 [1] FALSE > > sapply(cust_profile_1, class) # 한꺼번에 cust_profile_1 데이터셋에 class() 함수를 적용하라는 명령문 cust_id last_name age gender "integer" "character" "character" "character"

 

 

'last_name'만 character로 원하는 속성으로 들어가 있고, 나머지는 R이 저의 맘을 몰라주고 R 마음대로 데이터 속성이 들어가 있습니다.

'gender'는 남/녀 성별 통계분석이나 그래프 그릴 때 요인(factor)으로 요긴하게 써먹어야 하므로 문자형(character)를 요인형(factor)로

as.factor() 함수를 써서 전해보도록 하겠습니다.

 

> cust_profile_1$gender <- as.factor(cust_profile_1$gender)
> is.factor(cust_profile_1$gender)
[1] TRUE



요인형의 값으로서, 미리 정해진(predefined), 한정된 수의 범주형 값(finite number of categorical values)을 수준(levels) 이라고 합니다. 요인 데이터의 수준을 확인하거나 지정, 변경하려면 levels() 함수를 사용합니다.  


> # level check

> levels(cust_profile_1$gender)

[1] " F" " M"

> # change levels' name from {"F", "M"} to {"FEMALE", "MALE"}

> levels(cust_profile_1$gender) <- c("FEMALE", "MALE")

> levels(cust_profile_1$gender)

[1] "FEMALE" "MALE"  



혹은 factor() 함수labels 옵션을 사용해서 아래처럼 할 수도 있습니다. 



> cust_profile_1$gender <- factor(cust_profile_1$gender, labels = c("FEMALE", "MALE")

 




다음으로, stringsAsFactor = TRUE 로 했을 때는 어떻게 데이터를 불어오는지 알아보겠습니다.

 

 

 (2) stringsAsFactor = TRUE   로 한 경우 (요인으로 인식하라는 옵션)

 

> cust_profile_2 <- read.table("C:/Users/user/Documents/R/cust_profile_2.txt", 
+                              header = TRUE, 
+                              sep = ",", 
+                              stringsAsFactor = TRUE, 
+                              na.strings = "")
> 
> sapply(cust_profile_2, class)
  cust_id last_name       age    gender 
"integer"  "factor"  "factor"  "factor" 
> 
> 
> cust_profile_2$cust_id <- as.character(cust_profile_2$cust_id)
> cust_profile_2$age <- as.numeric(cust_profile_2$age)
> sapply(cust_profile_2, class)
    cust_id   last_name         age      gender 
"character"    "factor"   "numeric"    "factor"

 

 

'gender'는 요인(factor)로 제대로 불러왔는데요, 명목형 변수인 'cust_id'는 문자형(character)가 아닌 숫자형(interger)로, 'last_name'을 문자형(character)가 아닌 요인형(factor)로, 'age'를 숫자형(numeric)이 아닌 요인형(factor)로 잘못 불러왔습니다.

 

자, 이제 아래 표 처럼 이터 특성/분석 목적에 맞게 데이터 속성을 전환 시켜 보도록 하겠습니다.

 

 변수명

 기존 속성

전환 후 속성

 속성 전환 함수

 cust_id

numeric

 character

 as.character()

 last_name

 factor

 character

 as.character()

 age

 factor

 numeric

 as.numeric()

 gender

 factor

 factor

 as.factor()

 

'gender'는 이미 요인형(factor)로 제대로 잘 들어가 있기 때문에 아래 예시에서는 별도로 속성 전환하지는 않았습니다.  

 

> cust_profile_2$cust_id <- as.character(cust_profile_2$cust_id)
> cust_profile_2$last_name <- as.character(cust_profile_2$last_name)
> cust_profile_2$age <- as.numeric(cust_profile_2$age)
>

> sapply(cust_profile_2, class) cust_id last_name age gender "character" "character" "numeric" "factor"

 

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

 

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

 

728x90
반응형
Posted by Rfriend
,

미스터리 음악쇼 복면가왕 16회가 7/19(일) 저녁에 있었습니다. 10주 만에 '화생방실 클레오파트라'가 복면가왕 가면을 벗었고, '노래왕 퉁키'는 새로운 복면가왕으로 등극하였습니다.

 

'죠스가 나타났다'도 정말 잘했는데 '노래왕 퉁키' 너무 잘했죠. 그리고 '화생방실 클레오파트라' 역시 4연속 가왕으로서 '한오백년'과 '진도아리랑'을 가지고 관객과 시청자를 홀렸습니다만, 관객석을 콘서트장으로 만든 '노래왕 퉁키'가 새로운 복면가왕이 되었습니다.  이번 회차는 노래 듣는 내내 참 귀가 호강했습니다.

 

 

 

새로운 복면가왕 '노래왕 퉁키'

콘서트장을 방불케하는 카리스마!

 

 

 

한오백년으로 전혀 새로운 장르에 도전하며 가왕 타이틀 방어전에 임하는 화생방실 클레오파트라

 

 

 

8대 복면가왕은 간발의 표 차이로 '노래왕 퉁키'

 

 

 

10주만에 새 복면가왕 탄생에 놀라워하는 심사단

 

* 화면 출처: MBC 복면가왕 16회차 방송 사진 캡쳐

 

 

 

16회차 복면가수들의 노래에 대한 후기는 다른 많은 분들이 포스팅을 할 것이라 예상이 되구요, 저는 이번 포스팅에서 "대진 순서가 승패에 영향을 미칠까?"라는 질문(가설)에 대해서 통계적으로 검증을 해보았습니다.

 

14회차 Final Round에서 '소녀감성 우체통(린)'과 '내 칼을 받아라 낭만자객(김보아)'가 맞붙었는데요, 낭만자객이 52표, 우체통이 47표를 얻어서 낭만자객이 클레오파트라와 복면가왕전을 벌였고, 클레오파트라 승으로 끝났었죠. 

 

14회차 Final Round 때 둘다 잘하기는 했지만, 저는 '소녀감성 우체통'에 조금 더 마음이 끌리더라구요. 그런데 낭만자객이 근소한 표 차이로 승리를 했는데요, '그게 혹시 노래 순서 때문은 아닐까?', '뒤에 노래하는 복면가수일 수록 심사 관객/심사원의 뇌리에 인상이 더 크게 남아 있어서 더 유리한 것은 아닐까?' 하는 생각이 들었습니다. 그래서 .... 인터넷 뒤져가면서 1회부터 28회까지 대진표랑 승패 결과를 데이터로 정리해서 카이제곱 독립성 검정을 해보았습니다.

 

결론부터 말씀드리자면, "대진 순서는 10% 유의수준 하에서 승패에 영향이 있다"로 나왔습니다.

 

 

분석에 활용한 데이터는 1회~28회까지 매 회의 2 round, 3round 의 대진 순서와 승패 결과 데이터를 활용하였습니다.

아래 15, 16회차를 예로 들면 황색으로 테두리 친 영역이 분석 대상이 되겠습니다.  

 

 

 

 

데이터셋은 바로 아래에 링크 걸어놓습니다. (자료 모으고 정리하느라 시간 꽤 걸렸습니다. ㅜ_ㅠ) 

 

 

 

masksinger_win_lose_20151011.csv

 

(17회, 18회, 19회, 20회, 21회, 22회, 23회, 24회, 25회, 26회, 27회, 28회 업데이트 한 자료 새로 올립니다)

 

 

귀무가설 H0 : "노래 대진 순서는 승패에 영향이 없다"

대립가설 H1 : "노래 대진 순서는 승패에 영향이 있다"

 

라는 가설 검정을 위해서 R 오픈소스 통계툴을 활용해서 이변량 범주형 데이터의 독립성을 10% 유의수준 하에서 카이제곱 검정했습니다.

다음은 R에서 데이터 불러오고, 분할표 만들고, 카이제곱 검정 옵션 줘서 검정하라는 R 명령문입니다.

 

> masksinger_win_lose <- read.csv("C:/Users/user/Documents/R/masksinger_win_lose_20151011.csv", + header = TRUE)

>

> install.packages("gmodels") # gmodels 패키지 설치

> library(gmodels) # 패키지 호출 > str(masksinger_win_lose) # 데이터셋 구조 탐색, 60개의 관측치, 5개 변수를 가진 데이터 프레임 'data.frame': 84 obs. of 5 variables: $ broadcast: Factor w/ 10 levels "1_2","11_12",..: 1 1 1 1 1 1 7 7 7 7 ... $ round : int 2 2 2 2 3 3 2 2 2 2 ... $ singer : Factor w/ 40 levels "7월의크리스마스",..: 24 5 40 8 5 40 33 2 9 28 ... $ sequence : Factor w/ 2 levels "1st","2nd": 1 2 1 2 1 2 1 2 1 2 ... $ win_lose : Factor w/ 2 levels "lose","win": 1 2 2 1 1 2 2 1 1 2 ...

 

 

아래는 노래 대진 순서(1st, 2nd)와 승패 결과 (lose, win) 분할표 결과 입니다.

'2번째(2nd)'로 노래를 했으면서 승리(win)할 기대도수(Expected N)은 '21'인데 실제는 '25' 이고, 패배(lose)할 기대도수(Expected N)도 '21'인데 실제는 '17'이 나왔다는 뜻입니다.  

피어슨 카이제곱 검정 통계량은 '3.047619'이고, p-value는 0.0808이므로, 유의수준 10% 하에서 귀무가설 H0 : "노래 대진 순서는 승패에 영향이 없다"를 기각하고, 대립가설 H1: "노래 대진 순서는 승패에 영향이 있다"를 채택하게 됩니다.

 

 

 

> masksinger_win_lose <- read.csv("C:/Users/user/Documents/R/0_분석 관점으로 세상보기/02_복면가왕/masksinger_win_lose_20151011.csv", 
+                                 header = TRUE)
> CrossTable(masksinger_win_lose$sequence, masksinger_win_lose$win_lose, 
+            expected = TRUE, 
+            chisq = TRUE)

 
   Cell Contents
|-------------------------|
|                       N |
|              Expected N |
| Chi-square contribution |
|           N / Row Total |
|           N / Col Total |
|         N / Table Total |
|-------------------------|

 
Total Observations in Table:  84 

 
                             | masksinger_win_lose$win_lose 
masksinger_win_lose$sequence |      lose |       win | Row Total | 
-----------------------------|-----------|-----------|-----------|
                         1st |        25 |        17 |        42 | 
                             |    21.000 |    21.000 |           | 
                             |     0.762 |     0.762 |           | 
                             |     0.595 |     0.405 |     0.500 | 
                             |     0.595 |     0.405 |           | 
                             |     0.298 |     0.202 |           | 
-----------------------------|-----------|-----------|-----------|
                         2nd |        17 |        25 |        42 | 
                             |    21.000 |    21.000 |           | 
                             |     0.762 |     0.762 |           | 
                             |     0.405 |     0.595 |     0.500 | 
                             |     0.405 |     0.595 |           | 
                             |     0.202 |     0.298 |           | 
-----------------------------|-----------|-----------|-----------|
                Column Total |        42 |        42 |        84 | 
                             |     0.500 |     0.500 |           | 
-----------------------------|-----------|-----------|-----------|

 
Statistics for All Table Factors


Pearson's Chi-squared test 
------------------------------------------------------------
Chi^2 =  3.047619     d.f. =  1     p =  0.0808556 

Pearson's Chi-squared test with Yates' continuity correction 
------------------------------------------------------------
Chi^2 =  2.333333     d.f. =  1     p =  0.1266305

 

 

 

심사단의 투표 결과가 박빙일 때는 노래 순서가 영향을 미치지 않을까...하고 지금도 의혹을 가지고 있기는 합니다만, 복면가왕 보시는 분들은 아시겠지만, 진짜 가수와 아마추어 (개그맨, 탤런드, 아나운서 등)가 나와서 대진을 하면 실력차이가 나기 때문에 실력있는 가수 쪽으로 쏠림이 심한 경우가 많았습니다. 이렇게 실력차가 확연해서 쏠림이 심하면 '대진 순서'는 의미가 퇴색되겠지요. (다 아는 내용인데 미련한 저만 몰랐던 걸까요? 똥인지 된장인지 꼭 찍어먹어봐야 아는.... ㅋㅋ)

 

암튼, "노래 대진 순서는 승패에 영향이 없다"가 그동안의 1회~26회까지의 결과를 기반으로 통계분석를 통해 도출한 결론이 되겠습니다.

 

앞으로 몇 회 더 추적해서 분석을 업데이트 하도록 하겠습니다.

 

16회 때 이 블로그를 썼었는데요, 그때보다 회를 거듭할 수록 제가 세웠던 대립가설, 즉 "대진 순서가 승패에 영향이 있을 것이다 (두번째 노래한 가수의 승률이 더 높을 것이다)"라는 가설의 통계적 유의성이 점점 더 높아지고 있습니다.  두어달 더 꾸준히 트래킹해보면서 더 지켜보겠습니다.

 

그동안 아슬아슬하게 대립가설을 채택 못했는데요, 드디어 28회차에 뒤집혀서 10% 유의수준 하에  대진순서가 승패에 영향이 있다는 대립가설을 채택할 수 있게 되었습니다.  매주 데이터 수집, 정리하느라 시간이 솔찬히 걸렸는데요, 보람이 있었네요. ^^v

 

시간 순서에 따른 평가의 왜곡(후광효과?)을 줄이기 위해서는 첫번째 복면가수가 노래를 부르고 나서 바로 점수를 입력하게 하고, 두번째 복면가수가 노래를 부르면 또 바로 입력하게 하는 것이 필요할 것 같습니다.  그리고 평가를 할 때 몇 가지 평가요소를 세분화(예: 목소리, 리듬, 무대 퍼포먼스 등)해 놓고 평가를 하게 하면 왜곡을 줄일 수 있을 것 같습니다. 

 

회사에서도 연말에 한번 몰빵으로 평가를 하는 것이 아니라, 월이나 분기마다 평가를 하고 자주 피드백을 하는 것이 필요하거든요.  그리고 평가 항목을 직급, 업무에 따라 세분화, 구체화하구요.

 

아래는 복면가왕 역대 대진표와 승자를 정리한 내용이 되겠습니다. (데이터는 위의 본문 중간에 링크)

 


 

[복면가왕 1회, 2회 대진표 ('15.4.5~4.12)]

 

 


 

[복면가왕 3회, 4회 대진표 ('15.4.19~4.26)]

 

  


 

[복면가왕 5회, 6회 대진표 ('15.5.3~5.10)]

 

 


 

[복면가왕 7회, 8회 대진표 ('15.5.17~5.24)]

 

 


 

[복면가왕 9회, 10회 대진표 ('15.5.31~6.7)]

 

 


 

[복면가왕 11회, 12회 대진표 ('15.6.14~6.21)]

 

 


 

 [복면가왕 13회, 14회 대진표 ('15.6.28~7.5)]

 


 

[ 복면가왕 15회, 16회 대진표 ('15.7.12~7.19) ]

 

 

 


 

[ 복면가왕 17회, 18회 대진표 ('15.7.26~8.2) ]

 

 

 


 

[복면가왕 19회, 20회 대진표 ('15.8.9~8.16) ]

 

 

 

 

 


 

[ 복면가왕 21회, 22회 대진표 ('15.8.23~8.30) ]

  

 

 

 


 

[ 복면가왕 23회, 24회 대진표 ('15.9.6~9.13) ]

 

 

 

 

 


 

[ 복면가왕 25회, 26회 대진표 ('15.9.20~9.27) ]

 

 

 


 

[ 복면가왕 27회, 28회 대진표 ('15.10.04~10.11) ]

 

728x90
반응형
Posted by Rfriend
,

R의 장점이자 특징 중의 하나가 벡터 연산이 가능해서 프로그램을 깔끔하게 짤 수 있고 연산처리 속도가 빠르다는 점, Indexing을 활용하면 필요한 벡터 객체만 가져다가 다시 프로그램의 input으로 사용할 수 있다는 점 등에 대해 이전에 소개해준 적이 있는데요,

 

그럼 이번에는 벡터 끼리 혹은 벡터와 스칼라 간에 비교할 때 쓰는 비교/논리 연산자에 대해서 알아보도록 하겠습니다.

 

 

 

벡터의 비교/논리 연산자 

 

 

-- 비교 연산자 --

 

(1) ~ 보다 크다 :  >

 

aa, bb 라는 벡터와 cc 라는 스칼라를 만들어서 비교/논리 연산자를 실습해 보겠습니다.

 

> aa <- c(1, 2, 3, 4, 5, 6, 7) # 벡터
> bb <- c(7, 6, 5, 4, 3, 2, 1) # 벡터
> cc <- c(4) # 스칼라
> 
> aa > bb
[1] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE
> aa > cc
[1] FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE

 

'>' 연산자를 실행하면 'TRUE', 'FALSE'와 같은 논리형 벡터가 아래에 출력됩니다.

 

'aa' 벡터는 구성요소가 7개인 반면에, 'cc' 스칼라는 구성요소가 1개 밖에 없는데도 비교 연산이 되었습니다. 이처럼 서로 구성요소의 개수가 다르면 스칼라가 R 내부적으로 7번 반복이 되어서 순환연산이 실행되었습니다.  벡터끼리 연산할 때도 서로 개수가 다르면 개수가 작은 쪽이 순환연산이 이루어져서 서로 짝을 맞추어서 연산이 실행됩니다.

 

 

(2) ~ 보다 크거나 같다 : >=

 

> aa >= bb
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE
> aa >= cc
[1] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE

 

 

(3) ~ 보다 작다 : <

 

> aa < bb
[1]  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE
> aa < cc
[1]  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE 

 

 

(4) ~ 보다 작거나 같다 : <=

 

> aa <= bb
[1]  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE
> aa <= cc
[1]  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE
> aa =< cc
Error: unexpected '<' in "aa =<"

 

세번째 명령어에 보면 부호의 순서가 '=<' 처럼 '~보다 같거나 작다'의 순서로 했더니 에러가 났습니다. '<=' 처럼 '~보다 작거나 같다'의 순서로 부호 사용하기 바랍니다.

 

 

(5) 서로 같다 : ==

 

> aa == bb
[1] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE
> aa == cc
[1] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE

 

R에서 객체를 할당할 때 '<-' 또는 '=' 를 사용하는데요, 비교 연산자의 '==' 과 헷갈리지 않도록 합니다.

 

 

(6) 서로 같지 않다 : !=

 

> aa != bb
[1]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
> aa != cc
[1]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
> aa =! cc
> 

 

 

서로 같지 않다도 부호의 순서를 '!=' 로 해야지만 논리형 벡터가 결과 값으로 나옵니다. 세번째 줄처럼 '=!'로 하면 논리형 벡터 결과가 안나오니 순서 주의하세요.

 

 

 

-- 논리 연산자 --

 

(7) 동시에 조건 만족 (aa AND bb)  : aa & bb

 

> aa <- c(1, 2, 3, 4, 5, 6, 7) # 벡터 > bb <- c(7, 6, 5, 4, 3, 2, 1) # 벡터 > cc <- c(4) # 스칼라

>

> aa > 3 & aa < 6
[1] FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE
> aa >= 3 & aa <= 6
[1] FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE

 

 

 

 

(8) 또는 조건 만족 (aa OR bb) : aa | bb

 

> aa < 3 | aa > 6
[1]  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE
> aa <= 3 | aa >= 6
[1]  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE 

 

 

-- 논리 연산자에 대한 추가 연산 --

 

(9) 조건 만족하는 논리형 벡터에 대한 합계, 평균 : sum(), mean()

 

> sum(aa > 3 & aa < 6)
[1] 2
> sum(aa < 3 | aa > 6)
[1] 3
> mean(aa > 3 & aa < 6)
[1] 0.2857143
> mean(aa < 3 | aa > 6)
[1] 0.4285714

 

R은 'TRUE'는 '1'로, 'FALSE'는 '0'으로 내부적으로 인식합니다. 따라서 sum()이나 mean() 과 같은 숫자형 벡터에 대한 통계 함수를 사용할 수가 있습니다.

 

 

(10) 비교/논리 연산 결과에 대해 하나라도 (any) 혹은 전부(all) TRUE 가 있는지 여부 확인 : any(), all()

 

> any(aa > 3 & aa < 6)
[1] TRUE
> all(aa > 3 & aa < 6)
[1] FALSE
> any(aa > 8)
[1] FALSE
> any(aa == 1)
[1] TRUE
> all(aa == 1)
[1] FALSE

 

 

위의 (1) ~ (9)번까지의 예들이 1~7까지의 혹은 7~1까지의 몇 개 안되는 구성요소를 가지고 예를 들다보니 눈으로 보면 TRUE가 뭐고 몇 개인지 바로 알 수 있지만요, 실제 분석할 때는 많은 경우 관측치 수가 수 만개, 수 백만개, 수 천만개가 되는 경우가 많습니다. 그때 일일이 육안으로 TRUE, FALSE를 확인한다는 것은 사실상 불가능합니다. 따라서 비교/논리 연산 결과에 대해 sum(), mean(), any(), all() 등과 같은 함수를 적절히 활용해서 즉시 데이터 셋에 대한 상황 파악(탐색)을 하는 것이 필요합니다.

 

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

 

728x90
반응형
Posted by Rfriend
,

통계는 크게 표본의 (a) 도수 분포와 중심화 경향, 그리고 퍼짐 정도를 측정하여 집단의 특성에 대해서 기술하는 기술통계(descriptive statistics)와, (b) 기술통계량을 가지고 모집단의 parameter 값 (모평균, 모분산 등)을 추정하고 가설을 검증하는 추정통계(inferential statistics)로 구분할 수 있습니다.

 

이번 포스팅에서는 R에서 벡터를 대상으로 사용할 수 있는 기술 통계 관련 함수에 대해서 알아보겠습니다.

 

R 기술통계 함수

 

-- 분포 및 중심화 경향 --

 

(1) 평균 : mean(x)

 

> x <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
> mean(x)
[1] 5.5 

 

 

(2) 중앙값 : median(x)

 

> x <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
> median(x)
[1] 5.5
> y <- c(1, 2, 3, 4, 5, 6, 7, 8, 9)
> median(y)
[1] 5

 

벡터 x가 홀수개이면 정 가운데 값을 중앙값을 가져오지만, 위의 case와 같이 x가 짝수개 이면 정가운데의 양쪽 두개의 값을 가져다가 평균을 내서 중앙값을 계산합니다.

 

 

(3) 최소값 : min(x)

 

> min(x)
[1] 1
> min(y)
[1] 1 

 

 

which.min(my_vec) 은 최소값이 있는 위치의 index 를 반환합니다. NA가 포함되어 있는 vector의 경우 min(my_vec) 이 NA를 반환한데 반해서 (NA에 대한 전처리 필요), my_vec[wich.min(my_vec)] 처럼 최소값을 ndexing을 해오면 '-12'를 반환했습니다.

 

 

> my_vec <- c(-5, 3, 10, 3, -12, NA)
> my_vec
[1]  -5   3  10   3 -12  NA
> 
> min(my_vec)
[1] NA
> 
> which.min(my_vec) # index of min value in 'my_vec' vector
[1] 5
> 
> my_vec[which.min(my_vec)]
[1] -12

 

 

 

 

(4) 최대값 : max(x)

 

> max(x)
[1] 10
> max(y) 

 

> my_vec <- c(-5, 3, 10, 3, -12, NA)
> my_vec
[1]  -5   3  10   3 -12  NA
> 
> max(my_vec)
[1] NA
> 
> which.max(my_vec) # index of max value in 'my_vec' vector
[1] 3
> 
> my_vec[which.max(my_vec)]
[1] 10 

 

 

 

(5) 범위 : range(x)

 

> range(x)
[1]  1 10
> range(y)
[1] 1 9 

 

 

(6) IQR(Inter-Quartile Range) : IQR(x)

 

> IQR(x)
[1] 4.5
> IQR(y)
[1] 4 

 

 

(7) 중심화 경향 및 분포 요약 : summary(x)

 

> summary(x)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.00    3.25    5.50    5.50    7.75   10.00 

 

숫자형 벡터의 경우 summary() 함수가 위의 1번에서 6번까지 함수를 한번에 처리할 수 있는 유용한 함수가 되겠습니다.

 

 

-- 퍼짐 정도 --

 

(8) 분산 : var(x)

 

> var(x)
[1] 9.166667
> var(y)
[1] 7.5 

 

 

(9) 표준편차 : sd(x)

 

> sd(x); sd(y)
[1] 3.02765
[1] 2.738613 

 

참고) 세미콜론 ';' 을 사용하면 같은 줄에 R 명령어를 연속으로 해서 쓸 수 있습니다

 

 

-- 확률분포의 비대칭 정도 --

 

(10) 왜도

 

> install.packages("fBasics") # 왜도, 첨도 분석 가능한 package 설치 > library(fBasics) # package 호출 > hist(mtcars$mpg)

 

 

 

 

 

 

> skewness(mtcars$mpg) [1] 0.610655 attr(,"method") [1] "moment"

 

 

R에 왜도와 첨도를 위한 함수가 내장되어 있지 않기 때문에 별도 패키지(fBasics)를 설치해야 합니다.

자동차 정보가 들어있는 mtcars 데이터 프레임의 연비에 대해서 히스토그램을 그려보니 평균보다 왼쪽으로 치우쳐 있고 오른쪽으로 꼬리가 긴 분포를 띠고 있네요. 그러면 왜도(skewness) 가 '0'보다 크게 나타납니다. (공식이 평균에서 관측치를 뺀 값을 3제곱 하기 때문이예요) 위 예에서는 왜도가 0.61로 '0'보다 크게 나왔지요. 정규분포의 평균과 일치하면 왜도는 '0'이 되고, 반대로 평균보다 오른쪽으로 값이 치우쳐 있고 왼쪽으로 꼬리가 길면 왜도는 '0'보다 작은 값이 나옵니다.

 

 

(11) 첨도

 

> kurtosis(mtcars$mpg)
[1] -0.372766
attr(,"method")
[1] "excess"

 

관측값이 정규분포보다 뾰쪽한가 아닌가를 가늠하는 쳑도가 첨도입니다. '3'보다 크면 정규분포보다 더 뾰족한 모양이고, '3'보다 작으면 정규분포보다 덜 뾰족한 모양이라고 해석하면 되겠습니다. (패키지에 따라서는 '3'을 빼서 '0'으로 표준화해서 값을 제시하기도 합니다)

 

 

-- 기타 함수 --

 

(12) 합 : sum(x)

 

> sum(x)
[1] 55
> sum(y)
[1] 45

 

 

(13) n차 차분 : diff(x, lag=n)

 

> diff(x, lag=1)
[1] 1 1 1 1 1 1 1 1 1
> diff(x, lag=2)
[1] 2 2 2 2 2 2 2 2
> diff(x, lag=3)
[1] 3 3 3 3 3 3 3 

 

관측값에서 직전 관측값을 뺀 차분을 구하는 함수입니다. 시계열분석할 때 정상화하기 위해서 차분을 이용하는데요, 시차(lag)를 분석 목적에 따라 또 데이터 특성에 따라서 입력해주면 됩니다. 디폴트는 lag=1 이 되겠습니다.

 

 

(14) 길이, 관측값 개수 : length()

 

> # 벡터에 length() 사용 시
> length(x)
[1] 10
> length(y)
[1] 9 
> 

> # 데이터 프레임에 length()사용 시

> length(mtcars)
[1] 11
> 

> # 데이터 프레임의 특정 변수에 length($) 사용 시

> length(mtcars$mpg)
[1] 32

 

벡터에서 length()는 관측값 개수를 계산해서 보여줍니다.

데이터 프레임에서는 column 개수를 나타내주고요, 데이터 프레임의 특정 변수를 지정하면 그 특정 변수의 관측값의 개수를 세서 보여줍니다.

 

 

(15) 순위 : rank()

 

> rank(x) [1] 1 2 3 4 5 6 7 8 9 10 >
>
rank(-x) [1] 10 9 8 7 6 5 4 3 2 1 >

> mtcars$mpg
 [1] 21.0 21.0 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 17.8 16.4 17.3 15.2 10.4 10.4 14.7 32.4 30.4 33.9 21.5 15.5
[23] 15.2 13.3 19.2 27.3 26.0 30.4 15.8 19.7 15.0 21.4

>

> rank(mtcars$mpg, 
+      na.last = TRUE, 
+      ties.method = c("max"))
 [1] 20 20 25 22 15 14  4 26 25 17 13 11 12  8  2  2  5 31 30 32 23  9  8  3 17 28 27 30 10 18  6 22

 

> ##----------------------- > ## rank() {base package} > ##----------------------- > > # if there are no ties(i.e., equal values), no problem at all > x <- c(1, 5, 9, 7) > rank(x) [1] 1 2 4 3 > > > # if there are ties, ties can be handled in several ways > y <- c(1, 1, 1, 5, 9, 7) > > # returns average, default setting > rank(y) [1] 2 2 2 4 6 5 > rank(y, ties.method = c("average")) [1] 2 2 2 4 6 5 > > # first occurrence wins > rank(y, ties.method = c("first")) [1] 1 2 3 4 6 5 > > # ties broken at random > rank(y, ties.method = c("random")) [1] 3 2 1 4 6 5 > > rank(y, ties.method = c("random")) # ...random one more time [1] 1 3 2 4 6 5 > > rank(y, ties.method = c("random")) # ...random...again [1] 1 2 3 4 6 5 > > # rank by max value as used classically > rank(y, ties.method = c("max")) [1] 3 3 3 4 6 5 > > # rank by min value as in Sports > rank(y, ties.method = c("min")) [1] 1 1 1 4 6 5

 

rank는 순위대로 정렬해주는게 아니라 순위의 색인을 나타내줍니다.

디폴트는 작은 값부터 1을 부여해주고, 큰 것 부터 1을 부여하려면 '-'를 붙여주면 됩니다.

 

동일한 값(Ties, i.e, equal values)이 있을 경우 rank() 함수는 "average" (default), "first", "random", "max", "min" 등의 옵션을 제공합니다.

 

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

 

728x90
반응형
Posted by Rfriend
,

R 은 숫자형 벡터를 처리할 수 있는 편리한 함수를 다수 제공합니다.

 

이번 포스팅에서는 절대값 계산, 제곱근 계산, x 보다 크거나 같은 정수(지붕), x 보다 작거나 같은 정수(바닥), 소수점 이하는 잘라서 버리거나 반올림 하기, 로그 변환, 지수 변환, 팩토리얼 등의 벡터 처림 함수에 대해서 알아보도록 하겠습니다. 

 

문자형 벡터 처리 함수는 나중에 별도로 기회를 봐서 포스팅하도록 하겠습니다.

 

 

R 숫자형 벡터 처리 함수 

 

 

1) 절대값 계산 : abs(x)

 

> abs(10)
[1] 10
> abs(-10)
[1] 10

 

 

2) 제곱근 계산 : sqrt(x)

 

> sqrt(16) [1] 4 > sqrt(-16) # 허수는 NaN [1] NaN Warning message: In sqrt(-16) : NaNs produced

 

 

3) x 보다 크거나 같은 정수 (지붕 정수) : ceiling(x)

 

> ceiling(5.88)
[1] 6
> ceiling(6.00)
[1] 6

 

 

4) x 보다 작거나 같은 정수 (바닥 정수) : floor(x)

 

> floor(5.88)
[1] 5
> floor(5.00)
[1] 5

 

 

5) x 소숫점 이하는 잘라서 버림 : trunc(x)

 

> trunc(5.88)
[1] 5
> trunc(5.10)
[1] 5

 

 

6) x 를 소수점 n자리로 반올림 : round(x, digits=n)

 

> round(5.88, digits = 1)
[1] 5.9
> round(5.88, digits = 0)
[1] 6
> round(5.10, digits = 0)
[1] 5

 

 

7) x 를 밑이 n인 log 취하기 : log(x, base=n)

 

> log(10, base=2) # 밑이 2인 로그 [1] 3.321928 >

> log(10, base = exp(1)) # 자연로그 [1] 2.302585 >

> log(10) # 자연로그, base = exp(1) 을 생략해도 동일함 [1] 2.302585 >

> log(10, base=10) # 밑이 10인 상용로그 [1] 1 >

> log10(10) # 상용로그의 다른 표현법 [1] 1 

 

 

8) x 를 지수변환하기 : exp(x)

 

> exp(log(10, base=exp(1)))
[1] 10
> exp(1)
[1] 2.718282
> exp(10)
[1] 22026.47 

 

 

9) x factorial : factorial(x)

 

> factorial(2)
[1] 2
> factorial(3)
[1] 6
> factorial(4)
[1] 24
> factorial(5)
[1] 120 

 

 

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

 

728x90
반응형
Posted by Rfriend
,

 

 

 * 책 제목: 부채의 습격

   (원제: Vortex of the Korean Financial Crisis)

 

 * 저자: 더글라스 김(Douglas Kim)

 

 * 옮긴이 : 민경재

 

 * 출판사 : 길벗

 

 * 출판일: 2010.10월

 

 

 

이 책은 벤자민 프랭클린이 했던 말을 인용하면서 시작을 합니다.

 

"거짓말은 두 번째로 나쁜 짓이다.
 제일 나쁜 짓은 빚에 치이는 것이다" 
- Benjamin Franklin, 미국 건국의 아버지 중 한 사람

 

 

 

 

이 책의 전체 내용을 요약하자면 "인플레이션과 고금리의 쓰나미가 몰려올 때를 대비해서 과도하게 빚 내서 고정자산(부동산)에 투자(투기?) 하지 말라" 가 되겠네요.

 

 

이 책이 2010년도에 쓰여졌고 저자는 부채의 습격이 2011년~2013년 사이에 몰아칠 것으로 예상을 했고, 이를 경고하기 위해 이 책을 사명감을 가지고 쓴 듯 합니다.2015년 7월 현재 그동안의 경과를 놓고 2011~2013년을 뒤돌아보면 결과적으로 저자의 예측과 경고는 틀렸다고 할 수 있겠습니다.

 

 

[한국은행 기준금리 추이 (2010년 1월 ~ 2015년 7월) ]

 

 

 

 

한국은행은 2015.7.9일 현재 기준금리를 1.50%로 동결하였습니다.

 

그동안 미국, 중국, 일본이 경제 회복을 위해서 저금리 기조와 경기 부양 정책을 일관되게 지속하여 왔고, 덕분에 한국도 저금리 기조를 유지할 수 있었던 덕이겠지요. 작년 세월호 사고, 올해 메르스로 인해서 경제가 위축된 것에 대해 정부가 대응하기 위해 금리를 1%대로 낮추고 유지하는 마지막 카드를 썼다고 봐야할 것 같습니다.

 

그러면 이 책은 저자의 2011년~2013년 경고가 빗나갔기 때문에 쓸모없는 것일까요?

저는 이 책이 2015년 하반기를 위시해서 2020년까지의 한국 경제가 나타낼 몇 가지 시나리오 중에 가장 그럴듯한 시나리오를 제시하고 있다고 생각하기에 이 책의 진가는 이제부터라고 생각합니다. 그리고 여러 사람이 이 책을 읽고 이 책의 저자가 경고하는 내용에 귀 기울여서 부채의 늪으로 빠지는 일이 없기를 바라는 마음 또한 저자와 같은 마음이기에 이 책에 대한 블로그 포스팅을 쓰는 것이구요.

 

이 책의 저자가 자신의 주장을 뒷받침하기 위해 제시했던 논리, 근거를 차근 차근 들여다보면 왜 2015년 하반기, 지금 이 시기에 저자의 주장이 설득력이 있는지 이해가 가실 거라 생각합니다. (저자가 경제학 백그라운드에 글로벌 투자회사의 애널리스트로 일하는 경력의 소유자이다 보니 이론과 실무적인 균형이 잘 잡혀있고, 통계자료의 적재적소 인용에 있어서 참 놀랍고 대단하다는 생각을 책 읽는 내내 했습니다. 서로가 얽히고 설킨 복잡한 경제의 연결구조, 큰 그림을 이해하는데 도움이 많이 되는 책이었습니다.)

 

몇 가지 기억남는 저자의 주장, 논리를 적어보도록 하겠습니다. 2015년 하반기 부터 해서 향후 몇 년간 어떤 경제 상황이 펼쳐질지 이글을 읽는 분들은 나름 판단해보시기 바랍니다.

 

1) 한국 기업의 부채 비율은 IMF 이후 꾸준히 줄어든 반면, 가계부채는 지속적으로 늘어나 위험한 수준까지 도달했다.

 

가계부채는 금융기관의 가계 대출과 카드회사의 신용 판매 등을 합해야 하는데요, 아래 한국은행의 주택담보대출 추이만을 일단 놓고 봤을 때 그동안 꾸준히 증가해왔음을 알 수 있습니다.

 

[ 주택담보대출 추이 (2008.1월 ~ 2015.5월) ]

(* 출처: 한국은행)

 

(이 책에는 2008년 또는 2009년 까지의 통계치만 나와 있어서요, 한국은행 사이트 들어가서 최신 통계치로 그래프 그려서 포스팅합니다)

 

작년 연말에 담보인정비율(LTV)과 총부채상환비율(DTI) 등 부동산 관련 규제가 거의 다 풀리면서 정부가 대출받아 집사라고 많이 유도를 했었고, 그 효과가 위에 보는 그래프에 나타났다고 봐야겠습니다.

 

 

(2) 은행이 이자 변동 Risk를 고객에게 떠넘기기 위해 '고정금리' 대신 '변동금리'형 대출 상품으로 가계대출을 판매하여 왔다.

 

 

(3) 한국의 금리는 미국, 중국, 일본 등의 금리와 긴밀히 연결되어 있다. 이들 미국, 중국, 일본이 자국의 금리를 올리기 시작하는 순간, 해외 금융 자본은 한국에서 빠져나가기 시작할 것이고, 한국에서 이들 해외 자본을 붙들어 놓기 위해서는 금리를 올려야 할 수 밖에 없다.

 

15년 올해 하반기에 미국 연준에서 금리를 인상할 것이라고 했었죠. 저자 주장대로 이제 미국이 금리를 올리면 도미노 연쇄효과가 발생해서 우리나라도 금리를 올려야 할 상황이 발생하겠지요.

 

 

(4) 변동금리형 주택담보대출 비중이 90%인데, (고정금리보다 금리가 낮다면서 은행에서 고객에게 추천하므로), 변동금리 대출상품의 경우 금리 급상승의 위험을 가계가 떠안다 보니 금리가 상승하게 되면 부채의 1차 피해자는 자영업자, 카드 연체자가 될 것이다.  

 

금리가 요즘 하도 낮다보니 금리가 올라봐야 얼마나 오르겠어 하고 감이 잘 안올 수도 있는데요, 1976년~1980년 오일쇼크 때 20% 대로 이자율 급상승, 1997년~1998년 IMF 때 15%로 이자율 급상승의 선례가 있지요. 서민들, 중소기업들은 높은 이자를 견디지 못해 나자빠졌었고, 부자들은 "이대로 그대로~"를 외쳤다고 하는 아픈 선례요.

 

 

(5) 베이비붐 세대의 은퇴가 시작되면서 부동산 처분 혹은 부동산 담보 역모기지 등을 통해 생활 자금을 감당하려고 할 것이므로 부동산이 시장에 매물로 나오기 시작할 것이다. 이는 부동산 자산 가치의 하락을 부채질할 수 있다.

 

 

(6) 석유, 천연자원의 부족은 인플레이션을 부른다.  

 

최근 몇 년간 석유 값이 많이 내려서 2011~2013년 위기론의 저자의 주장이 무색하게 되어버렸는데요, 지금의 배럴당 50달러 선의 저유가가 치킨게임이 얼마나 더 지속될 수 있을지 궁금합니다. 석유와 천연자원이 분명 한정되어 있기 때문에 '언제?'의 문제일 뿐 저자의 주장은 여전히 유효하다고 생각합니다.  중동에서 전쟁이라도 발발하거나, OPEC에서 감량 선언하면서 석유 가격 오르기 시작하면 우리나라는 통제불가능한 직격탄을 맞은 셈이 되겠지요.

 

 

(7) 저출산, 세계 최고 수준의 고령화 속도 등 우리나라 인구구조가 위태롭다. 인구 구조적 문제로 인해 앞으로 지속적으로 가구당 가처분 소득이 줄어들고, 젊은 층의 생산가능인구와 수요가 낮아져 경제 활력도 낮아질 위험이 높다.

 

 

[ 인구구조 변화 추이 (총인구, 생산가능인구, 노인인구, 노년부양비) ]

 

 

 

 

2015년 7월 현재 그리스는 디폴트를 선언하네 마네, 유로존을 탈퇴하네 마네, 난리도 아닙니다.  그리스 경제 위기의 근본 원인은 높은 부채이며, 한국도 1997년 IMF 위기 때 비슷한 고통을 당한 바 있습니다.

 

저자의 주장은 단순명쾌합니다. 

"과도한 빚을 내서 부동산에 투자(투기?)하지 말라. 살아남고 싶다면 허리띠를 졸라매라.  금리인상과 유가상승이 시작되면 당신의 자산이 반토막나고 남은 생을 빚 갚고 이자 갚느라 고통을 당할 수 있다. 은행은 해가 짱짱한 시기에는 우산을 빌려주지만, 비가 쏟아지기 시작하면 우산을 돌려달라고 한다."

 

2010년에 쓰여진 책이지만 지금 이 시기에 적절한 경고라고 생각하기에 이 책의 일독을 권합니다.

물론, 투자와 관련해서는 개개인이 각자 판단하고 행동하고 스스로 책임질 일이겠기에, 요즘 시장 돌아가는 상황 파악하시고 현명하게 판단하시기 바랍니다.

 

728x90
반응형
Posted by Rfriend
,

R에서 데이터를 입력할 때 c() 를 사용하는데요, 일정한 반복이나 규칙을 따르는 데이터 입력이라면 함수 명령문을 이용하는게 단순 업무를 줄이는 방법이 되겠지요.

 

예전에 R의 장점이자 강점 중의 하나가 벡터 연산이 파워풀하다는 점이라고 했는데요,

 

아래의 rep(), seq() 함수를 곁들여서 벡터 연산에 활용하면 좋겠지요?!

 

 

 

 rep(), seq() 반복 데이터, 일정한 구조/순차 데이터 생성

 

 

(1) rep() : 일정한 데이터 반복

 

반복하고자 하는 4가지 형태별로 아래 예시를 들었습니다.

 


> ## "a"를 10번 반복

> rep("a", times = 10) [1] "a" "a" "a" "a" "a" "a" "a" "a" "a" "a" >

> ## "1"을 15번 반복

> rep(1, times = 15) [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 # 1 은 숫자형 그대로 임 >

> ## ("a" & "1") 을 5번 반복

> rep( c("a", 1), 5) [1] "a" "1" "a" "1" "a" "1" "a" "1" "a" "1" # "1"이 숫자형이 아니라 문자형으로 변환됨 >

> ## "a"를 먼저 5번 반복하고, "1"을 10번 반복

> rep( c("a", 1), c(5,10))
 [1] "a" "a" "a" "a" "a" "1" "1" "1" "1" "1" "1" "1" "1" "1" "1"

 

 




특정 범위의 정수 숫자를 각각 동일한 횟수로 반복하고자 한다면 rep(x:y, each=z) 함수를 사용하면 편합니다.

1~3까지의 정수를 10번씩 반복하는 예제는 아래와 같습니다.



> # repeat 1 at 10 times, 2 at 10 times, 3 at 10 times
> rep(1:3, each=10)
 [1] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3

 




1행부터 20행까지 있는 데이터 프레임에서 1부터 5까지의 반복하는 숫자를 행 기준으로 반복하는 예제는 아래와 같습니다. dataframe$var <- rep(c(1:5), len = ncol(dataframe)) 처럼 할당하는 방법도 있고, cbind()를 사용하는 방법도 있는데요, 2가지 방법 모두 아래에 소개하였습니다. 진동, 주파수 처럼 일정한 주기성을 띠는 데이터를 분석하는 경우 분석 단위/구간 지정을 위해서 은근히 많이 사용하는 데이터 전처리 방법입니다.



> ## rep() exmaple
> x <- c(1:20)
> y <- rep(1, times = 20)
> z <- rep(c(1, 2), c(10, 10))
> xyz <- data.frame(cbind(x, y, z))
> xyz
    x y z
1   1 1 1
2   2 1 1
3   3 1 1
4   4 1 1
5   5 1 1
6   6 1 1
7   7 1 1
8   8 1 1
9   9 1 1
10 10 1 1
11 11 1 2
12 12 1 2
13 13 1 2
14 14 1 2
15 15 1 2
16 16 1 2
17 17 1 2
18 18 1 2
19 19 1 2
20 20 1 2
> 
> ## repeating c(a:b) from first row until last row : way 1
> xyz$seq_no_1 <- rep(c(1:5), len = nrow(xyz))
> xyz
    x y z seq_no_1
1   1 1 1        1
2   2 1 1        2
3   3 1 1        3
4   4 1 1        4
5   5 1 1        5
6   6 1 1        1
7   7 1 1        2
8   8 1 1        3
9   9 1 1        4
10 10 1 1        5
11 11 1 2        1
12 12 1 2        2
13 13 1 2        3
14 14 1 2        4
15 15 1 2        5
16 16 1 2        1
17 17 1 2        2
18 18 1 2        3
19 19 1 2        4
20 20 1 2        5
> 
> ## repeating c(a:b) from first row until last row : way 2
> seq_no_2 <- rep(c(1:5), len = nrow(xyz))
> xyz <- cbind(xyz, seq_no_2)
> xyz
    x y z seq_no_1 seq_no_2
1   1 1 1        1        1
2   2 1 1        2        2
3   3 1 1        3        3
4   4 1 1        4        4
5   5 1 1        5        5
6   6 1 1        1        1
7   7 1 1        2        2
8   8 1 1        3        3
9   9 1 1        4        4
10 10 1 1        5        5
11 11 1 2        1        1
12 12 1 2        2        2
13 13 1 2        3        3
14 14 1 2        4        4
15 15 1 2        5        5
16 16 1 2        1        1
17 17 1 2        2        2
18 18 1 2        3        3
19 19 1 2        4        4
20 20 1 2        5        5

 

 



(2) seq() : 일정한 구조/순차 데이터 생성

 

 아래 예시를 참고하세요.

 

 

> ## c()를 이용한 1부터 10까지 입력 (1 단위씩 커짐)
> c(1:10)
 [1]  1  2  3  4  5  6  7  8  9 10
> 
> ## seq()를 이용한 1부터 10까지 입력 (1 단위씩 커짐)
> seq(from=1, to=10)
 [1]  1  2  3  4  5  6  7  8  9 10
> 
> ## seq_len()을 이용한 1부터 10까지 입력 (1 단위씩 커짐)
> seq_len(10)
 [1]  1  2  3  4  5  6  7  8  9 10
> 
> ## seq()를 이용한 1~10까지의 숫자를 2단위씩 증가시키면서 입력
> seq( from = 1, to = 10, by=2 )  # from, to 는 제외해도 괜찮음
[1] 1 3 5 7 9
> 
> ## seq()를 이용한 1~10까지의 수를 5개의 숫자로 등간격으로 구성 (숫자는 5개, 구간은 4개)
> seq( 1, 10, length = 5 )  # from, to 는 제외해도 괜찮음
[1]  1.00  3.25  5.50  7.75 10.00
> 
# length.out : 개수 지정
> seq(from = 1, by = 2, length.out = 10)
 [1]  1  3  5  7  9 11 13 15 17 19

 

 

 

 

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

 

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

 

728x90
반응형
Posted by Rfriend
,