변수의 개수 및 데이터의 형태에 따라서 그래프, 시각화 방법이 달라지는데요,

 

지난번 포스팅에서는 연속형 데이터의 시각화 방법으로

 - 히스토그램(Histogram)
    : geom_histogram()

- 커널 밀도 곡선(Kernel Density Curve)
    : geom_density()

 - 박스 그래프(Box Plot)
    : geom_boxplot()

 - 바이올린 그래프(Violin Plot)
    : geom_violin()

에 대해서 알아보았습니다. 

 

 

이번 포스팅에서는 범주형 데이터의 시각화 방법으로서

 

 - 막대그림(Bar Chart): geom_bar()

 - 원그림(Pie Chart): geom_bar() + coord_polar()

 - 모자이크 그림(Mosaic Chart): vcd 패키지 mosaic()

 

에 대해서 소개해드리겠습니다.

 

 

[ 변수 개수 및 데이터 형태에 따른 그래프 ]

  

 

모자이크 그림(Mosaic Chart)은 Marimekko chart, Eikosogram 이라고도 하는데요, 특히 2개 이상의 다변량 변수를 한꺼번에 그림으로 나타내어 탐색적 분석을 할 때 아주 유용합니다. 

 

모자이크 그림은 vcd 패키지의 mosaic() 함수를 이용하겠으며, 데이터는 MASS 패키지의 cars93 데이터 프레임 내에 있는 차종(Type), 제조국(Origin), DriveTrain(Rear, Front, 4WD) 의 3개 변수를 모자이크 그림으로 표현해 보겠습니다.

 

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

 

 

(1) vcd 패키지 설치, 호출 후에 차종(Type)과 제조국(Origin) 2개의 변수를 가지고, 세로 방향(direction="v") 으로 모자이크 그림을 그려보겠습니다.  table() 함수를 써서 분할표를 먼저 만들고, 이걸 가져다가 모자이크 그림을 그리게 됩니다.

 

> # vcd package installation > install.packages("vcd") > library(vcd) > > # 모자이크 그림 : 차종(Type) & 제조국(Origin) > # 세로 방향 > table_1 <- with(Cars93, table(Type, Origin)) >

> table_1
         Origin
Type      USA non-USA
  Compact   7       9
  Large    11       0
  Midsize  10      12
  Small     7      14
  Sporty    8       6
  Van       5       4

 

 

>

