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

 

선형대수(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
,

데이터는 크게 (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
,

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
,

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
,