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, Midsize, Small, 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, Midsize, Small, 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"
|
많은 도움이 되었기를 바랍니다.
이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^