> mosaic(table_1, + gp=gpar(fill=c("yellow", "blue")), + direction="v", # 세로 + main="Mosaic Chart by Car Type and Origin, using vcd package")

 

 

 

 

(2) 차종(Type)과 제조국(Origin) 2개의 변수를 가지고, 모자이크 그림을 가로 방향 (direction="h") 으로  그리면 아래와 같습니다.  Origin이 y축에 있던 것이 x축으로 바뀌었습니다.

 

> # 가로 방향
> mosaic(table_1, 
+        gp=gpar(fill=c("yellow", "blue")), 
+        direction="h", # 가로
+        main="Mosaic Chart by Car Type and Origin, direction=horizontal")

 

 

 

 

 

 

(3) 이번에는 변수를 하나 더 추가해서 차종(Type), 제조국(Origin), DriveTrain 의 3개의 변수를 가지고 모자이크 그림을 그려보겠습니다.  먼저 table()함수를 써서 3개 변수에 대한 분할표를 만들고, 이를 가져다가 모자이크 그림을 그리게 됩니다.

 

> # 모자이크 그림 : 차종(Type) & 제조국(Origin) & DriveTrain(Rear, Front, 4WD)
> # 세로 방향
> 

> # 3개 변수별 범주 확인

> levels(Cars93$Type)
[1] "Compact" "Large"   "Midsize" "Small"   "Sporty"  "Van"    
> levels(Cars93$Origin)
[1] "USA"     "non-USA"
> levels(Cars93$DriveTrain)
[1] "4WD"   "Front" "Rear"
> table_2 <- with(Cars93, table(Type, Origin, DriveTrain))
> 
> table_2
, , DriveTrain = 4WD

         Origin
Type      USA non-USA
  Compact   0       1
  Large     0       0
  Midsize   0       0
  Small     0       2
  Sporty    2       0
  Van       3       2

, , DriveTrain = Front

         Origin
Type      USA non-USA
  Compact   7       6
  Large     7       0
  Midsize   9       8
  Small     7      12
  Sporty    2       5
  Van       2       2

, , DriveTrain = Rear

         Origin
Type      USA non-USA
  Compact   0       2
  Large     4       0
  Midsize   1       4
  Small     0       0
  Sporty    4       1
  Van       0       0

> 
> 
> mosaic(table_2, 
+        gp=gpar(fill=c("yellow", "blue", "red")), 
+        direction="v", 
+        main="Mosaic Chart by Car Type, Origin and DriveTrain, direction=v")

 

 

 

 

 

 

마지막으로, 모자이크 그림에 비율 라벨(Mosaic Chart with Percentage Label)을 추가해보겠습니다.

 

> # 비율 라벨 추가 (Mosaic Chart with Percentage Labels)
> table_1 <- with(Cars93, table(Type, Origin))
> proportions <- round(prop.table(table_1)*100, 1) # 백분율, 소수점 첫째자리 반올림
> 
> proportions
         Origin
Type       USA non-USA
  Compact  7.5     9.7
  Large   11.8     0.0
  Midsize 10.8    12.9
  Small    7.5    15.1
  Sporty   8.6     6.5
  Van      5.4     4.3
> 
> values <- c(table_1)
> rowvarcat <- c("USA","non_USA")
> columnvarcat <- c("Compact","Large", "Midsize", "Small", "Sporty", "Van")
> names=c("Origin", "Type")
> dims <- c(2,6)
> 
> TABS <- structure( c(values), 
+                    .Dim = as.integer(dims), 
+                    .Dimnames = structure( list(rowvarcat, columnvarcat ),
+                    .Names = c(names) ) , class = "table") 
> 
> PROPORTIONS <- structure( c(proportions), 
+                           .Dim = as.integer(dims), 
+                           .Dimnames = structure( list(rowvarcat,columnvarcat ),
+                           .Names = c(names) ) , class = "table") 
> 
> TABSPROPORTIONS <- structure( c(paste(proportions,"%","\n", "(",values,")",sep="")), 
+                               .Dim = as.integer(dims), 
+                               .Dimnames = structure( list(rowvarcat,columnvarcat ),
+                               .Names = c(names) ) , class = "table") 
> 
> mosaic(TABS, 
+        pop=FALSE, 
+        main="Mosaic Chart by Car Type and Origin, with Percentage Labels")
> 
> labeling_cells(text=TABSPROPORTIONS, clip_cells=FALSE)(TABS)

 

 

 

* Michael Friendly’s book “Visualizing Categorical Data” 예제 참고 

 

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

 

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

 

728x90
반응형
Posted by Rfriend
,

변수의 개수 및 데이터의 형태에 따라서 그래프, 시각화 방법이 달라지는데요,

 

지난번 포스팅에서는 연속형 데이터의 시각화 방법으로

 - 히스토그램(Histogram)
    : geom_histogram()

- 커널 밀도 곡선(Kernel Density Curve)
    : geom_density()

 - 박스 그래프(Box Plot)
    : geom_boxplot()

 - 바이올린 그래프(Violin Plot)
    : geom_violin()

에 대해서 알아보았습니다. 

 

 

이번 포스팅에서는 범주형 데이터의 시각화 방법으로서

 

 - 막대그림(Bar Chart): geom_bar()

 - 원그림(Pie Chart): geom_bar() + coord_polar()

 - 모자이크 그림(Mosaic Chart): vcd 패키지 mosaic()

 

에 대해서 소개해드리겠습니다.

 

 

[ 변수 개수 및 데이터 형태에 따른 그래프 ]

 

 

 

데이터는 MASS 패키지에 들어있는 Cars93 데이터 프레임의 차종(Type)과 제조국(Origin) 데이터를 활용해서 차종별 제조국별 도수를 구한 후에, 원그림을 그려보겠습니다.

 

원그림은 세로로 쌓아올린(stacked) 막대그림을 먼저 geom_bar()로 먼저 그린 후에 ==> coord_polar("y")로 해서 표현 형식을 원형(polar coordinates)으로 바꾸어주는 절차를 따릅니다.

 

(1) 바로 아래에 있는 그래프가 쌓아올린 막대그림 (stacked bar chart) 이구요,

 

> ## 원그림(Pie Chart)
> library(ggplot2)
> library(MASS)
> 
> # 막대그림 먼저 그린 후 => 원그림
> library(sqldf)
> Car_Type_Origin_cnt <- sqldf( 'select Type, Origin, count(*) as Type_Origin_cnt
+                               from Cars93
+                               group by Type, Origin
+                               order by Type, Origin
+                               ')
> 
> Car_Type_Origin_cnt
      Type  Origin Type_Origin_cnt
1  Compact     USA               7
2  Compact non-USA               9
3    Large     USA              11
4  Midsize     USA              10
5  Midsize non-USA              12
6    Small     USA               7
7    Small non-USA              14
8   Sporty     USA               8
9   Sporty non-USA               6
10     Van     USA               5
11     Van non-USA               4
> 
> sapply(Car_Type_Origin_cnt, class)
           Type          Origin Type_Origin_cnt 
       "factor"        "factor"       "integer" 
> 
> 
> ggplot(data=Car_Type_Origin_cnt, aes(x="", y=Type_Origin_cnt, fill=Type)) +
+   facet_grid(facets=. ~ Origin) + 
+   geom_bar(stat="identity", width=1) + 
+   ggtitle("Bar Chart of Frequency by Car Type & Origin")

 

 

 

 

 

(2) 그 다음 표현형식을 coord_polar("y") 를 써서 원형으로 바꾸어 본 것이 아래 script 가 되겠습니다.

 

> 
> # 원그림(Pie Chart) : geom_bar() + coord_ploar(theta="y")
> ggplot(data=Car_Type_Origin_cnt, aes(x="", y=Type_Origin_cnt, fill=Type)) +
+   facet_grid(facets=. ~ Origin) + 
+   geom_bar(stat="identity", width=1) + 
+   coord_polar(theta="y") + 
+   ggtitle("Pie Chart of Car Type by Origin, theta=y")

 

 

 

 

 

 

이번 포스팅에서 원그림(Pie Chart)를 소개하기는 했습니다만, 저는 원그림은 그다지 추천하지 않습니다.  일단 눈으로 보고 해석하기에, 범주간 비교하기에 그다지 용이하지가 않습니다.  파이 조각의 면적을 눈으로 가늠해서 비교하기가 쉽지 않거든요.  막대그림으로 높이를 비교하는 것이 범주 간 차이를 단박에 보여주는 것과 비교하면 원그림은 그래프를 봐야하는 이에게는 불친절한 그래프입니다.

(단, 두 집단(가령 USA, non-USA) 간에 과반을 넘는 특정 segment를 비교해서 강조하고 싶은 경우는 원그림이 효과적일 수 있음)

 

신문이나 잡지에서 보면 원그림을 많이 사용하고 있고, 더 나아가서는 입체 원그림을 그리고, 또 나아가서는 입체 원그림의 한 조각을 확대하거나 원그림을 살짝 눕히기도 하는데요, 이건 정말 최악 중의 최악입니다.  일단, 시각적으로 해석하기에 어렵구요, 더 나아가서는 악의를 가지고 시각의 불완전성을 이용해서 정보를 왜곡/오도하기 위한 것일 수도 있으니 주의가 필요합니다

 

웬만하면 원그림은 사용하지 말기를 권하며, 누가 원그림 사용하거든 그만 사용하라고 권해주기를 권합니다.  그냥 쉽고 편한 막대그림 사용하세요.

 

참고용으로, 막대그림으로 나타내면 아래와 같은데요, 위의 원그림과 비교해보시기 바랍니다.  뭐가 더 쉽고 해석하기 편한지요.

 

> # 막대그림
> ggplot(data=Car_Type_Origin_cnt, aes(x=Type, y=Type_Origin_cnt)) +
+   facet_grid(facets=. ~ Origin) + 
+   geom_bar(stat="identity", width=1, fill="white", colour="black") + 
+   ggtitle("Bar Chart of Frequency by Car Type & Origin")

 

 

 

 

원그림 소개한다고 해놓고는, 막대그림으로 끝을 맺네요. ^^'

 

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

 

 

728x90
반응형
Posted by Rfriend
,

변수의 개수 및 데이터의 형태에 따라서 그래프, 시각화 방법이 달라지는데요,

 

지난번 포스팅에서는 일변량 연속형 데이터의 시각화 방법으로

 - 히스토그램(Histogram)
    : geom_histogram()

- 커널 밀도 곡선(Kernel Density Curve)
    : geom_density()

 - 박스 그래프(Box Plot)
    : geom_boxplot()

 - 바이올린 그래프(Violin Plot)
    : geom_violin()

에 대해서 알아보았습니다. 

 

 

이번 포스팅에서는 일변량 범주형 데이터의 시각화 방법으로서

 

 - 막대그림(Bar Chart): geom_bar()

 - 원그림(Pie Chart): geom_bar() + coord_polar()

 

에 대해서 소개해드리겠습니다.

 

 

[ 변수 개수 및 데이터 형태에 따른 그래프 ]

 

 

 

 

 

 

먼저, 범주별 도수를 구하고 이를 막대 형태로 나타낸 막대 그래프 (Bar Chart)를 ggplot2의 geom_bar() 로 그려보겠습니다. 

 

사용할 데이터는 MASS 패키지에 있는 Cars93 데이터 프레임에서 자동차 유형(Type), 제조국(Origin) 등의 범주형/요인(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)별 도수를 가지고 막대그림을 그려보겠습니다.

 

> ggplot(Cars93, aes(x=Type)) + 
+   geom_bar(fill="white", colour="black") + 
+   ggtitle("Bar Chart of Frequency by Car Type")

 

 

 

 

 

 

위와 똑같은 그래프를 그려볼건데요, 이번에는 aes(x, y)의 x변수와 도수에 해당하는 y변수로 된 데이터프레임을 만들어서 이를 직접 x, y에 입력해서 그래프를 그려보겠습니다 (간편하게는 위의 방식 사용하면 되구요, 아래 처럼 데이터가 구성이 되어있다면 이번 방식을 이용하면 되겠습니다).  아래 예제에서는 자동차 유형(Type)별로 도수를 집계(aggregation)할 때 sqldf 패키지를 사용하였습니다.

 

> install.packages("sqldf")
> library(sqldf)
> 
> Car_Type_cnt <- sqldf( 'select Type, count(*) as Type_cnt
+                           from Cars93
+                           group by Type
+                           order by Type
+                         ')
> 
> Car_Type_cnt
     Type Type_cnt
1 Compact       16
2   Large       11
3 Midsize       22
4   Small       21
5  Sporty       14
6     Van        9
> 
> sapply(Car_Type_cnt, class)
     Type  Type_cnt 
 "factor" "integer" 

 

다음으로 자동차 유형(Type)별로 geom_bar()를 이용하여 막대그림을 그려보도록 하겠습니다.  y에 직접 입력해주고, geom_bar()에 stat="identity"를 설정해주어야 합니다.

 

> # 자동차 유형별 도수 막대 그림
> library(ggplot2)
> 
> ggplot(Car_Type_cnt, aes(x=Type, y=Type_cnt)) + 
+   geom_bar(stat="identity", fill="white", colour="black") + 
+   ggtitle("Bar Chart of Frequency by Car Type")

 

 

 

 

 

 


 

일변량에 더해서, 이번에는 2개의 변수를 사용한 막대그림도 살펴보도록 하겠습니다.  차종(Type) 별 제조국(Origin) 별 자동차 수를 가지고 막대그림을 그려보도록 하겠습니다. 

 

> # Origin별 구분 추가하기
> ggplot(Cars93, aes(x=Type, fill=Origin)) + 
+   geom_bar(position="dodge", colour="black") + 
+   scale_fill_brewer(palette=1) +
+   ggtitle("Bar Chart of Frequency by Car Type & Origin")

 

 

 

 

 

 

이번에는 위와 동일한 그래프를 그릴건데요, sqldf()로 차종(Type)별 & Origin 별 자동차 도수를 집계를 해서 데이터프레임을 만들어서 막대그림을 그려보겠습니다.

 

> # 차종(Type) 별 실린더개수(Cylinders) 별 자동차 개수 > library(sqldf)

> Car_Type_Origin_cnt <- sqldf( 'select Type, Origin, count(*) as Type_Origin_cnt + from Cars93 + group by Type, Origin + order by Type, Origin + ') > Car_Type_Origin_cnt Type Origin Type_Origin_cnt 1 Compact USA 7 2 Compact non-USA 9 3 Large USA 11 4 Midsize USA 10 5 Midsize non-USA 12 6 Small USA 7 7 Small non-USA 14 8 Sporty USA 8 9 Sporty non-USA 6 10 Van USA 5 11 Van non-USA 4 >

 

geom_bar()로 막대그림을 그리되, 처음의 일변량 때와는 다르게 fill=Origin 로 하여서 제조국별로 구분을 해보겠습니다.  position="dodge" 를 하면 수평으로 나란히 Origin별로 그려집니다.

 

> ggplot(Car_Type_Origin_cnt, aes(x=Type, y=Type_Origin_cnt, fill=Origin)) + 
+      geom_bar(stat="identity", position="dodge", colour="black") + 
+      scale_fill_brewer(palette=1) +
+      ggtitle("Bar Chart of Frequency by Car Type & Origin_1")

 

 

 

 

 

만약 position="dodge" 옵션을 지정하지 않으면 아래와 같이 세로로 올라탄 그래프 형식으로 제시됩니다.

 

> # without position="dodge" > ggplot(Car_Type_Origin_cnt, aes(x=Type, y=Type_Origin_cnt, fill=Origin)) + + geom_bar(stat="identity", colour="black") + # position="dodge" 미지정 + scale_fill_brewer(palette=1) + + ggtitle("Bar Chart of Frequency by Car Type & Origin, without podge option")

 

 




* 누적 막대 그래프 (stacked bar chart)


아래와 같이 생긴 데이터프레임에서 'id' 그룹별로 'bin_val' 값을 이용해서 누적 막대그래프 (stacked bar chart)를 그려보겠습니다. 이때 막대그래프의 색깔은 'color' 칼럼의 색으로 지정해서 그려보겠습니다. 


parsed.txt


df = read.table('parsed.txt', sep=',', header=T)

df <- transform(df, bin_val = bin_end - bin_start)

df

A data.frame: 12 × 7
idcolor_cdcolorbin_startbin_endbin_rangebin_val
<fct><fct><fct><int><int><fct><int>
AAAared0100[0,100)100
AAAbblue100200[100,200)100
AAAared200300[200,300)100
AAAbblue300400[300,400)100
BBBared0250[0,250)250
BBBbblue250350[250,350)100
BBBared350450[350,450)100
BBBbblue450550[450,550)100
BBBared550650[550,650)100
BBBbblue650750[650,750)100
BBBared750800[750,800)50
BBBbblue800910[800,910)110


library(ggplot2)

ggplot(df, aes(x=id, y=bin_val, fill=color, group=id)) + 

    geom_bar(stat="identity") +

    scale_fill_manual("legend", values = c("red" = "red", "blue" = "blue"))





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

다음번 포스팅에서는 원그림(Pie Chart)를 알아보겠습니다.

 

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

 

728x90
반응형
Posted by Rfriend
,

데이터셋을 받으면 제일 먼저 하는 일이 데이트의 구조를 파악하고, 변수명, 변수별 데이터 유형(숫자형, 문자형, 논리형), 결측값 여부, 이상치/영향치 여부, 데이터의 퍼진 정도/분포 모양 등을 탐색하게 됩니다.

 

하나의 연속형 변수에 대한 퍼진 정도/분포 모양와 이상치 여부를 쉽고 빠르게 파악할 수 있는 그래프로

 - 히스토그램(Histogram)

 - 커널 밀도 곡선 (Kernel Density Curve)

 - 박스그림(Box Plot)

 - 바이올린 그래프 (Violin Plot) 

등 이 있습니다.

 

 

 

[ 변수 개수 및 형태별 그래프 종류 ]

 

 

 

 

박스 그래프(Box Plot)는 Box-and-Whiskers Plot 이라고도 하는데요, 연속형 변수에 대해 min, Q1, median, Q3, max, lower whisker, upper whisker, outlier(*IQR(Inter-Quartile Range)의 1.5배 초과 시) 등을 한눈에 볼 수 있게 그린 그래프입니다. 연속형 한 변수의 분포의 중심, 퍼짐 정도(variance), 치우침 정도(skewness), 이상치/특이값(outlier) 존재 여부, 대칭성(symmetry) 등을 알 수 있는 매우, 매우, 매우 유용한 그래프라고 하겠습니다.  특히 박스 그래프는 이상치에 덜 민감한(robust) 중앙값, IQR 값을 사용한다는 점에서 이상치에 민감한 평균, 표준편차 대비 중심 경향과 퍼짐 정도를 파악하는데 있어 상대적으로 더 믿을 만 하다고 말할 수 있습니다.  상당히 유용하겠지요?!

 

 

[ 박스 그래프(Box-Whiskers Plot) 이해 ]

 

 

 

이번 포스팅에서는 먼저 ggplot2 패키지의 geom_boxplot() 를 활용해서 박스그래프(상자그림)을 그리는 방법에 대해서 알아보겠습니다. 

 

데이터는 MASS 패키지에 들어있는 Cars93 데이터 프레임 데이터 셋에서 가격(Price)과 자동차유형(Type) 변수를 활용하여 히스토그램을 그려보겠습니다. 

 

> # Cars93 데이터 프레임
> 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 ...

 

 

 

그럼, 먼저 Cars93 데이터 프레임 내 가격(Price) 변수 하나를 가지고 geom_boxplot()을 사용하여 박스 그래프(Box Plot)을 그려보겠습니다. 

 

> ggplot(Cars93, aes(x = 1, y = Price)) + # x=1 (임의의 값) 지정해줘야 함
+   geom_boxplot(width=0.8, outlier.size=3, outlier.shape=16, outlier.colour="red") + 
+   scale_x_continuous(breaks = NULL) + # x축 이름 생략
+   theme(axis.title.x = element_blank()) + # x축 구분자 생략
+   ggtitle("Box Plot")

 

 

 

 

 

 

다음으로, 차종별(Type)로 가격(Price) 변수에 대해 박스 그래프를 그려보겠습니다.  aes(x = factor variable) 을 입력해주면 되겠습니다.  여기서 주의할 점은 x 에다가 요인(factor)형 변수를 입력해줘야 합니다.  차종(Type)은 이미 요인형 변수이므로 그대로 입력하면 되겠구요, 만약 요인형 변수가 아니라면 x=factor(variable name)  이런 식으로 입력해주거나, 아니면 transform() 함수로 as.factor()로 데이터 형태를 요인형 변수로 아예 만든 후에 ggplot2 그래프를 그려도 되겠습니다.

 

> # 박스그림(Box Plot) - 차종별(Type)
> ggplot(Cars93, aes(x = Type, y = Price)) + 
+   geom_boxplot(width=0.8, outlier.size=3, outlier.shape=16, outlier.colour="red") +
+   ggtitle("Box Plot by Car Type")

 

 

 

 

 

 

위의 차종(Type)별 박스그림의 박스 가운데 선은 중앙값(Median)을 나타낸다고 했는데요, 이번에는 평균을 추가해 보도록 하겠습니다.  평균은 이상치에 민감하므로 아래 예제처럼 이상치가 있는 경우에는 중심화 경향을 나타내는 통계량으로 부적합하며, 박스 그림의 박스 가운데에 쳐진 선이 의미하는 중앙값(Median) 이 더 적합한 중심화 경향 지표라고 하겠습니다.

 

> # box plot with mean > ggplot(Cars93, aes(x = Type, y = Price)) + + geom_boxplot(width=0.8, outlier.size=3, outlier.shape=16, outlier.colour="red") + + stat_summary(fun.y="mean", geom="point", shape=21, size=3, fill="blue") + + ggtitle("Box Plot by Car Type, adding mean")

 

 

 

 


만약 차종(Type)과 생산지(Origin)의 두 기준/그룹으로 구분해서 박스 그래프를 그리고 싶다면 aes(fill = Origin) 을 추가해주면 됩니다. 



> ggplot(Cars93, aes(x = Type, y = Price, fill = Origin)) + 

+   geom_boxplot(width=0.8, outlier.size=3, outlier.shape=16, outlier.colour="red") +

+   ggtitle("Box Plot by Car Type and Origin")

 





 

다음으로 박스 그래프(Box Plot)와 커널 밀도 곡선(Kernel Density Curve)을 짬뽕해놓은 바이올린 그래프(Violin Plot)를 geom_violin()을 사용해 그려보겠습니다.  제공해주는 정보량이 많아서 바이올린 그래프(Violin Plot) 또한 유용한 편인데요, 실전에서는 별로 사용하지 않게 되는 그래프이기도 합니다. ^^;

 

> # Violin Plot with Box Plot
> ggplot(Cars93, aes(x = Type, y = Price)) + 
+   geom_violin() + 
+   geom_boxplot(width=0.1, fill="white", outlier.colour=NA) +
+   stat_summary(fun.y="median", geom="point", shape=21, size=2, fill="black") + 
+   ggtitle("Violin Plot by Car Type with Box Plot")

 

 

 

 

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

 

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

 

 

728x90
반응형
Posted by Rfriend
,

데이터셋을 받으면 제일 먼저 하는 일이 데이트의 구조를 파악하고, 변수명, 변수별 데이터 유형(숫자형, 문자형, 논리형), 결측값 여부, 이상치/영향치 여부, 데이터의 퍼진 정도/분포 모양 등을 탐색하게 됩니다.

 

하나의 연속형 변수에 대한 퍼진 정도/분포 모양와 이상치 여부를 쉽고 빠르게 파악할 수 있는 그래프로 히스토그램(Histogram), 커널 밀도 곡선 (Kernel Density Curve)과 박스그림(Box Plot), 바이올린 그래프 (Vilon Plot) 등 이 있습니다.

 

 

 

[ 변수 개수별 형태별 그래프 종류 ]

 

 

 

히스토그램(Histogram)은 연속형 변수를 일정한 구간(binwidth)으로 나누어서 빈도수를 구한 후에 이를 막대그래프로 그린 그래프입니다.

 

이번 포스팅에서는 먼저 ggplot2 패키지의 geom_histogram() 를 활용해서 히스토그램을 그리는 방법에 대해서 알아보겠습니다. 

 

 

데이터는 MASS 패키지에 들어있는 Cars93 데이터 프레임 데이터 셋에서 가격(Price)과 자동차유형(Type) 변수를 활용하여 히스토그램을 그려보겠습니다. 

 

> # Cars93 데이터 프레임
> 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 ...

 

 

ggplot2 패키지를 library()로 호출한 후에 ggplot() 함수의 +geom_histogram() 함수를 사용하여 default 옵션으로 히스토그램을 그리면 아래와 같습니다.

 

> ## 히스토그램 (Histogram)
> # install.packages("ggplot2") # ggplot2 패키지 설치
> library(ggplot2)
> 
> 
> # binwidth defaulted to range/30
> 
> ggplot(Cars93, aes(x=Price)) + 
+   geom_histogram()
stat_bin: binwidth defaulted to range/30. Use 'binwidth = x' to adjust this. 
 

 

 

 

위에 실행결과 콘솔창의 메시지를 보면 "stat_bin: binwidth defaulted to range/30. Use 'binwidth = x' to adjust this."이라는 메시지가 아래 보이는데요, 이는 binwidth를 설정하지 않아서 range/30 디폴트 기준으로 binwidth를 계산해서 그렸다는 뜻입니다.  아래에 실제 범위(range)를 구해서 30으로 나누었더니 1.816 이었고, 이 값을 geom_histogram(binwidth = 1.816) 옵션값이 입력해서 히스토그램을 그려보았더니 위와 같음을 알 수 있습니다.  

 

> range(Cars93$Price) # 7.4 ~ 61.9
[1]  7.4 61.9
> diff(range(Cars93$Price))  # 54.5
[1] 54.5
> diff(range(Cars93$Price))/30 # 1.816
[1] 1.816667
> 
> ggplot(Cars93, aes(x=Price)) + 
+   geom_histogram(binwidth=1.816) + 
+   ggtitle("Binwidth=1.816 ; Default, range/30")
 

 

 

 

히스토그램에서 중요하면서 어려운 문제 중의 하나가 bin 개수를 몇 개로 할 것인가, 다른 말로 binwidth를 몇  으로 할 것인가 입니다.  bin 개수가 너무 많으면 (즉, binwidth가 너무 좁으면) 이빨빠진 머리빗처럼 데이터의 분포 모양을 보기에 부적할 수가 있습니다.  반면에 bin 개수가 너무 적으면 (즉, binwidth가 너무 넓으면) 너무 많은 도수가 하나의 bin에 퉁쳐져서 막대기둥 한두개만 덩그라니 서있게 되어 이 또한 데이터의 분포 모양을 파악하는데 도움이 안되게 됩니다.  적절한 bin 개수를 선정하는게 중요한데요, 아래에 binwidth 를 조절해가면서 히스토그램을 그려봤습니다. 

 

 

> # histograms by various binwidths > > h1 <- ggplot(Cars93, aes(x=Price)) + + geom_histogram(binwidth=1.816) + + ggtitle("Binwidth=1.816 ; Default, range/30") > > h2 <- ggplot(Cars93, aes(x=Price)) + + geom_histogram(binwidth=5) + + ggtitle("Binwidth=5") > > h3 <- ggplot(Cars93, aes(x=Price)) + + geom_histogram(binwidth=10) + + ggtitle("Binwidth=10") > > h4 <- ggplot(Cars93, aes(x=Price)) + + geom_histogram(binwidth=20) + + ggtitle("Binwidth=20") > > h5 <- ggplot(Cars93, aes(x=Price)) + + geom_histogram(binwidth=30) + + ggtitle("Binwidth=30") > > h6 <- ggplot(Cars93, aes(x=Price)) + + geom_histogram(binwidth=40) + + ggtitle("Binwidth=40") > > > > > ##----------------- > ## multiplot function by knitr and Jekyll (author of Cookbook for R) > ## 아래 사용자정의 함수를 그대로 카피해서 사용하면 됨 > install.packages("grid")

> > multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) { + library(grid) + + # Make a list from the ... arguments and plotlist + plots <- c(list(...), plotlist) + + numPlots = length(plots) + + # If layout is NULL, then use 'cols' to determine layout + if (is.null(layout)) { + # Make the panel + # ncol: Number of columns of plots + # nrow: Number of rows needed, calculated from # of cols + layout <- matrix(seq(1, cols * ceiling(numPlots/cols)), + ncol = cols, nrow = ceiling(numPlots/cols)) + } + + if (numPlots==1) { + print(plots[[1]]) + + } else { + # Set up the page + grid.newpage() + pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout)))) + + # Make each plot, in the correct location + for (i in 1:numPlots) { + # Get the i,j matrix positions of the regions that contain this subplot + matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE)) + + print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row, + layout.pos.col = matchidx$col)) + } + } + } > ##----------------- > > # Multiple graphs on one page : multiplot > multiplot(h1, h2, h3, h4, h5, h6, cols=2)

 

 

 

 

 

참고로, 위처럼 한개의 화면에 여러개의 그래프를 배열하기 위해서 multiplot() 함수(by knitr and Jekyll)를 사용하였습니다.  binwidth = 5 일 때가 위의 6개 그래프 중에서는 상대적으로 가장 적합해 보이므로 아래 예제부터는 binwidth = 5 를 사용하겠습니다.

 

 

위의 히스토그램을 보면 거무튀튀하니 그다지 색깔이 아름답지는 않지요?  그러면 이번에는 색 채우기, 경계선 색 지정하기를 해보겠습니다. 

 

> # 채우기 색, 경계선 색 : geom_histogram(binwidth, fill, colour)
> ggplot(Cars93, aes(x=Price)) + 
+   geom_histogram(binwidth=5, fill = "blue", colour = "black") + 
+   ggtitle("Binwidth=5, fill = blue, colour = black")

 

 

 

 

 

마지막으로, facet_grid() 를 써서 요인(factor)/집단/그룹별로 히스토그램을 구분해서 그려보도록 하겠습니다.  단, facet_grid()에 들어가는 변수는 요인(factor)형 변수이어야 합니다.

 

> # 요인(factor) 여부 확인, levels 확인
> class(Cars93$Type); levels(Cars93$Type) 
[1] "factor"
[1] "Compact" "Large"   "Midsize" "Small"   "Sporty"  "Van"    
> 
> # 요인/집단/그룹(factor)별로 나누어서 히스토그램 그리기
> ggplot(Cars93, aes(x=Price)) + 
+   geom_histogram(binwidth=5, fill = "blue", colour = "black") + 
+   ggtitle("Binwidth=5, fill = blue, colour = black, group by Type") + 
+   facet_grid(Type ~ .)

 

 

 

 

위의 히스토그램처럼 자동차의 유형(Type)인 'Compact', 'Large', 'Midsize', 'Small', 'Sporty', 'Van' 의 6개 유형별로 가격(Price)의 히스토그램을 그려보면 서로 한눈에 비교가 가능하니 매우 유용하다고 하겠습니다.

 

 

참고로, 위처럼 가로로 비교를 하는 것이 아니라 세로로 세워서 그래프를 그린 후에 비교를 하려면

+ facet_grid(. ~ Type) 처럼 괄호안의 기입 순서를 바꾸어주면 됩니다.  단, 아래에 예시 그래프를 보면 알겠지만, 차종별로 가격의 분포를 비교하기에는 아래 처럼 그래프를 그려서는 안되겠지요?  분석 목적에 맞게 가로로 비교할지, 세로로 비교할지 잘 선택해서 사용하시기 바랍니다.

 

> # 요인/집단/그룹(factor)별로 나누어서 히스토그램 그리기
> ggplot(Cars93, aes(x=Price)) + 
+   geom_histogram(binwidth=5, fill = "blue", colour = "black") + 
+   ggtitle("Binwidth=5, fill = blue, colour = black, group by Type") + 
+   facet_grid(. ~ Type) # 수직 
 
 

 

 

 

 

다음으로, 커널 밀도 추정함수를 가지고 그린 커널 밀도 곡선(kernel density curve)를 그려보겠습니다. 

 

히스토그램은 빈도를 가지고 그리며, geom_histogram()함수를 사용하며, 계단식으로 각이 져 있는데요,

 

커널 밀도 곡선(kernel density curve)은 확률(모두 더하면 1)을 가지고 그리고, geom_density() 함수를 사용하며, smoothing 된 곡선으로 되어 있습니다.

 

> # 가격 커널 밀도 곡선(Kernel Density Curve) > ggplot(Cars93, aes(x=Price)) + + geom_density(fill = "yellow", colour=NA, alpha=.5) + # alpha 반투명 + geom_line(stat="density") + + expand_limits(y=0) + + ggtitle("Kernel Density Curve")

 
> # 차종별 가격 커널 밀도 곡선(Kernel Density Curve)_중복
> ggplot(Cars93, aes(x=Price, colour = Type)) + 
+   geom_density(fill = NA) + 
+   geom_line(stat = "density") + 
+   expand_limits(y = 0) + 
+   ggtitle("Kernel Density Curve by Car Type_overlap")
 

 

> # 차종별 가격 커널 밀도 곡선(Kernel Density Curve)_수평 > ggplot(Cars93, aes(x=Price)) + + geom_density(fill = "yellow", colour=NA, alpha=.5) + + geom_line(stat="density") + + expand_limits(y=0) + + ggtitle("Kernel Density Curve by Car Type") + + facet_grid(Type ~ .) + + xlim(10, 40) # X축 범위를 지정해줬더니 40 초과하는 값 짤렸다고 경고메시지 뜸 Warning messages: 1: Removed 3 rows containing non-finite values (stat_density). 2: Removed 10 rows containing non-finite values (stat_density). 3: Removed 3 rows containing non-finite values (stat_density). 4: Removed 10 rows containing non-finite values (stat_density).

 

 

 
 

 

R ggplot2의 커널밀도곡선에서 최대 피크값 좌표를 구하고 수직선을 추가하는 방법은 https://rfriend.tistory.com/485 를 참고하세요. 


 


히스토그램과 커널 밀도 곡선을 겹쳐서 그려보도록 하겠습니다.

 

> # Histogram + Kernel Density Curve
> ggplot(Cars93, aes(x=Price, y=..density..)) + 
+   geom_histogram(binwidth=5, fill = "blue", colour="white", alpha=0.5) + 
+   geom_density(fill = NA, colour=NA, alpha=0.8) + 
+   geom_line(stat="density") + 
+   expand_limits(y=0) + 
+   ggtitle("Histogram + Kernel Density Curve")

 

 

 





히스토그램의 bin width를 수동으로 설정해주고, bin별로 색깔을 다르게 해서 히스토그램을 그려보겠습니다. 



#----------------

# histogram with variable size of bin width and different colors per bins using ggplot2

#----------------


# sample data frame

mydf <- data.frame(var = c(1100, 10000, 100000, 190000, 110000, 220000, 550000, 701000, 790000))


# numeric notation for large numbers

options(scipen = 30)


library("ggplot2")


# fill color with different colors per bins

mydf $group <- ifelse(mydf $var < 10000, 1, 

                          ifelse(mydf $var < 100000, 2, 

                                 ifelse(mydf $var < 200000, 3, 

                                        ifelse(mydf $var < 500000, 4, 5))))


# breaks of bin

bins <- c(1000, 10000, 100000, 200000, 500000, 800000)


# draw histogram with variable size of bin width and different colors per bins

ggplot(mydf, aes(x= var)) +

  geom_histogram(data=subset(mydf, group==1), breaks = c(1000, 10000), fill="black") +

  geom_histogram(data=subset(mydf, group==2), breaks = c(10000, 100000), fill="yellow") +

  geom_histogram(data=subset(mydf, group==3), breaks = c(100000, 200000), fill="green") +

  geom_histogram(data=subset(mydf, group==4), breaks = c(200000, 500000), fill="blue") +

  geom_histogram(data=subset(mydf, group==5), breaks = c(500000, 800000), fill="red") +

  scale_x_continuous(breaks = bins, limits = c(1000, 800000)) +

  xlab("variable 1") + 

  ylab("count") +

  ggtitle("Histogram with different size of bin width and colors") + 

  theme(plot.title = element_text(hjust = 0.5, size = 14))






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

 

다음번 포스팅에서는 Box Plot 을 소개하겠습니다.

 

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

 

 

728x90
반응형
Posted by Rfriend
,

R에는 다양한 그래프 패키지들이 있는데요, 그 중에서도 가장 많이 사용되는 패키지 3개를 들자면 Base Graphics package(Base package로서 별도 설치 필요 없음), Lattice package(별도 설치/호출 필요), ggplot22 package(별도 설치/호출 필요) 를 꼽을 수 있습니다. 

 

시중에 나와있는 많은 수의 R 관련 책에서는 Base Graphics package를 가지고 R 그래프 소개를 많이 하고 있는 편이구요, R 그래프/시각화 전문 책에서는 ggplot2에 무게중심을 두고 쓰여진 책이 많은 편인데요, 저는 ggplot2를 중심으로 'R 그래프/시각화' 카테고리에 포스팅을 해보려고 합니다.  (ggplot2를 주로 쓰고, 부수적으로 Base Graphics package, Lattice package 의 그래프 R script를 참고용으로 간략히 소개하는 식으로 글을 써나갈까 합니다.)

 

 

[ R 그래프 패키지 ]

 

 

 

일단 Base Graphics, Lattice, ggplot2에 대해 간략히 장단점을 짚어보고, 그 다음으로 ggplot2의 문법에 대해도 간략히 소개하겠습니다.

 

 

 package

author 

장점 

단점 

 Base Graphics

 R Core Team and contributors worldwide

- 별도 설치/호출 필요 없음

- 쉽고 편함

- 사람이 생각하는 방식처럼
 순차적으로 그래프를 쌓아감

- 한번 실행하면 취소 못함

- 미리 계획 필요
  (예: 세로 축 scale)

 Lattice

Deepayan Sarkar

 - 전체 데이터를 보고 세로축,
  마진, 여백 자동 계산 편리

- 여러개 그래프를 동시에

 하나의 화면에 그릴때 편리

- 순차적으로 그래프 쌓아가는 것 안됨

- 직관적이지 못함

 ggplot2

Hadley Wickham

- Base Graphics 와 Lattice의

 장점만 골라놨음

- 그래프 문법에 따라 체계적,

 통계적 조건 등 부여하여
 고급 그래프 생성 가능

- 처음 배우기가 상대적으로 

 어려움

 (단, 일단 문법이 익으면 

 그때부터는 생산성 더 높음) 

 

 

위 표에서 언급했듯이 ggplot2가 Base Graphics의 interactive하게 하나씩 생각하고, 그려보고, 눈으로 확인하고, 그래프 위에 하나 더 쌓아서 그려보고...하는 직관적인 방식의 장점과, Lattice의 여러개의 집단 전체 데이터의 범위를 보고 축/마진/여백 등을 자동 설정해주는 등의 편리함의 장점을 따다가 '그래프 문법(Grammer of Graphics)'이라는 체계적인 방식으로 만들어진 패키지이다 보니 ggplot2를 추천하는 바입니다.  (ps. 그냥 R로 그래프 몇 개 그리다 말거면, 그냥 일회성으로 쉽게 뚝딱 그래프 그리고 말거면 편하게 Base Graphics 패키지 활용하는게 견적이 더 좋을 수도 있겠습니다.  하지만 그래프 많이 쓰는 사용자라면 Base Graphics는 예외 케이스가 많아서 나중에는 생산성이 ggplot2에 비해 떨어집니다.)

 

그래프의 문법(Grammer of Graphics)라고 말씀드렸는데요, 그래프를 그리는데에도 "동사(Verb)", "명사(Noun)", "형용사(Adjective)"라는 체계를 갖추어 문법을 만들었다고 보면 되겠습니다.

 

 

"In brief, the grammer tells us that a statistical graphic is a mapping from data to aesthetic attirbutes (colour, shape, size) of geometric objects (points, lines, bars). The plot may also contain statistical transformations of the data and is drawn on a specific coordinate system"

       - source : ggplot2 book

 

[ R ggplot2 Components ]

 

 

 

lattice와 ggplot2 패키지 설치 및 패키지 호출은 아래와 같이 하면 됩니다. 

 

## lattice 패키지 설치/호출

install.packages("lattice")
library(lattice)

 

## ggplot2 패키지 설치/호출

install.packages("ggplot2")
library(ggplot2) 

 

 

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

 

 

728x90
반응형
Posted by Rfriend
,

데이터 분석을 시작할 때 가장 처음 하는 일이 탐색적 데이터 분석(EDA, Exploratory Data Analysis) 입니다.  탐색적 데이터 분석을 할 때 데이터 형태와 구조를 파악하고, 많은 요약/기술 통계량도 뽑아보고, 그래프도 그려보면서 데이터의 형태와 특성, 분포 등을 파악하곤 합니다.  즉, 데이터 분석의 시작은 그래프라고 해도 관언이 아니겠습니다.

 

R의 장점 중에 하나가 바로 출판물에 사용해도 좋을 만큼 미적으로도 아름다우면서도 어떤 모양, 조건으로도 그래프를 그려서 인사이트를 뽑아내기에 유용한 다양한 그래프/시각화를 지원한다는 점입니다.  R의 강력한 그래프/시각화 기능 때문에 R을 이용한다고 하는 사용자도 있을 정도니깐요. 

 

 

왜 그래프/시각화가 중요할까요?  

 

1) 그래프/시각화는 숫자나 표보다 직관적으로 이해하기가 쉽습니다. 

 

2) 그래프/시각화가 이해하기 쉬우므로 의사소통하기에도 쉽고 효과적입니다.  마치 몇 페이지의 호소문이나 사설보다 한 장의 역사 사진이 사람들에게 울림이 더 큰 경우가 있듯이요.

 

3) 자칫 통계량만을 뽑아보고 의사결정했을 때 자칫 오류를 범할 수 있는데요, 이때 그래프/시각화를 통해 데이터의 분포, 형태나 변수간의 관계에 대해서 중요한 통찰을 얻을 수 있습니다.

 

 

