예전 포스팅 중에서 일변량 연속형 변수에 대해 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를 같은 그래프에 그리는데, 만약 이것을 막대 그래프로 그린다고 상상해 보세요. 막대그래프로 그린다면 지저분하고 해석, 가독성이 클리브랜드 점 그래프 대비 떨어질겁니다.
Python 의 Plotly 모듈을 사용해서 클리브랜드 점 그래프 (Cleveland Dot Plot in Python using Plotly) 그리는 방법은 https://rfriend.tistory.com/802 를 참고하세요.
[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
이번 포스팅이 도움이 되었다면 아래의 '공감 ~♡' 단추를 꾸욱 눌러주세요.^^
'R 분석과 프로그래밍 > R 그래프_시각화' 카테고리의 다른 글
R ggplot2 막대그래프(Bar Chart) 양수, 음수 색상 다르게 하기 (geom_bar()) (0) | 2015.08.29 |
---|---|
R ggplot2 누적 영역 그래프 (Stacked Area Plot) (geom_area()) (0) | 2015.08.23 |
R ggplot2 이차원 밀도 그래프(2D Density Plot) (stat_density2d()) (0) | 2015.08.23 |
R ggpolt2 선그래프, 시계열그래프 (Line Graph) (geom_line()) (7) | 2015.08.22 |
R ggplot2 산점도(Scatter Plot) (geom_point()) (2) | 2015.08.22 |