예전 포스팅 중에서 일변량 연속형 변수에 대해 ggplot2로 막대 그래프 그리는 법을 소개했었는데요, 막대 그래프의 훌륭한 대안으로서 점 그래프(Dot Plot)이 있습니다.
Cleveland and McGill (1984) 이 “Graphical Methods for Data Presentation: Full Scale Breaks, Dot Charts, and Multibased Logging.” 이라는 논문에서 막대 그래프 대비 점 그래프가 데이터 해석, 가독성에서 가지는 우수성을 소개하면서 Cleveland Dot Plot 이라고도 많이 불리는 그래프입니다.
분석에 활용할 데이터는 MASS 패키지 내 Cars93 데이터 프레임에서, 차종(Type), 모델(Model), Max.Price, Min.Price의 4개 변수를 사용하겠으며, 관측치 개수가 많아서 화면 하나에 전부 뿌리기에는 너무 많으므로 차종(Type)의 Level 중에서 "Large", "Midsize", "Small" 만 선별하고 "Compact", "Sproty", "Van"은 제외하도록 하겠습니다.
> 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 ...
> table(Cars93$Type)
Compact Large Midsize Small Sporty Van
16 11 22 21 14 9
>
> # Model, Type, Max.Price, Min.Price 변수만 선택
> # Type 중에서 Large, Midsize, Small만 선택 (Compact, Sortry, Van은 제외)
>
> Cars93_P <- subset(Cars93,
+ select = c(Model, Type, Min.Price, Max.Price),
+ subset = (Type %in% c("Large", "Midsize", "Small")))
> str(Cars93_P)
'data.frame': 54 obs. of 4 variables:
$ Model : Factor w/ 93 levels "100","190E","240",..: 49 56 1 6 24 54 74 73 35 79 ...
$ Type : Factor w/ 6 levels "Compact","Large",..: 4 3 3 3 3 2 2 3 2 3 ...
$ Min.Price: num 12.9 29.2 30.8 23.7 14.2 19.9 22.6 26.3 33 37.5 ...
$ Max.Price: num 18.8 38.7 44.6 36.2 17.3 21.7 24.9 26.3 36.3 42.7 ...
> head(Cars93_P)
Model Type Min.Price Max.Price
1 Integra Small 12.9 18.8
2 Legend Midsize 29.2 38.7
4 100 Midsize 30.8 44.6
5 535i Midsize 23.7 36.2
6 Century Midsize 14.2 17.3
7 LeSabre Large 19.9 21.7 |
geom_point() 함수를 사용하여 클리브랜드 점 그래프(Cleveland dot plot)을 그려보겠습니다.
aes(y = reorder(Model, Max.Price)) 를 사용해서 y축에 사용할 Model 을 Max.Price 를 기준으로 정렬을 하였기 때문에 아래처럼 Max.Price가 높은 것부터 낮은 것으로 정렬이 된 채로 점 그래프가 제시되었습니다.
aes(shape = Type) 을 적용하여서 Type(Large, Midsize, Small) 별로 모양(shape)을 달리해서 제시하였습니다.
> # Cleveland dot plot of Max Price of Models with different shape by Type
> library(ggplot2)
>
> ggplot(Cars93_P, aes(x = Max.Price, y = reorder(Model, Max.Price), shape = Type)) +
+ geom_point(size = 3, colour = "blue") +
+ theme_bw() + # background 색 없애기
+ theme(panel.grid.major.x = element_blank(), # x축 선 없애기
+ panel.grid.minor.x = element_blank(),
+ panel.grid.major.y = element_line(colour="grey90", linetype="dashed")) +
+ ggtitle("Cleveland dot plot of Max.Price of Models with different shape by Type")