영국의 통계학자 Francis Anscombe 가 "Graphs in Statistical Analysis"(1973년)라는 논문 (링크를 클릭하면 pdf 다운로드 가능)에서 왜 통계분석을 할 때 반드시 통계량 뿐만 아니라 그래프 분석을 병행해야 하는지를 보여주는 데이터 예를 듭니다.  (x1, y1), (x2, y2), (x3, y3), (x4, y4) 변수들로 구성된 4개 그룹이 있는데요, x1~x4, y1~y4 끼리 평균, 표준편차가 같고,  (x1, y1), (x2, y2), (x3, y3), (x4, y4) 변수들 간의 상관계수와 회귀모형이 같습니다.  이정도면 같은 모집단에서 뽑은 같은 성격/특징/형태를 보이는 4개의 표본이라고 지레짐작하기 쉬운데요, 그래프를 그려보면 4개의 표본이 날라도 너무 달라서 깜짝 놀라게 됩니다.   

 

 

He later became interested in statistical computing, and stressed that "a computer should make both calculations and graphs", and illustrated the importance of graphing data with four data sets now known as Anscombe's quartet

 

* source: https://en.wikipedia.org/wiki/Frank_Anscombe

 

 

 

 

 

R에 base패키지인 datasets 패키지에 'anscombe'라는 데이터 프레임이 기본 탑재되어 있는 데이터 셋이어서 쉽게 불러다가 예시를 들어보겠습니다.  str()함수로 데이터 구조를 보니 8개 변수에 11개 관측치로 구성되어있는 데이터 프레임이고, x1, x2, x3, x4, y1, y2, y3, y4 모두 숫자형(numeric) 변수들이군요.

 

