> ## 4 기초 분석 1 : 노선별, 시간대별 이용량
>
>
> # Step 1 : 노선별, 시간대별 이용량 특성 분석
>
> ## 이용자가 몇 번 버스를 타고 어디에서 어디로 이동하였는지 알아내기
> # 개별 이동 데이터 불러오기
> load("./01_save/02_003_trip_chain.rdata")
> # 버스노선 정보 불러오기
> route_map <- read.csv("./SBJ_1910_001/routestationmapping.csv", fileEncoding = "UTF-8")
> head(route_map)
구분 운수사명 운수사ID 이비노선ID 표준노선ID 노선명
1 경기시내 경원여객M 2805000 216000044 28050900 M6410
2 경기시내 강화운수 4100100 232000028 41001001 2
3 경기시내 강화운수 4100100 232000029 41001013 88
4 경기시내 강화운수 4100100 232000061 41001020 3000
5 경기시내 강화운수 4100100 232000067 41001024 388
6 경기시내 강화운수 4100100 232000087 41001027 3
> # 노선 ID와 버스 번호(노선명) 추출
> route_map <- route_map[, 5:6]
> # 개별 이동 데이터와 버스 번호(노선명) 결합
> route_sta <- merge(trip_chain, route_map, by.x = "버스노선ID1", by.y = "표준노선ID")
> str(route_sta)
'data.frame': 234639 obs. of 66 variables:
$ 버스노선ID1 : num 41002003 41002003 41002003 41002003 41002003 ...
$ 암호화카드번호 : num 900474615375 900109571235 900456269492 100073476942 900517750322 ...
...
$ S_시군명 : chr "광주시" "성남시" "성남시" "광주시" ...
$ S_정류소명 : chr "현대모닝사이드1차아파트.새마을입구.신현리" "성남아트센터.태원고교" "금광1.2동주민센터.중원어린이도서관" "뉴광주농협주유소" ...
...
$ E_시군명 : chr "화성시" "화성시" "화성시" "화성시" ...
$ E_정류소명 : chr "반월1통.빅마켓앞" "삼성전자" "능동주공단지" "수직리" ...
$ 노선명 : chr "17" "17" "17" "17" ...
> # 개별 이동 정보 확인
> head(route_sta[, c("암호화카드번호", "노선명", "S_정류소명", "E_정류소명")])
암호화카드번호 노선명 S_정류소명 E_정류소명
1 900474615375 17 현대모닝사이드1차아파트.새마을입구.신현리 반월1통.빅마켓앞
2 900109571235 17 성남아트센터.태원고교 삼성전자
3 900456269492 17 금광1.2동주민센터.중원어린이도서관 능동주공단지
4 100073476942 17 뉴광주농협주유소 수직리
5 900517750322 17 문형삼거리 신미주아파트앞
6 900114055502 17 보건소.공설운동장 동문아파트.협성대학교.봉담중고교
>
> ## 이용자가 많은 버스노선 추출
> # 노선별 이용 건수를 피벗테이블로 작성하기
> bus_usr <- as.data.frame(table(route_sta$노선명))
> # 컬럼명 변경
> colnames(bus_usr) <- c("line", "Freq")
> # 노선별 이용 건수 sortig => 빈도수가 많은 것부터
> bus_usr <- bus_usr %>%
+ arrange(desc(Freq))
> # 비율(percentage) 보기
> bus_usr$pcnt <- round((bus_usr$Freq/sum(bus_usr$Freq))*100, 1)
> # 노선별 이용건수 상위 5개 보기
> head(bus_usr, 5)
line Freq pcnt
1 92-1 18702 8.0
2 13-5 16748 7.1
3 98 9607 4.1
4 62-1 8312 3.5
5 7-1 7604 3.2
>
> ## 노선별 누적 이용자 비율 분석
> # 노선별 누적 비율 계산
> bus_usr <- bus_usr %>%
+ mutate(cum = round((cumsum(Freq)/sum(Freq)) * 100, 1))
> # 상위 42개 노선이 전체 이동량의 87.3%를 차지하고 있음
> head(bus_usr[39:42,])
line Freq pcnt cum
39 51 1645 0.7 85.4
40 5-1 1644 0.7 86.1
41 7 1634 0.7 86.8
42 720-1 1174 0.5 87.3
> # 저장
> save(bus_usr, file = "./01_save/03_001_bus_usr.rdata")
> # 누적 비율 차트
> plot(bus_usr$cum, type = "l", xlim = c(0,100))
> abline(v = 42, col = "red", lwd = 3, lty = 2)
>
> ## 이동 발생 시간대 분석
> hist <- hist(trip_chain$start_hour, plot = FALSE)
> plot(hist, xaxt = "n", xlab = "시간", ylab = "건수",
+ main = "시간대별 trip 발생량", col = "blue")
> axis(1, hist$mids, labels = c(5:23))
>
>
> # Step 2 : 이용량 많은 버스노선 정류장 위치 알아보기
>
> ## 정류장 정보 추출
> # 버스노선 번호 추출
> bus_line <- as.character(bus_usr[1:42, 1])
> # 노선 - 정류장 매핑 테이블 불러오기
> load("./01_save/01_004_route_sta.rdata")
> # 이용량 많은 42개 버스노선의 정류장만 추출
> bus_line <- filter(route_sta, grepl(paste(as.character(bus_line), collapse = "|"), route_sta$bus_line_no))
> # 버스노선 중 대상 지역에 위치하는 정류장 정보만 추출
> patterns <- c("수원시", "화성시", "용인시", "오산시")
> bus_line <- filter(bus_line, grepl(paste(patterns, collapse = "|"), 시군명))
> # 중복되는 노선 지우기
> head(bus_line)
station_id bus_line_no bus_line_no_seq station_nm 이비카드정류장ID WGS84위도 WGS84경도 시군명
1 200000069 27-1 11 율전성당.성균관대역 4116713 37.30135 126.9725 수원시
2 200000070 27 53 체육고교.신안아파트 4116712 37.30305 126.9755 수원시
3 200000088 27-1 41 화서역.화서주공3단지 4116700 37.28670 126.9914 수원시
4 200000100 27 46 장안등기소.성우아파트 4108173 37.29632 126.9834 수원시
5 200000101 27 45 천천중학교 4108175 37.29913 126.9821 수원시
6 200000103 27 42 신안아파트 4108178 37.30272 126.9756 수원시
정류소명
1 율전성당.성균관대역
2 체육고교.신안아파트
3 화서역.화서주공3단지
4 장안등기소.성우아파트
5 천천중학교
6 신안아파트
> bus_line <- bus_line[!duplicated(bus_line[c(2,4)]), ]
> bus_line <- bus_line %>%
+ arrange(bus_line_no, bus_line_no_seq)
> head(bus_line)
station_id bus_line_no bus_line_no_seq station_nm 이비카드정류장ID WGS84위도 WGS84경도 시군명
1 228001584 10-7 1 백암터미널 4176507 37.16525 127.3744 용인시
2 233000387 10-7 2 발안만세시장 4170543 37.13330 126.9094 화성시
3 228001998 10-7 3 백암중고교 4196851 37.16427 127.3700 용인시
4 233001558 10-7 3 발안삼거리.바다마트 4130357 37.13592 126.9109 화성시
5 228001532 10-7 4 백암초.중.고등학교 4196849 37.16332 127.3680 용인시
6 228000625 10-7 5 근창리 4150573 37.16050 127.3620 용인시
정류소명
1 백암터미널
2 발안만세시장
3 백암중고교
4 발안삼거리.바다마트
5 백암초.중.고등학교
6 근창리
> # 노선별 이용량 결합
> bus_line <- merge(bus_line, bus_usr[1:42, 1:3], by.x = "bus_line_no", by.y = "line", all.x = TRUE)
> bus_line <- na.omit(bus_line)
> # 저장
> save(bus_line, file = "./01_save/03_002_bus_line.rdata")
>
> ## 정류장 정보를 공간 포인트로 만들기
> # 좌표값 추출
> library(tidyr)
> library(sp)
> coords <- bus_line %>% select(WGS84경도, WGS84위도)
> data <- bus_line[, 1:11]
> # 투영
> crs <- CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0")
> sta_pnt <- SpatialPointsDataFrame(coords = coords, data = data, proj4string = crs)
> # 저장
> save(sta_pnt, file = "./01_save/03_003_sta_pnt.rdata")
> # 시각화
> library(tmap)
> qtm("Hwaseong")
Switching to view mode. Run tmap_mode("plot") or simply ttm() to switch back to plot mode.
> qtm("Hwaseong") +
+ tm_basemap("OpenStreetMap") +
+ # 이용자 1% 이상 노선
+ qtm(subset(sta_pnt, sta_pnt@data$pcnt > 1), symbols.col = "cadetblue2", symbols.size = .05) +
+ # 이용자 2% 이상 노선
+ qtm(subset(sta_pnt, sta_pnt@data$pcnt > 2), symbols.col = "burlywood3", symbols.size = .1) +
+ # 이용자 3% 이상 노선
+ qtm(subset(sta_pnt, sta_pnt@data$pcnt > 3), symbols.col = "orange", symbols.size = .3) +
+ # 이용자 4% 이상 노선
+ qtm(subset(sta_pnt, sta_pnt@data$pcnt > 4), symbols.col = "red", symbols.size = .5)
Warning message:
qtm called without shape objects cannot be stacked
> head(subset(sta_pnt, sta_pnt@data$pcnt > 4))
bus_line_no station_id bus_line_no_seq station_nm 이비카드정류장ID WGS84위도 WGS84경도 시군명
221 13-5 233002505 6 향남주공5단지입구 4119431 37.11412 126.9054 화성시
222 13-5 233001026 21 화성중앙병원.발안초등학교 4130354 37.13118 126.9115 화성시
223 13-5 233002413 42 향남부영3단지 4120518 37.11895 126.9116 화성시
224 13-5 233002411 40 향남주공1.2단지 4120516 37.11882 126.9159 화성시
225 13-5 233002074 35 향남고등학교 4120441 37.12772 126.9268 화성시
226 13-5 233001854 36 한일베라체.행정초교 4199618 37.12602 126.9242 화성시
정류소명 Freq pcnt
221 향남주공5단지입구 16748 7.1
222 화성중앙병원.발안초등학교 16748 7.1
223 향남부영3단지 16748 7.1
224 향남주공1.2단지 16748 7.1
225 향남고등학교 16748 7.1
226 한일베라체.행정초교 16748 7.1
> max(sta_pnt@data$pcnt)
[1] 7.1
> # 화성시 외곽에서 시내로 이동하는 노선 13-5에 이용객이 집중된 것으로 판단된다. 해당 지역의 대중교통노선이나 버스 배차 간격을 확대할 필요가 있는 것으로 판단된다.
>
>
> ## 5 기초 분석 2 : 집계구별 이동 특성
>
>
> # Step 1 : 집계구별 이동 계산
>
> ## 정류장 데이터와 집계구의 공간 조인
> # 데이터 불러오기
> load("./01_save/02_003_trip_chain.rdata")
> load("./01_save/01_002_fishnet.rdata")
> load("./01_save/03_003_sta_pnt.rdata")
> load("./01_save/01_001_admin.rdata")
> # 공간 조인
> install.packages("spatialEco")
> require(spatialEco)
> sta_pnt <- point.in.poly(sta_pnt, fishnet)
> head(sta_pnt[, c(1,4,10,12)])
bus_line_no station_nm Freq id
90 116-2 동수원세무소.영통역2번출구 3872 2173
91 116-2 영통역7번출구.영덕고등학교 3872 2173
92 116-2 한화꿈에그린 3872 2636
93 116-2 공공청사부지 3872 2728
94 116-2 영통차고지(경유) 3872 2081
95 116-2 한림대병원(중) 3872 2450
> # 저장
> save(sta_pnt, file = "./01_save/04_001_sta_pnt.rdata")
>
> ## 승하차 정류장 데이터에 집계구 아이디 추가하기
> head(trip_chain[, c(3,60,65)])
암호화카드번호 S_정류소명 E_정류소명
1 900130818940 병점사거리 하북차고지
2 100519260268 병점사거리 하북차고지
3 900018471815 롯데시네마 하북차고지
4 900016616508 반도.모아아파트 하북차고지
5 100511197656 신미주아파트앞 하북4리
6 900497235853 병점입구.화남아파트.병점육교 하북4리
> tail(trip_chain[, c(3,60,65)])
암호화카드번호 S_정류소명 E_정류소명
206820 900500081540 두산신일아파트 단국대.평화의광장
206821 900494106715 반석초등학교 단국대.평화의광장
206822 900520373625 메타폴리스(중) 단국대.평화의광장
206823 900470527479 쌍용예가 단국대.평화의광장
206824 900514973139 한빛마을(중) 단국대.평화의광장
206825 900111667300 와우2리 단국대.평화의광장
> trip_chain <- merge(trip_chain, sta_pnt@data[, c(5,12)], by.x = "승차역ID1", by.y = "이비카드정류장ID")
> trip_chain <- merge(trip_chain, sta_pnt@data[, c(5,12)], by.x = "최종하차역ID", by.y = "이비카드정류장ID")
> tail(trip_chain[, c(3, 60, 65, 66, 67)])
암호화카드번호 S_정류소명 E_정류소명 id.x id.y
161561 900455093812 한빛마을(중) 단국대.평화의광장 2541 1535
161562 900514973139 한빛마을(중) 단국대.평화의광장 2541 1535
161563 900455093812 한빛마을(중) 단국대.평화의광장 2541 1535
161564 900514973139 한빛마을(중) 단국대.평화의광장 2541 1535
161565 900514973139 한빛마을(중) 단국대.평화의광장 2541 1535
161566 900514973139 한빛마을(중) 단국대.평화의광장 2541 1535
> save(trip_chain, file = "./01_save/04_002_trip_chain.rdata")
>
> ## trip_chain에서 필요한 정보만 추출
> keeps <- c("id.x", "id.y", "승차역ID1", "최종하차역ID", "총이용객수", "환승횟수")
> grid_chain <- trip_chain[keeps]
> head(grid_chain)
id.x id.y 승차역ID1 최종하차역ID 총이용객수 환승횟수
1 2630 3185 4100049 4100017 1 1
2 2630 3185 4100049 4100017 1 1
3 2630 3185 4100049 4100017 1 1
4 2541 3185 4170481 4100017 1 1
5 2630 3185 4100049 4100017 1 1
6 2541 3185 4170481 4100017 1 1
> rm("trip_chain"); rm("keeps")
> save(grid_chain, file = "./01_save/04_003_grid_chain.rdata")
>
>
> # Step 2 : 집계구별 출발, 도착 이동량 특성 분석
>
> ## 출발지 기준 총 이용객 수, 평균 환승 횟수 분석
> # 라이브러리 불러오기
> library(dplyr)
> library(sp)
> library(sf)
> # 출발지 기준 집계구 분석
> grid_in <- grid_chain %>%
+ group_by(id.x) %>%
+ summarize_if(is.numeric, sum) %>%
+ dplyr::select(id.x, 총이용객수, 환승횟수)
> # 평균 환승 횟수 계산
> grid_in$평균환승 <- round((grid_in$환승횟수 / grid_in$총이용객수), 1)
> # 컬럼 이름 정리하기
> colnames(grid_in)[1] <- c("id")
> colnames(grid_in)
[1] "id" "총이용객수" "환승횟수" "평균환승"
> # s3(spatial polygon dataframe) => s4 포맷으로 변환
> fishnet_2 <- as(fishnet, "sf")
> # grid_in 결합
> fishnet_2 <- full_join(fishnet_2, grid_in, by = "id")
> head(fishnet_2)
Simple feature collection with 6 features and 4 fields
Geometry type: POLYGON
Dimension: XY
Bounding box: xmin: 126.5062 ymin: 37.47342 xmax: 126.5662 ymax: 37.48342
CRS: +proj=longlat +datum=WGS84 +no_defs
id 총이용객수 환승횟수 평균환승 geometry
1 1 NA NA NA POLYGON ((126.5062 37.48342...
2 2 NA NA NA POLYGON ((126.5162 37.48342...
3 3 NA NA NA POLYGON ((126.5263 37.48342...
4 4 NA NA NA POLYGON ((126.5362 37.48342...
5 5 NA NA NA POLYGON ((126.5463 37.48342...
6 6 NA NA NA POLYGON ((126.5563 37.48342...
> # 저장
> save(fishnet_2, file = "./01_save/04_004_fishnet_2.rdata")
>
> ## 출발지 기준 => "총이용객수", "환승횟수", "평균환승" 플로팅
> # 총이용객수
> library(tmap)
> tm_shape(fishnet_2) +
+ tm_polygons("총이용객수", alpha = 0.6, border.col = "gray50", border.alpha = .2, colorNA = NULL) +
+ tm_shape(admin, alpha = 0.1) + tm_borders()
> # 총이용객수는 도심이 외곽 지역보다 많은 것으로 나타났다.
> # 평균환승
> tm_shape(fishnet_2) +
+ tm_polygons("평균환승", alpha = 0.6, border.col = "gray50", border.alpha = .2, colorNA = NULL) +
+ tm_shape(admin, alpha = 0.1) + tm_borders()
> # 도시 외곽 지역 주로 용인에서 출발하는 사람들은 평균 환승 횟수가 많은 것으로 나타났다.
>
> ## 도착지 기준 총 이용객 수, 평균 환승 횟수 분석
> # 도착지 기준 집계구 분석
> grid_out <- grid_chain %>%
+ group_by(id.y) %>%
+ summarize_if(is.numeric, sum) %>%
+ dplyr::select(id.y, 총이용객수, 환승횟수)
> # 평균 환승 횟수 계산
> grid_out$평균환승 <- round((grid_out$환승횟수 / grid_out$총이용객수), 1)
> # 컬럼 이름 정리하기
> colnames(grid_out)[1] <- c("id")
> colnames(grid_out)
[1] "id" "총이용객수" "환승횟수" "평균환승"
> # s3(spatial polygon dataframe) => s4 포맷으로 변환
> fishnet_2 <- as(fishnet, "sf")
> # grid_out 결합
> fishnet_2 <- full_join(fishnet_2, grid_out, by = "id")
> head(fishnet_2)
Simple feature collection with 6 features and 4 fields
Geometry type: POLYGON
Dimension: XY
Bounding box: xmin: 126.5062 ymin: 37.47342 xmax: 126.5662 ymax: 37.48342
CRS: +proj=longlat +datum=WGS84 +no_defs
id 총이용객수 환승횟수 평균환승 geometry
1 1 NA NA NA POLYGON ((126.5062 37.48342...
2 2 NA NA NA POLYGON ((126.5162 37.48342...
3 3 NA NA NA POLYGON ((126.5263 37.48342...
4 4 NA NA NA POLYGON ((126.5362 37.48342...
5 5 NA NA NA POLYGON ((126.5463 37.48342...
6 6 NA NA NA POLYGON ((126.5563 37.48342...
> # 저장
> save(fishnet_2, file = "./01_save/04_005_fishnet_2.rdata")
>
> # 총이용객수
> tm_shape(fishnet_2) +
+ tm_polygons("총이용객수", alpha = 0.6, border.col = "gray50", border.alpha = .2, colorNA = NULL) +
+ tm_shape(admin, alpha = 0.1) + tm_borders()
> # 총이용객수는 도심이 외곽 지역보다 많은 것으로 나타났다.
> # 평균환승
> tm_shape(fishnet_2) +
+ tm_polygons("평균환승", alpha = 0.6, border.col = "gray50", border.alpha = .2, colorNA = NULL) +
+ tm_shape(admin, alpha = 0.1) + tm_borders()
> # 주로 도착지가 용인인 경우 평균 환승 횟수가 많은 것으로 나타났다.
출처 : 김철민, ⌜공공데이터로 배우는 R 데이터분석 with 샤이니⌟, 이지스퍼블리싱, 2022
'데이터분석 > R' 카테고리의 다른 글
[R 데이터분석 with 샤이니] 교통 카드 데이터 분석 사례 04 - 종합 분석 (0) | 2022.08.06 |
---|---|
[R 데이터분석 with 샤이니] 교통 카드 데이터 분석 사례 03 - 교통 흐름 분석 (0) | 2022.08.04 |
[R 데이터분석 with 샤이니] 교통 카드 데이터 분석 사례 01 - 데이터 전처리 (0) | 2022.07.30 |
[R 데이터분석 with 샤이니] 커피 전문점 접근성 분석 (Shiny) (0) | 2022.07.28 |
[R 데이터분석 with 샤이니] 지진 발생 분석 (Shiny) (0) | 2022.07.26 |