|
다음으로, Type(Large, Midsize, Small) 별로 facet_grid(Type ~ ., scales="free_y", space="free_y") 을 적용하여 면을 분할을 한 클리브랜드 점 그래프(Cleveland dot plot)을 그려보겠습니다.
면 분할해서 그리려면 위의 예처럼 ggplot2 내 aes(reorder)로는 안되구요, 먼저 Type과 Max.Price 순서대로 데이터셋을 따로 정렬해서 요인(factor)으로 levels 를 지정해서 변환해주어야 합니다. 그래프는 상대적으로 쉬운데, 데이터셋 정렬/요인변환이 어려울 수 있겠습니다.
> # Type, Max.Price 순서대로 정렬
> Model_Order <- Cars93_P$Model[order(Cars93_P$Type, # Large, Midsize, Small 순서
+ -Cars93_P$Max.Price, # 높은것에서 낮은 순서
+ decreasing=TRUE)]
>
> # Model_Order를 요인(factor)으로 변환
> Cars93_P$Model <- factor(Cars93_P$Model, levels=Model_Order)
>
> # Type별로 면 분할, Max.Price 순서대로 정렬된 Cleveland dot plot
> ggplot(Cars93_P, aes(x = Max.Price, y = Model)) +
+ geom_point(size = 3, aes(colour = Type)) +
+ theme_bw() +
+ theme(panel.grid.major.y = element_blank(),
+ panel.grid.minor.y = element_blank()) +
+ facet_grid(Type ~ ., scales="free_y", space="free_y") +
+ ggtitle("Cleveland dot plot of Max.Price of Models with Facets of Type")

|
다음으로, 차종(Type)별로 면 분할은 유지하면서 위의 Max.Price 에 더해서 Min.Price 를 추가하고 모양(shape)을 다르게 제시해보겠습니다.
이것도 데이터셋을 따로 미리 손을 봐줘야 합니다. reshape 패키지의 melt() 함수를 사용해서 Max.Price, Min.Price 두 값을 Price_cd (Max.Price, Min.Price)와 Price (value) 의 두개 변수로 녹여서 데이터 구조를 ggplot2의 geom_point()에 사용할 수 있도록 변경하여야 합니다. (reshape 패키지의 melt(), cast() 함수는 여기서 자세히 설명하기가 힘든데요, 따로 알아보시면 좋겠습니다)
> #--------
> # Min.Price 추가
> # melt
> library(reshape)
> Cars93_P_melt <- melt(Cars93_P, idvars = c("Type", "Model"))
Using Model, Type as id variables
> head(Cars93_P_melt)
Model Type variable value
1 Integra Small Min.Price 12.9
2 Legend Midsize Min.Price 29.2
3 100 Midsize Min.Price 30.8
4 535i Midsize Min.Price 23.7
5 Century Midsize Min.Price 14.2
6 LeSabre Large Min.Price 19.9
>
> # 변수명 변경
> Cars93_P_melt <- rename(Cars93_P_melt, c(variable = "Price_cd", value = "Price"))
> head(Cars93_P_melt)
Model Type Price_cd Price
1 Integra Small Min.Price 12.9
2 Legend Midsize Min.Price 29.2
3 100 Midsize Min.Price 30.8
4 535i Midsize Min.Price 23.7
5 Century Midsize Min.Price 14.2
6 LeSabre Large Min.Price 19.9
>
> # Type별로 면 분할, Max.Price 순서대로 정렬, Min.Price추가된 Cleveland dot plot
> ggplot(Cars93_P_melt, aes(x = Price, y = Model)) +
+ geom_segment(aes(yend=Model, xend=0)) + # 점까지만 선 그리기
+ geom_point(size=3, aes(shape = Price_cd)) + # Price_cd로 모양 구분
+ theme_bw() + # backgroud 색 없애기
+ theme(panel.grid.major.y = element_blank(), # y축 없애기
+ panel.grid.minor.y = element_blank()) + # y축 없애기
+ facet_grid(Type ~ ., scales="free_y", space="free_y") + # Type별로 면 분할
+ ggtitle("Cleveland dot plot of Max, Min Price of Models with Facets of Type") 
|
위의 세번째 그래프처럼 Max.Price와 Min.Price를 같은 그래프에 그리는데, 만약 이것을 막대 그래프로 그린다고 상상해 보세요. 막대그래프로 그린다면 지저분하고 해석, 가독성이 클리브랜드 점 그래프 대비 떨어질겁니다.
[Reference]
Cleveland, William S. 1984. “Graphical Methods for Data Presentation: Full Scale
Breaks, Dot Charts, and Multibased Logging.” The American Statistician, 38:270-280.
Dot Plots: A Useful Alternative to Bar Charts, Naomi B. Robbins, Ph.D. March 7, 2006
이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^

댓글을 달아 주세요
비밀댓글입니다
네, 티스토리 블로그 초대장 보내드리겠습니다. 멋진 블로그 일궈나가시길 바래요.