> ## Anscombe's Quartet of ‘Identical’ Simple Linear Regressions > # Four x-y datasets which have the same traditional statistical properties > # (mean, variance, correlation, regression line, etc.), > # yet are quite different. > > # 데이터 구조 > str(anscombe) 'data.frame': 11 obs. of 8 variables: $ x1: num 10 8 13 9 11 14 6 4 12 7 ... $ x2: num 10 8 13 9 11 14 6 4 12 7 ... $ x3: num 10 8 13 9 11 14 6 4 12 7 ... $ x4: num 8 8 8 8 8 8 8 19 8 8 ... $ y1: num 8.04 6.95 7.58 8.81 8.33 ... $ y2: num 9.14 8.14 8.74 8.77 9.26 8.1 6.13 3.1 9.13 7.26 ... $ y3: num 7.46 6.77 12.74 7.11 7.81 ... $ y4: num 6.58 5.76 7.71 8.84 8.47 7.04 5.25 12.5 5.56 7.91 ... >  

 

 

관측치가 11개 밖에 안되므로 모두 불어와 보면 아래와 같습니다. 이처럼 숫자만 봐서는 데이터 분포, 특성, 변수간 관계가 어떠한지 한눈에 안들어오고 이해가 잘 안되지요? 

 

> # 데이터 view
> anscombe
   x1 x2 x3 x4   y1  y2   y3   y4
1  10 10 10  8  8.0 9.1  7.5  6.6
2   8  8  8  8  7.0 8.1  6.8  5.8
3  13 13 13  8  7.6 8.7 12.7  7.7
4   9  9  9  8  8.8 8.8  7.1  8.8
5  11 11 11  8  8.3 9.3  7.8  8.5
6  14 14 14  8 10.0 8.1  8.8  7.0
7   6  6  6  8  7.2 6.1  6.1  5.2
8   4  4  4 19  4.3 3.1  5.4 12.5
9  12 12 12  8 10.8 9.1  8.2  5.6
10  7  7  7  8  4.8 7.3  6.4  7.9
11  5  5  5  8  5.7 4.7  5.7  6.9

 

 

이럴때 보통 기술통계량을 보곤 하지요.  중심화 경향을 나타내는 평균과 퍼짐 정도를 나타내는 표준편차를 살펴보겠습니다.  x1~4x 의 평균과 표준편차가 같고, y1~y4의 평균과 표준편차가 같게 나왔습니다.

 

> # 변수별 평균, 표준편차
> options(digits = 2) # 소수점 자리 설정
> 
> sapply(anscombe, mean) # mean
 x1  x2  x3  x4  y1  y2  y3  y4 
9.0 9.0 9.0 9.0 7.5 7.5 7.5 7.5 
> 
> sapply(anscombe, sd) # standard deviation
 x1  x2  x3  x4  y1  y2  y3  y4 
3.3 3.3 3.3 3.3 2.0 2.0 2.0 2.0 

 

 

이번에는 (x1, y1), (x2, y2), (x3, y3), (x4, y4) 변수들 간의 상관계수를 살펴보겠습니다.  4개 집단 모두 상관계수가 0.82로 동일하게 나왔습니다.

 

> # x, y 상관계수 (x, y correlation)
> attach(anscombe)
> cor(x1, y1)
[1] 0.82
> cor(x2, y2)
[1] 0.82
> cor(x3, y3)
[1] 0.82
> cor(x4, y4)
[1] 0.82
> detach(anscombe) 

 

 

다음으로 4개의 각 집단별로 종속변수 y, 독립변수 x 로 해서 단순회귀모형을 적합시켜보겠습니다.

결과는 4개 집단 모두 y = 3.0 + 0.5*x  로 나왔습니다. (y절편 3.0, 변수 x의 계수 0.5)

 

 

> # Simple Linear Regrassions by 4 groups
> attach(anscombe)
The following objects are masked from anscombe (pos = 3):

    x1, x2, x3, x4, y1, y2, y3, y4

> lm(y1 ~ x1)

Call:
lm(formula = y1 ~ x1)

Coefficients:
(Intercept)           x1  
        3.0          0.5  

> lm(y2 ~ x2)

Call:
lm(formula = y2 ~ x2)

Coefficients:
(Intercept)           x2  
        3.0          0.5  

> lm(y3 ~ x3)

Call:
lm(formula = y3 ~ x3)

Coefficients:
(Intercept)           x3  
        3.0          0.5  

> lm(y4 ~ x4)

Call:
lm(formula = y4 ~ x4)

Coefficients:
(Intercept)           x4  
        3.0          0.5

 

 

위에 살표본 바를 종합해보면,

  • (1) 4개 집단의 x변수들의 평균, 표준편차가 같고, y변수들의 평균, 표준편차가 같습니다.
  • (2) 4개 집단의 x변수와 y변수들 간의 상관계수가 동일합니다.
  • (3) 4개 집단의 x독립변수와 y종속변수간 단순회귀모형 적합결과 x변수 계수와 y절편 값이 같은 동일 모델로 적합되었습니다.

이쯤되면 4개 집단의 x, y 변수 데이트들이 동일한 모집단에서 랜덤하게 추출된 동일한 형태/분포/특성을 지닌 데이터라고 믿어도 되지않겠습니까? 

 

 

그런데, 그게 아닙니다.

 

아래의 4개 그룹별 x변수, y변수 산점도를 살펴보시지요.  그래프를 그려서 눈으로 보니 4개 집단이 다르지요?  통계량들은 이 4개의 집단이 같다고 말하고 있지만, 그래프는 이 4개 집단이 다르다고 말하고 있습니다.  이래서 서양 표현에 "One Look Is Worth A Thousand Words" 말이 있는 거지요.

 

> # Scatter Plot & Simple Linear Regression Line
> par(mfrow = c(2,2)) # 2 x 2 layout
> 
> attach(anscombe)
The following objects are masked from anscombe (pos = 3):

    x1, x2, x3, x4, y1, y2, y3, y4

> 
> plot(x1, y1); abline(lm(y1~x1), col = "blue", lty = 3)
> plot(x2, y2); abline(lm(y2~x2), col = "blue", lty = 3)
> plot(x3, y3); abline(lm(y3~x3), col = "blue", lty = 3)
> plot(x4, y4); abline(lm(y4~x4), col = "blue", lty = 3)
> 
> detach(anscombe)

 

 

 

 

 

시각화가 왜 중요한지, 왜 인사이트를 뽑아내는데 효과적인지, 왜 그동안 몰랐던 사실을 발견하는데 큰 역할을 하는지에 대한 사례를 하나 더 들자면, TED에서 유명인사가 된 한스 로즐링(Hans Rosling)의 Dynamic Visualization 을 들고 싶습니다.  Youtube에서 "TED & Hans Rosling" 이라는 키워드로 검색을 해보면 많은 동영상이 검색이 될거예요.  그중에 하나를 아래에 링크를 걸어놓습니다.  연도별로 그래프가 움직이면서 변화하는데요, 시간이라는 차원을 그래프에 녹여낸 아주 기가 막힌 시각화 방법이 되겠습니다.  거기다가 한스 로즐링의 열정적인 설명까지 곁들여져서 한편의 인상적인 통계학 공연이 완성이 됩니다. ^^

마음 편하게 한번 감상해 보시지요.  (아래 동영상 말고도 한스 로즐링거 많습니다)

 

 

 

 

이밖에도 구글 지도에 매쉬업을 해서 시각화를 한다든지, 소셜 네트워크 분석을 한다든지, 워드 크라우드 분석을 한다든지, 타이타닉 생존자에 대한 모자이크 그림 분석을 한다든지, 회귀분석을 하기 전 변수들 간 산점도 행렬 그래프 분석을 한다든지, 정규성 가정 검증을 위해 Q-Q plot을 그려본다든지, 분포 형태를 파악하기 위해 히스토그램이나 박스그림을 그려본다든지, 등, 등, 등 ... 그래프/시각화가 얼마나 중요한지, 왜 필요한지를 알 수 있는 사례, 예시는 무수히 많습니다.  앞으로 하나씩 차근차근 포스팅을 해나가도록 하겠습니다.

오늘 포스팅의 결론은, "반드시 그래프/시각화를 병행하라!" 가 되겠습니다.

 

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

 

 

728x90
반응형
Posted by Rfriend
,

데이터 변환 방법으로서

(1) 표준화

(2) 정규분포화

(3) 범주화

     - 이산형화

     - 이항변수화

(4) 개수 축소

(5) 차원 축소

     - 주성분분석

     - 요인분석 

(6) 시그널 데이터 변환

   - 푸리에 변환 (FFT: Fast Fourier Transform)

    - 웨이블릿 변환(Wavelet Transform)

의 6개 구분 중에서

 

전파, 진동, 소리, 파도, 빛 등 시간(time domain)에 따라 주기성(periodicity)을 띠면서 파형을 형성하는 데이터를 주파수(spectrum domain) 대역별로 세기로 변환하는 푸리에 변환(Fourier Transform)에 대해서 알아보겠습니다.  참고로, 푸리에(Jean-Baptiste Joseph Fourier, 1768~1830)는 프랑스의 수학자이자 물리학자로서, 푸리에 변환은 바로 이분의 이름을 딴 것이랍니다.  저 아래에 애니메이션의 오른쪽 하단에 사진이 푸리에가 되겠습니다.

 

 

 

푸리에 변환은 물리나 공업 분야에서 폭넓게 사용되고 있기에, 특히 시그널 데이터, 기계 데이터, 센서 데이터 등의 주기성을 띤 데이터를 분석하는 분이라면 반드시 알아야 할 변환이라고 하겠습니다.

 

푸리에 변환을 이해하려면 삼각함수(특히 사인함수, 코사인함수), 미적분, 함수의 사칙연산, 함수의 직교성, 푸리에 급수와 계수 등에 대해서 알아야 하는데요, 어려운 수학 공식은 다른 교재를 참고하시기 바라며, 이번 포스팅에서는 직관적으로 이해할 수 있는 그림과 예시를 들어서 가급적 쉽게 설명을 해보겠습니다.

 

아래의 그림처럼 시간에 따른 진폭 데이터를 => 주파수별 세기의 데이터로 변환하는 작업을 푸리에변환이라고 하며, 그 반대를 푸리에역변환이라고 합니다.

 

 

[ 푸리에변환과 푸리에역변환 ]

 

 

 

먼저, 일정한 괘도를 회전하는 운동을 하는 시간함수로 나타내는 법에 대해서 알아보겠습니다.  아래 애니메이션의 왼쪽이 일정한 속도로 반지름이 일정한 괘도를 회전하는 운정이 되겠구요, 오른쪽이 이 회전운동을 시간축에 옮겨놓았을 때의 모양입니다.  전형적인 코사인(cosine) 형태를 띠고 있습니다.

 

* 출처: http://www.di.fc.ul.pt/~jpn/r/fourier/fourier.html

 

 

 

아래는 왼쪽의 반지름 거리와 시간 주기가 다른 4개의 회전운동을 오른쪽에 시간 축에 진폭을 나타낸 그래프가 되겠습니다.  아래 보는 것처럼 회전운동은 시간을 축으로 해서 진폭이 변화하는 값을 사인(sine) 또는 코사인(cosine) 함수로 나타낼 수 있습니다.

 

* 출처: http://www.di.fc.ul.pt/~jpn/r/fourier/fourier.html

 

 

 

이렇게 주기성을 띤 회전운동을 시간함수로 나타낼 수 있는데요, 이 시간함수는 사실 여러개의 주파수를 띤 시간함수들이 합해진 것입니다.  아래 예시로 든 그림에서는 주기(주파수)가 다른 3개의 시간함수가 함쳐져서 1개의 시간함수를 형성하고 있는데요, 주파수별로 필터링을 해서(주파수 성분을 구한다고 함) 세기가 큰 (peaks) 주파수를 헤아리면 되겠습니다. 시간함수를 알면 주파수 스펙트럼을 구할 수 있고, 주파수 스펙트럼을 알면 이들을 합쳐서 시간함수를 구할 수 있게 됩니다.

 

 

[ 시간함수와 주파수 스펙트럼의 관계 ]

 

* 그림 출처: aragec.com556

 

 

주파수는 1초에 파동 cycle이 몇 번 반복되느냐를 나타내는 말로서, 단위는 Hz(헤르츠)를 사용합니다.  아래의 3개의 파형을 예로 들면, 3개 파형 모두 진폭은 -1 ~ +1 로 동일한 반면에 주기는 모두 다릅니다. (즉, 주파수가 모두 다름)  첫번째 파형은 1초에 2회 주기이므로 주파수는 2Hz, 두번째 파형은 1초에 4회 주기이므로 4Hz, 세번째 파형은 1초에 6회 주기이므로 6Hz 주파수가 되겠습니다.

 

 

[ 주기와 진폭 ]

 

참고로, 악기 음 조율할 때 사용하는 소리굽쇠는 440Hz 의 '라'음을 낸답니다. 1초에 주기가 440회 진동이 발생한다는 뜻입니다. 서울/경기 지역의 '별이 빛나는 밤에' 라디오 주파수가 95.9kHz 인데요, 이는 전파가 1초에 9만 5천9백번 진동한다는 의미입니다.  음악에서는 주파수가 낮을 수록(현이 굷고 길수록) 저음이 나고, 주파수가 높을 수록(현이 가늘고 짧을 수록) 고음이 납니다.

 

이제 R을 가지고 1초에 2pi 만큼을 단위 시간 구간으로 해서 진폭과 주기를 달리한 4개의 사인함수 그래프도 그려보고, FFT (Fast Fourier Transform) 변환 실습을 해보도록 하겠습니다.

 

> # 사인함수 파라미터 설정 > x <- seq(0, 2*pi, by=pi/100) > > amp.1 <- 2 # 진폭(amplitude) 2 > amp.2 <- 2 # 진폭 2 > amp.3 <- 5 # 진폭 5 > amp.4 <- 5 # 진폭 5 > > wav.1 <- 1 # 주기(wave-length, cycle) 1 > wav.2 <- 2 # 주기 2 > wav.3 <- 3 # 주기 3 > wav.4 <- 7 # 주기 7 > > # 사인함수 생성 > signal.1 <- amp.1*sin(wav.1*x) # 진폭 2 & 주기 1인 사인함수 > signal.2 <- amp.2*sin(wav.2*x) # 진폭 2 & 주기 2인 사인함수 > signal.3 <- amp.3*sin(wav.3*x) # 진폭 5 & 주기 3인 사인함수 > signal.4 <- amp.4*sin(wav.4*x) # 진폭 5 & 주기 7인 사인함수 >

 

 

4개의 사인함수를 각각 순서대로 그려보면 아래와 같습니다. 첫번째와 두번째 그래프는 진폭(높이, y축)이 '2'로서 동일하고, 세번째와 네번째 그래프는 진폭이 '5'로서 동일합니다.  1초에 몇번의 주기가 있는지, 즉 주파수에 해당하는 주기는 순서대로 1, 2, 3, 7로서 뒤로 갈수록 증가하는 사인함수 그래프로서, 주파수가 커질수록 1초당 주기 갯수가 많아집니다 (진동 회수가 증가).

 

> # 사인함수 시간에 따른 그래프 > par(mfrow = c(1,4)) > plot(x, signal.1, type='l', ylim=c(-5,5)); abline(h=0, lty=3) # 진폭 2 & 주기 1인 사인함수 > plot(x, signal.2, type='l', ylim=c(-5,5)); abline(h=0, lty=3) # 진폭 2 & 주기 2인 사인함수 > plot(x, signal.3, type='l', ylim=c(-5,5)); abline(h=0, lty=3) # 진폭 5 & 주기 3인 사인함수 > plot(x, signal.4, type='l', ylim=c(-5,5)); abline(h=0, lty=3) # 진폭 5 & 주기 7인 사인함수



 

 

다음으로, 위 4개의 시간에 따른 사인함수를 합한 후에 그래프로 나타내보겠습니다.  위 4개의 개별 시간에 따른 사인함수가 주기성을 띠므로 아래의 1개로 합쳐진 시간함수도 일정한 주기성을 띠게 됩니다.  (푸리에 변환은 지금 하는 작업과는 거꾸로, 여러개의 시간함수들이 합쳐진 시간함수를 개별 시간함수들로 분해 해서 각 개별 시간함수들의 주파수 성분을 구하는 것입니다.  푸리에 변환할 때는 파형이 주기성을 띤다고 가정하고 변환을 진행합니다.) 

 



> # 사인함수 4개 합치기 (sine function summation) > signal.1234 <- signal.1 + signal.2 + signal.3 + signal.4 > head(signal.1234, n=30) [1] 0.000000 1.749660 3.442051 5.022470 6.441235 7.655888 8.633062 9.349913 9.795059 9.968964 [11] 9.883774 9.562593 9.038247 8.351592 7.549453 6.682284 5.801674 4.957812 4.197038 3.559593 [21] 3.077684 2.773944 2.660382 2.737852 2.996074 3.414214 3.961977 4.601179 5.287703 5.973757 > > # 사인함수 4개 합친 그래프 그리기 > par(mfrow = c(1,1)) > plot(x, signal.1234, type='l', main = "Sum of signal.1&2&3&4", + xlab = "Time", ylab = "Amplitude") > abline(h=0, lty=3)
 

 

 

 

R로 푸리에 변환할 때는 stats 패키지의 fft() 함수를 사용합니다.  fft()함수를 적용한 값을 관측치 개수의 반개((N-1)/2 = (201-1)/2 = 100 )로 나누어서 표준화를 시켜주고, abs()함수를 적용해 절대값을 취하게 됩니다.  plot()으로 그래프를 그려보면 좌우로 대칭인 그래프가 그려지는데요, 절반을 기점으로 해서 똑같은 대칭 그래프라서 왼쪽의 값(그래프)만을 사용하면 되겠습니다.  

 

이 예제에서는 주파수가 1Hz, 2Hz, 3Hz, 7Hz 짜리 4개의 사인함수를 더한 것이었으므로 1Hz, 2Hz,  3Hz, 7Hz 지점에서 피크(peak)를 치는 스펙트럼을 보여줄 겁니다.  따라서 1~20Hz 까지만 뽑아서 자세히 본 그래프가 두번째 그래프가 되겠습니다. 

 

> # 푸리에 변환 (FFT: Fast Fourier Transform)
> library(stats) > N <- length(x) > > fft_x_abs <- abs(fft(signal.1234)/((N-1)/2)) # 표준화, 절대값 > plot(fft_x_abs, type="h") >






 
> # 앞의 주파수 20개만 그래프 그려보기 > plot(fft_x_abs[1:20], type="h")





 

 

참고로, Python 을 이용한 스펙트럼 분석은 https://rfriend.tistory.com/690 를 참고하시기 바랍니다. 

 

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

 

728x90
반응형
Posted by Rfriend
,

요즘 빅데이터(Big Data), 데이터 과학자(Data Scientist) 에 대한 관심이 어느때보다 높은 듯 합니다.  데이터 과학자에 대한 자질, 역량에 대한 이야기도 많은데요, 많은 경우 분석 역량, 분석기법에 대한 지식에 대해서 할애를 하고 있습니다.  데이터 과학자라고 하는 사람이 데이터 처리도 못하고, 분석기법에 대해서도 모른다는 것은 마치 요리사가 칼을 잘 다루지도 못하고, 요리 레서피도 모른다는 것이기 때문에 이는 합당한 강조라고 하겠습니다.

 

반면에 데이터 분석의 처음 시작이라고 할 수 있는 '호기심', '(좋은) 질문하기', '데이터를 능동적으로 찾고 수집하고 조합해보기', '상식과 통념을 의심해보고 도전해보기', '일상의 실생활 속에서 데이터로 생각해보기' 등에 대해서는 상대적으로 관심이 덜 한 듯 합니다. 

 

위 전자(분석기법)를 잘하는 데이터 과학자는 현업이 주는 요건에 대해서 분석만 해주는 수동적 분석대행자로 전락할 위험이 있는 반면, 후자(호기심, 질문)까지 겸비한 데이터 과학자는 데이터 속에 숨겨진 현업이 몰랐던 숨겨진 패턴과 인사이트까지 찾아낼 가능성이 더 높다고 생각합니다.  분석에 임하는 즐거움, 행복지수도 역시 후자가 더 높을 것이라고 보구요.

 

오늘 리뷰할 책 '괴짜경제학 (FREAKONOMICS)'은 바로 이 후자에 대한 모범사례가 되겠습니다.  (네이트 실버의 '신호와 소음 (The Signal and The Noise)'도 모범사례로 추천)

 

 

 

책의 표지를 보면 '겉은 사과인데 속을 칼로 썰어서 실제 확인해 보니 귤'인 사진이 나오지요?  상식과 통념이라는 겉(사과)에 현혹이 되면 실제의 본질(귤)을 못본다는 은유가 되겠습니다.

 

책의 저자는 두명이예요.  스티븐 레빗은 시카고 대학교 경제학자이고 스티븐 더브너는 뉴욕타임스 기자예요. 

 

 
* 사진출처: '괴짜경제학' 책 표지   * 사진출처: '괴짜경제학' 책 표지

 

경제학자 스티븐 레빗이 말하는 경제학에 대한 생각을 들어보면 '괴짜경제학'이라는 신종 용어가 왜 생겼는지 조금 이해가 될 것 같습니다. 

 

p7. 레빗의 견해에 따르면, 경제학은 해답을 얻는데 유용한 도구들을 보유하고 있는 반면, 흥미로운 질문은 심각할 정도로 부족한 학문이다. 레빗이 지닌 특수한 재능은 바로 그러한 질문을 던지는 능력이다.  

 

레빗은 경제학에서 데이터를 가지고 해답을 얻는 도구를 가져다가, 레빗의 '흥미로운 질문 던지는' 특기를 더해 경제학 비전공자가 읽기에도 재미있는 이야기를 풀어갑니다.  상식과 통념뿐만 아니라 '이래야 한다'는 '윤리'도 뛰어넘어 '실제'에 불편한 직면을 하게 만들기도 합니다.

 

p30. 윤리학이 우리가 원하는 이상적인 세상을 대표한다면 경제학은 실제로 존재하는 현실적인 세상을 의미한다. 경제학은 측정을 목적으로 하는 모든 학문의 상위에 위치하며, 두서없이 복잡하게 얽혀 있는 정보를 신뢰성 높게 평가할 수 있는 강력하고 융통성 있는 도구로 구성되어 있어, 한 요인이 미친 영향 혹은 전체적인 결과를 결정할 수 있다. ...

p31. 이 책의 목적은 모든 것, 그렇다, 모든 것의 숨겨진 이면을 파헤치는 것이다. ... 우리는 경제학이 보유한 가장 분석적인 도구들을 사용하여 누구의 머릿속에나 떠오를 수 있는 호기심 어린 질문을 따라가는 길을 택했다. 우리가 발명한 이 새로운 학문 분야는 '괴짜경제학(Freaknomics)'이라고 한다.

 

이 책에서 레빗이 던지는 질문들을 예로 들어보면 이런 것들 이예요. 

 

  • 교사와 스모 선수의 공통점은?
  • KKK와 부동산 중개업자는 어떤 부분이 닮았을까?
  • 마약 판매상은 왜 어머니와 함께 사는 걸까?
  • 그 많던 범죄자들은 다 어디로 갔을까?
  • 완벽한 부모는 어떻게 만들어지는가?
  • 부모는 아이에게 과연 영향을 미치는가?

레빗은 이런 괴짜(?) 질문을 던지고 여기서 끝나는 것이 아니라, 데이터를 가지고 분석을 해서 가설을 검증한다는 점이 일반인과 다른 점이 되겠습니다.

 

저자는 이 책이 '책 전체를 관통하는 일관된 하나의 주제는 없다'고 말하고 있습니다만, 경제학에서 중요하게 생각하는 '보상'이라는 개념을 여기저기서 차용해서 활용하고 있습니다.

 

p36. 경제학은 근본적으로 인센티브를 연구하는 학문이다. 사람들은 어떻게 자신이 원하거나 필요로 하는 것을 얻는가? 특히 다른 이들이 같은 것을 원하고 있을 때 말이다. 경제학자들은 인센티브를 사랑한다. ... 인센티브는 총탄이며 지렛대이자 열쇠다. 즉 상황을 극적으로 바꿀 수 있는 놀라운 힘을 지닌 자그마한 어떤 것이다.

 

 

이 책 말고 인센티브, 행동경제학 관련 다른 책들에서도 본 적이 있는 사례인데요, 이스라엘 어린이집의 학부모들을 대상으로 '아이들을 늦게 데리러 오면 벌금을 부과하는 실험'을 했던 사례가 참 인상깊게 남습니다.

 

p40~41. 그러나 이 벌금 제도에는 또 다른 심각한 문제가 있었다. 도덕적 인센티브(지각한 부모들이 느껴야 하는 죄책감)을 경제적 인센티브(3달러의 벌금)로 대체한 것이 문제였다. 겨우 하루 몇 달러의 돈으로, 이제 부모들은 죄책감을 정당화할 수 있게 되었다. 나아가 적은 액수의 벌금은 부모들에게 지각이 그 정도의 가치밖에 안 된다는 생각을 품게 만들었다. 아무리 지각을 해봤자 놀이방이 손해 보는 정도가 겨우 3달러라면 굳이 테니스 시합을 서둘러 마치고 아이를 데리러 갈 필요가 없지 않은가? 실제로 실험 17주째에 경제학자들이 벌금 제도의 시행을 중단했는데도, 지각하는 부모들의 수에는 변화가 없었다. 이제 그들은 지각을 할 뿐만 아니라 벌금을 내지도 않았으며, 무엇보다도 더 이상 죄책감을 느끼지 않았다.

 이는 인센티브의 강력하면서도 교묘한 특성이다. 단 하나의 아주 작고 미묘한 변화가 거대하고 극적인, 그리고 대개는 미리 예측하지 못했던 결과를 낳는다. 토머스 제퍼슨은 보스턴 차 사건의 발단이 된 조그마한 인센티브가 결국에는 미국 독립전쟁을 이끌어낸 데 주목하여 이러한 사실을 깨달았다. "이 세상을 지배하는 인과관계는 참으로 불가사의하다. 차에 부과된 겨우 2페니의 세금이, 비록 부당하게 매겨졌다고는 하나, 이 대륙에 사는 모든 이의 삶을 완전히 바꾸어버렸으니 말이다."

 

 

위의 사례는 실험을 통한 사례였다면 아래 소개하는 사례는 방대하게 쌓인 데이터를 분석해서 '교사와 스모선수의 공통점'을 찾아낸 사례가 되겠습니다.  학생들의 성적이 교사의 처우에 연계되게끔 한 인센티브에 반응해서 교사들이 학생들의 성적을 좋게 만들기 위해 부정행위를 한 것(스모선수가 인센티브에 반응해서 시합을 모의한 것처럼...)을 데이터를 가지고 밝혀낸 사례예요.

 

p47~48. 경제학은 근본적으로 인센티브와 관련된 학문인 동시에, 아주 다행스럽게도 사람들이 어떻게 그런 인센티브에 반응하는가를 측정하는 통계적 도구를 지닌 과학이기도 하다. 당신에게 필요한 것은 '약간의' 데이터, 그뿐이다. ...

부정행위를 저지르는 교사의 학급은 어떤 모습을 하고 있을까? 먼저 찾아야 할 것은 '희귀한' 답안 패턴이다. ... 이런 식으로 시카고 공립학교들의 모든 데이터를 분석해보면 연간 200개 이상의 학급에서 부정행위가 저질러진다는 증거를 발견할 수 있다. 전체의 5%에 가까운 수치다.

실제로 부정행위 의심교사의 학급(실험군)과 건전한 교사의 학급(대조군)을 뽑아서 재시험을 치루었고, 부정행위 의심에 대해 가설을 검증/실증하여 증거가 확고한 12명의 교사는 해고, 다른 많은 교사에게는 경고 조치가 취해졌다. 그리고 다음해, 교사들에 의한 부정행위는 30% 이상 줄었다고 한다.

 

 

이 책의 저자가 그렇다고 현란한 고급 통계분석 기법, 데이터 마이닝 기법을 사용했나 하면 그렇지도 않습니다.  대부분의 경우 실험군/대조군을 설정하는 실험설계의 기본적인 개념을 적용해서 통제변수를 통제하고 원하는 변수에 대해 영향력을 평가 (유의미한 차이가 있는 것인지 검증)하는, 통계학의 가장 기본적인 분석을 주로 수행했습니다.   "완벽한 부모는 어떻게 만들어지는가?", "부모는 아이에게 과연 영향을 미치는가?" 파트에서는 '회귀분석' 기법을 이용했는데요, 이 또한 통계학을 배우는 분이라면 다변량분석 중에서도 제일 많이 배우는 기법이기도 하지요. 

 

이는 우리가 분석기법을 몰라서 분석적 사고, 분석을 통한 검증을 못하겠다고 말하는게 실은, 호기심이 없고, 제대로된 질문을 못던지고, 데이터를 찾아나서고 모으고 분석하기에는 게을러서 그런 것은 아닌지 자문해보게 하는 대목입니다.

 

다만 저자가 하버드, MIT, 시카고 대학교에 몸 담고 있었고 네트워킹이 빠방하기 때문에 일반인이 접근하기 힘든 다양한 데이터에 접근할 수 있었던 점은 일반인과는 차별화된 저자만의 강점이기는 하겠습니다.

 

 

마지막으로 "그 많던 범죄자들은 다 어디로 갔을까?"에 대한 저자의 질문에 대한 답변을 소개하는 것으로 글을 마칠까 합니다.  많은 범죄학자, 경찰관, 경제학자, 정치가 등이 아래와 같은 다양한 가설을 제시했습니다.  가령, 혁신적 치안 정책, 징역형의 증가, 크랙을 비롯한 마약시장의 변화, 인구 고령화, 강력한 총기 규제 정책, 건실한 경제, 경찰 인원의 증가, 사형 구형의 증가, 은밀한 무기 소지 허용법, 총기류 유상회수 등....

 

저자가 데이터를 가지고 분석을 해보니 이들 가설 중에서 '사형 구형', '경제 호황', '총기 규제', '마약시장 변화' 등은 범죄율 감소와 연관이 거의 없었다고 합니다. 반면 '혁신적 치안 정책', '경찰 인원의 증가'는 어느 정도 연관이 있다고 하네요.  그리고 저자가 위의 가설에는 없는 새로운 가설을 들고 나와 데이터로 검증을 하였습니다. 바로 1960대 "낙태 합법화"가 가장 결정적인 범죄 감소 유발요인이라는 것입니다.  이러한 주장은 정치계, 종교계, 학계에서 거대한 논쟁을 일으켰습니다. 

 

15~20여년 전에 '깨진 유리창의 법칙'이라는 책이 한 때 유행했었구요, 그 책의 초반에 '뉴욕 경찰청'이 '깨진 유리창의 법칙'을 차용해 노상방뇨나 무임지하철승차와 같은 '경범죄'(깨진 유리창)를 강력단속하여 살인, 방화, 강도 등의 '중범죄'를 예방하는 혁혁한 성과를 거두었다는 사례가 나옵니다.  괴짜경제학의 레빗은 이 '깨진 유리창의 법칙'에 나오는 뉴욕 경찰청 사례가 경찰 청장의 '깨진 유리창의 법칙' 차용 덕분이 아니라 실제는 20여년 전에 미국 대법원이 '낙태 합법화'를 했기 때문이라고 말하고 있습니다.  뉴욕뿐만이 아니라 다른 도시에서도 1990년대에는 범죄율이 동반 급락했다는게 그 증거입니다.  '깨진 유리창의 법칙' 저자와 뉴욕 경찰청장은 '괴짜경제학' 책과 저자 '스티븐 레빗'을 정말로 싫어할 것 같습니다. ㅋㅋ

 

 

 

728x90
반응형
Posted by Rfriend
,

데이터 변환 방법으로서

(1) 표준화

(2) 정규분포화

(3) 범주화

    - 이산형화

    - 이항변수화

(4) 개수 축소

(5) 차원 축소

   - (5-1) 주성분분석

   - (5-2) 요인분석

(6) 시그널 데이터 압축

의 6개 구분 중에서

 

등간척도(혹은 비율척도)로 측정한 두 개 이상의 다수의 변수들에 잠재되어 있는 공통인자를 찾아내는 (5-2) 요인분석(Factor Analysis)에 대해서 알아보겠습니다. 

 

요인분석은 통계학자 Spearman이 학생들의 여러개의 시험 성적(예: Classic, French, English, Math...) 간에 상관관계 행렬을 보다가 "어떻게 하면 연관성있는 변수들을 묶어주는 내재하는 속성을 찾을 수 있을까?"를 가지고 고민하다가 유래되었다고 합니다.

 

요인분석을 왜 하는지에 대해서는 이전에 포스팅한 (5-1) 주성분분석의 초반부를 참고하시기 바랍니다. (바로가기 ☞ 주성분분석)

 

 

 

 

대신에 요인분석과 주성분분석의 공통점과 차이점에 대해서 정리한 다른 분의 블로그(http://ai-times.tistory.com/112) 내용을 소개하겠습니다.

 

요인분석 과 주성분분석의 관계는?

많은 경우 (많은 사람들이) 요인분석과 주성분분석을 혼동한다.
두 용어를 같은 것으로 이해하는 사람들도 많다. ( 요인분석 = 주성분분석 ? ) 그러나 이것은 요인분석이나 주성분 분석을 잘 이해하지 못한 것이다. (참고는 요인분석은 Factor Analysis 이고, 주성분 분석은 Principle Component Analysis 이며 보통 PCA 라고 불린다.)

요인분석과 주성분분석은 물론 깊은 관계가 있다. 그러나 엄밀하게는 같은 것은 아니다.
요인분석을 수행하기 위해서 즉, 몇 개의 요인(잠재된 변수)들을 추출하기 위해서 여러 가지 방법이 사용될 수 있으나 그 중에 가장 많이 사용되는 방법이 <주성분 분석>이다. (그렇다고, 요인분석이 주성분분석의 상위 개념에 있는 것이라고 할 수는 없다. 집합으로 볼 때 포함 관계 아님)

 

* 공통점
[1] 모두 데이터를  축소한다. 
[2] 원래 데이터의 새로운 몇 개의 변수들로 만들어 낸다.


* 차이점
(아래에 정리해보았다. 요인분석은 FA 로, 주성분분석은 PCA 로 표현하였다.)

[1] 생성되는 변수의 수
FA  : 몇 개라고 지정할 수 없다. 데이터의 의미에 따라 다르다. 3개가 될 수도 있고, 또는 4개도 있고, ...
데이터에 서로 성관성을 갖는 변수들의 군집의 개수로 나뉘어질 것이다.
PCA : 주성분이라고 하며, 보통 2개를 찾는다. 제1주성분, 제2주성분 이라고 불린다.

[2] 생성되는 변수의 의미 (이름)
FA : 위에서 학생들의 성적데이터를 가지고 설명했듯이 분석가가 적절한 이름을 붙일 수 있다. 자동적으로 이름을 만들어주지는 않는다.
PCA : 보통 2개의 변수를 채택한다. 첫번째 것은 제1주성분, 제2주성분 이라고 부른다. (원래 데이터의 입력변수가 p라고 하면, ... 제p주성분까지 만들수 있다. 그러나 보통 2개 정도만 사용한다. 이걸로 보통 충분하다.)
요인분석에서는 서로 상관있는 변수들의 이름을 지을 수 있으나 제n주성분의 경우는 그게 좀 힘들다. (의미 중심으로 묶였다기 보다는 분류 결정력이 높은 임의의 변수를 만든 것이기 때문이다.)

[3] 생성된 변수들의 관계
FA : 새 (잠재)변수들은 기본적으로 대등한 관계를 갖는다. 어떤 것이 더 중요하다 라는 의미는 요인분석에서는 없다. 단, 분류/예측에 그 다음 단계로 사용된 다면 그 때 중요성의 의미가 부여될 것이다.  
PCA : 제1주성분이 가장 중요하고, 그 다음 제2주성분이 중요하게 취급된다. 그 다음은 제3주성분 ... 이런 식이다. 즉, 변수들 간의 중요성의 순위가 존재한다.

[4] 분석방법의 의미
FA : 목표 필드를 고려하지 않는다. 그냥 데이터가 주어지면 변수들을 비슷한 성격들로 묶어서 새로운 [잠재]변수들을 만들어 낸다.
PCA : 목표 변수를 고려한다. 목표 변수를 잘 예측/분류하기 위하여 원래 변수들의 선형 결합으로 이루어진 몇 개의 주성분(변수)들을 찾아낸다 

 

* 출처: http://ai-times.tistory.com/112

 

 

요인 추출 방법으로 주성분분석이 활용됩니다. 요인분석을 할 때 초기값 m을 어떻게 잡아주느냐에 따라서 계산 속도가 많이 영향을 받게 됩니다. 이때 보통은 반응변수들이 가지고 있는 변동량의 대부분들을 설명해줄 수 있는 고유값(eigenvalue)와 고객벡터(engenvector)의 수는 몇 개인가를 결정할 수 있는 주성분분석(Principal Component Analysis, PCA)를 활용해서 초기값 m을 잡게 됩니다. (지난 주성분분석 포스팅의 Scree Plot 참조)

 

 

[참고: 용어설명]

- 요인점수 (Factor Score) : 각 관측치의 요인 점수는 요인 점수 계수(Standardized Scoring Coefficients)와 실제 (표준화된) 관측치의 값의 곱으로 구하며, 요인별로 이를 summation하면 요인별 요인점수가 됨.

- 요인패턴 (Factor Loading) :  각 요인이 각 변수에 미치는 효과.  변수와 요인의 상관 행렬

- 공통 분산치 (Communality) : 요인에 의해 설명될 수 있는 변수의 분산량

- 요인회전 (Factor Rotation) : p개의 변수들을 m개의 요인(factor)로 묶어주기 편리하게 혹은 해석하기 쉽게하도록 축을 회전시키는 것. 직교회전에 varimax, transvarimax 등이 있고 비직교회전방법도 있으며, 보통 분산을 최대화하는 직교회전방법 varimax 를 많이 씀.

 

한국신용평가정보에서 나온 '국내 증권회사의 주요 재무제표' (2007.3.31 기준)를 가지고 요인분석을 R로 해보도록 하겠습니다. (지난번 포스팅에서는 똑같은 데이터에 대해 주성분분석을 해보았습니다)

 

이 데이터는 18개 증권사별로 V1.총자본순이익율, V2.자기자본순이익율, V3.자기자본비율, V4.부채비율, V5.자기자본회전율 재무지표 변수로 구성되어 있습니다.

 

예제 데이터('국내 증권회사의 주요 재무제표' (2007.3.31 기준)) 다운로드 ☞

secu_com_finance_2007.csv

 

R로 외부 csv 데이터 불러오기, 표준화 변환, 부채비율 방향 변환, 변수 선택, 상관계수분석, 산포도행렬은 아래와 같습니다. (지난 포스팅 주성분분석 설명과 동일)

 

주성분분석처럼 요인분석도 변수별 scale 영향을 없애기 위해서 표준화(standardization)한 관측값을 사용합니다.

 

> # csv 파일 불러오기 (file importing)
> secu_com_finance_2007 <- read.csv("C:/Users/user/Documents/R/secu_com_finance_2007.csv",
+                                   header = TRUE, 
+                                   stringsAsFactors = FALSE)
> # V1 : 총자본순이익율
> # V2 : 자기자본순이익율
> # V3 : 자기자본비율
> # V4 : 부채비율
> # V5 : 자기자본회전율
> 
> 
> # 표준화 변환 (standardization)
> secu_com_finance_2007 <- transform(secu_com_finance_2007, 
+                                    V1_s = scale(V1), 
+                                    V2_s = scale(V2), 
+                                    V3_s = scale(V3), 
+                                    V4_s = scale(V4),
+                                    V5_s = scale(V5))
> 
> # 부채비율(V4_s)을 방향(max(V4_s)-V4_s) 변환
> secu_com_finance_2007 <- transform(secu_com_finance_2007, 
+                                    V4_s2 = max(V4_s) - V4_s)
> 
> # variable selection
> secu_com_finance_2007_2 <- secu_com_finance_2007[,c("company", "V1_s", "V2_s", "V3_s", "V4_s2", "V5_s")]
> 
> 
> # Correlation analysis
> cor(secu_com_finance_2007_2[,-1])
            V1_s       V2_s       V3_s      V4_s2        V5_s
V1_s  1.00000000  0.6165153  0.3239780  0.3553930  0.01387883
V2_s  0.61651527  1.0000000 -0.5124351 -0.4659444  0.42263462
V3_s  0.32397800 -0.5124351  1.0000000  0.9366296 -0.56340782
V4_s2 0.35539305 -0.4659444  0.9366296  1.0000000 -0.53954570
V5_s  0.01387883  0.4226346 -0.5634078 -0.5395457  1.00000000
> 
> round(cor(secu_com_finance_2007_2[,-1]), digits=3) # 반올림
       V1_s   V2_s   V3_s  V4_s2   V5_s
V1_s  1.000  0.617  0.324  0.355  0.014
V2_s  0.617  1.000 -0.512 -0.466  0.423
V3_s  0.324 -0.512  1.000  0.937 -0.563
V4_s2 0.355 -0.466  0.937  1.000 -0.540
V5_s  0.014  0.423 -0.563 -0.540  1.000
> 
> 
> # Scatter plot matrix
> plot(secu_com_finance_2007_2[,-1])

 

 

 

factanal()함수를 활용해서 R로 요인분석을 해보도록 하겠습니다.

- secu_com_finance_2007_2 : 데이터를 지정해주고 (표준화된 숫자형 변수들)

- factors = 2 : 요인의 개수 지정

- ratation = "varimax" : 회전방법 지정

- scores = "regression" :  요인점수 계산 방법 지정

해주면 되겠습니다.

 

지난번 포스팅의 주성분분석에서는 동일한 데이터로 했을 때 주성분을 3개(Scree plot 보고서 결정)로 해서 분석 결과 해석을 했었는데요,

 

> # Scree Plot
> plot(prcomp(secu_com_finance_2007_2[,c(2:6)]), type="l",
+      sub = "Scree Plot")

 

 

 

 

 

 

요인분석에서 요인 개수를 3개로 집어넣었더닌 변수 5개밖에 안되는데 요인을 3개씩이나 한다고 경고메시지가 뜨네요. ^^;  그래서 요인 2개로 집어넣었습니다.

 

> # 요인분석(maximum likelihood factor analysis)
> # rotation = "varimax"
> secu_factanal <- factanal(secu_com_finance_2007_2[,2:6], 
+                           factors = 2, 
+                           rotation = "varimax", # "varimax", "promax", "none" 
+                           scores="regression") # "regression", "Bartlett"
> 
> print(secu_factanal)

Call:
factanal(x = secu_com_finance_2007_2[, 2:6], factors = 2, scores = "regression",     rotation = "varimax")

Uniquenesses:
 V1_s  V2_s  V3_s V4_s2  V5_s 
0.005 0.026 0.036 0.083 0.660 

Loadings:
      Factor1 Factor2
V1_s   0.252   0.965 
V2_s  -0.588   0.792 
V3_s   0.979         
V4_s2  0.950   0.120 
V5_s  -0.562   0.155 

               Factor1 Factor2
SS loadings      2.586   1.604
Proportion Var   0.517   0.321
Cumulative Var   0.517   0.838

Test of the hypothesis that 2 factors are sufficient.
The chi square statistic is 1.59 on 1 degree of freedom.
The p-value is 0.207 

 

 

 

위에 Loadings 에 보면 Factor2의 V3_s가 숫자가 비어있는데요, 아래처럼 cutoff 를 조정해주면 모두 볼 수 있습니다.

 

> print(secu_factanal$loadings, cutoff=0) # display every loadings

Loadings:
      Factor1 Factor2
V1_s   0.252   0.965 
V2_s  -0.588   0.792 
V3_s   0.979   0.080 
V4_s2  0.950   0.120 
V5_s  -0.562   0.155 

               Factor1 Factor2
SS loadings      2.586   1.604
Proportion Var   0.517   0.321
Cumulative Var   0.517   0.838 

 

요인1(Factor1)은 자기자본비율(V3_s)과 (방햔변환 후의) 부채비율(V4_s2) 이 같이 묶였으며, 요인2(Factor2)는 총자본순이익율(V1_s)과 자기자본순이익율(V2_s)이 함께 묶었습니다.  V5_s가 두 요인 중에서 어디에 속한다고 할지 좀 애매한데요, 요인1하고는 부호가 다르므로 요인2에 묶인다고 하겠습니다.

 

 

 

 

다음으로, 요인분석 Biplot을 그려보도록 하겠습니다.  주성분분석할 때는 prcomp() 함수로 분석하고 biplot()함수로 단 한번에 아주 쉽게 Biplot을 그렸었는데요, 요인분석에서는 biplot을 단번에 그릴 수 있는 함수를 못찾았습니다. (혹시 이 포스팅 보시는 분중에 요인분석 biplot 그릴 수 있는 패키지, 함수 알고 계신분은 댓글로 공유해주시면 감사하겠습니다. 미리 꾸벅~ ☞_☜)

 

> # factor scores plotting
> secu_factanal$scores
          Factor1     Factor2
 [1,] -1.01782141 -0.28535410
 [2,] -0.17230586  0.08808775
 [3,] -0.13294211 -0.71511403
 [4,] -1.03557284  2.77950626
 [5,] -0.34416962 -1.21841127
 [6,] -0.01993668  0.44223954
 [7,] -0.62177426  1.26909067
 [8,]  1.79002399  0.28314793
 [9,]  1.60353334  0.52158445
[10,] -0.55591603 -0.12331881
[11,]  0.55387868 -1.03939155
[12,] -0.93740279 -0.74332879
[13,]  0.45680247  0.06433085
[14,] -1.13490535 -0.63034122
[15,]  1.36209539 -0.98147959
[16,]  1.57141053  0.89812864
[17,] -0.56190944  0.38006982
[18,] -0.80308800 -0.98944656
> 
> plot(secu_factanal$scores, main="Biplot of the first 2 factors")
> 
 

 

 
 
> # 관측치별 이름 매핑(rownames mapping)
> text(secu_factanal$scores[,1], secu_factanal$scores[,2], 
+      labels = secu_com_finance_2007$company, 
+      cex = 0.7, pos = 3, col = "blue")
> 
 

 

 
 
> # factor loadings plotting
> points(secu_factanal$loadings, pch=19, col = "red")
>
 

 

> text(secu_factanal$loadings[,1], secu_factanal$loadings[,2], + labels = rownames(secu_factanal$loadings), + cex = 0.8, pos = 3, col = "red") >
> # plotting lines between (0,0) and (factor loadings by Var.)
> segments(0,0,secu_factanal$loadings[1,1], secu_factanal$loadings[1,2])
> segments(0,0,secu_factanal$loadings[2,1], secu_factanal$loadings[2,2])
> segments(0,0,secu_factanal$loadings[3,1], secu_factanal$loadings[3,2])
> segments(0,0,secu_factanal$loadings[4,1], secu_factanal$loadings[4,2])
> segments(0,0,secu_factanal$loadings[5,1], secu_factanal$loadings[5,2])

 

 

 

 

가로축 Factor1이 '안정성' (자기자본비율, 부채비율) 지표라고 했는데요, Factor1 축의 오른쪽에 위치한 한양증권, 브릿지증권, 부국증권, 유화증권사 등은 안정성이 높은 회사들이라고 해석할 수 있겠습니다.

 

(참고: Factor1 = 0.252*V1_s - 0.588*V2_s + 0.979*V3_s + 0.950*V4_s2 - 0.562*V5_s)

 

 

다음으로, 세로축 Factor2는 '수익성'(총자본순이익율, 자기자본순이익율, 자기자본회전율) 지표라고 했는데요, Factor2 축의 위쪽에 위치한 미래애셋증권, 한화증권, 메리츠증권, 교보증권, 삼성증권 등이 수익성이 양호한 증권사라고 해석할 수 있겠습니다.

 

(참고: Factor2 = 0.965*V1_s + 0.792*V2_s + 0.080*V3_s + 1.20*V4-s2 + 0.155*V5_s)

 

 

이처럼 요인분석을 활용하면 다수의 변수를 안정성과 수익성이라는 두 개의 축으로 차원을 축소해서 포지셔닝맵을 그려서 쉽게 전체 상황을 파악할 수 있겠습니다.

 

다음번 포스팅에서는 기계데이터, 신호데이터에서 나오는 신호를 압축 변환하는 방법에 대해서 알아보겠습니다.

 

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

 

728x90
반응형
Posted by Rfriend
,