본문 바로가기
데이터분석/R

[R 데이터분석 with 샤이니] 교통 카드 데이터 분석 사례 01 - 데이터 전처리

by 버섯도리 2022. 7. 30.

> ## 2 데이터 전처리 1 : 지역 정보

> # Step 1 : 집계구 만들기

> library(sp)
> install.packages("geojsonio")
> library(geojsonio)
> setwd(dirname(rstudioapi::getSourceEditorContext()$path))
> dir.create("./01_save")
> # 행정동 geojson 불러오기
> admin <- geojsonio::geojson_read("./SBJ_1910_001/tl_scco_emd.geojson", what = "sp")
> save(admin, file = "./01_save/01_001_admin.rdata")
> # 플로팅
plot(admin)


> ## 집계구 외곽 경계 만들기
> library(raster)
> library(leaflet)
> # 외곽 경계 만들기 : x_min, x_max, y_min, y_max
> fishnet <- as(raster::extent(126.50625, 127.42245, 36.99653, 37.483419), "SpatialPolygons")
> # WGS84 좌표계 투영
> proj4string(fishnet) <- "+proj=longlat +datum=WGS84 +no_defs _ellps=WGS84 +towgs84=0,0,0"
> # 플로팅
> plot(fishnet, border = "red")
> plot(admin, add=T)


> ## fishnet(집계구) 만들기
> # 래스터 변환
> fishnet <- raster(fishnet)
> # 0.1도 단위로 분할
res(fishnet) <- .01
[1] TRUE TRUE
> # 좌표계 투영
> crs(fishnet) <- CRS("+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0")
> # Polygon으로 변환
> fishnet <- rasterToPolygons(fishnet)
> # 지도 시각화
> leaflet() %>% addTiles() %>%
+   addPolygons(data = fishnet, weight = 0.4, fillOpacity = 0) %>%
+   addPolygons(data = admin, color = "red")


> ## 셀별 일련번호 부여하기
> # 현재 번호 없음
> fishnet$id
NULL
> # 일련번호 부여
> fishnet@data <- data.frame(id = 1:nrow(fishnet))
> # 저장
> save(fishnet, file = "./01_save/01_002_fishnet.rdata")
> # 일련번호 확인
> head(fishnet$id, 10)
 [1]  1  2  3  4  5  6  7  8  9 10

>

> # Step 2 : 정류장-버스노선 매핑 테이블 생성

> ## 정류장 정보 불러오기
> # CSV 불러오기
> sta_table <- read.csv("./SBJ_1910_001/stations_table.csv", fileEncoding = "UTF-8")
> # 필요한 컬럼만 추출
> colnames(sta_table)
 [1] "표준정류장ID"     "시군명"           "정류소명"         "정류소영문명"     "정류소번호"       "중앙차로여부"    
 [7] "관할관청"         "위치"             "WGS84위도"        "WGS84경도"        "모바일정류장ID"   "이비카드정류장ID"
> keeps <- c("표준정류장ID", "이비카드정류장ID", "WGS84위도", "WGS84경도", "시군명", "정류소명")
> sta_table <- sta_table[keeps]
> # NA 제거
> sta_table <- na.omit(sta_table)
> # 저장
> save(sta_table, file = "./01_save/01_003_sta_table.rdata")
> # 불필요한 변수 지우기
> rm("keeps")
> head(sta_table, 2)
    표준정류장ID 이비카드정류장ID WGS84위도 WGS84경도 시군명              정류소명
106    208000192          4103451  37.40163  126.9109 안양시              삼성빌라
158    232000001          4115377  37.59633  126.7213 김포시 유현마을.신동아아파트

> ## 버스노선(route)별 정류장 정보 불러오기
> # CSV 불러오기
> route_sta <- read.csv("./SBJ_1910_001/routestationinfo.csv", fileEncoding = "UTF-8")
> # 필요한 컬럼만 추출
> colnames(route_sta)
[1] "seq"             "pr_station_id"   "bus_line_no"     "bus_line_no_seq" "station_nm"      "station_id"     
[7] "mobile_no"      
> keeps <- c("bus_line_no", "bus_line_no_seq", "station_id", "station_nm")
> route_sta <- route_sta[keeps]
> head(route_sta, 2)
  bus_line_no bus_line_no_seq station_id       station_nm
1        10-4               1  228001552       용인터미널
2        10-4               2  277102443 용인터미널(경유)

> ## 매핑 테이블 생성 : 정류장-버스노선
> # 정류장 id(station_id) + 좌표값(위도, 경도) 결합
> route_sta <- merge(route_sta, sta_table, by.x = "station_id", by.y = "표준정류장ID")
> # 결측치 확인
> sum(is.na(route_sta))
[1] 0
> # 저장
> save(route_sta, file = "./01_save/01_004_route_sta.rdata")
> rm("keeps")
> head(route_sta, 2)
  station_id bus_line_no bus_line_no_seq                    station_nm 이비카드정류장ID WGS84위도 WGS84경도 시군명
1  124000437           5              43 미사강변18단지.강일리버10단지          4103229     37.56  127.1818 하남시
2  124000437           3              53 미사강변18단지.강일리버10단지          4103229     37.56  127.1818 하남시
                       정류소명
1 미사강변18단지.강일리버10단지
2 미사강변18단지.강일리버10단지


> ## 3 데이터 전처리 2 : 교통 카드 데이터

> # Step 1 : 개별 이동 데이터 생성

> ## 변수명 한글로 변경
> setwd(dirname(rstudioapi::getSourceEditorContext()$path))
> # 개별 이동 데이터(trip_chain) 불러오기
> trip_chain <- read.csv("./SBJ_1910_001/TripChain.csv", fileEncoding = "UTF-8")
> # 변수명 한글로 변경
> colnames(trip_chain) <- c("암호화카드번호", "트랜잭션ID", "환승횟수", "교통카드발행사ID",
+                           "총이용객수", "사용자구분", "교통수단CD1", "교통수단CD2",
+                           "교통수단CD3", "교통수단CD4", "교통수단CD5", "버스노선ID1",
+                           "버스노선ID2", "버스노선ID3", "버스노선ID4", "버스노선ID5",
+                           "차량ID1", "차량ID2", "차량ID3",    "차량ID4",
+                           "차량ID5", "총통행거리", "총탑승시간", "총소요시간",
+                           "승차일시1", "승차일시2", "승차일시3",  "승차일시4",
+                           "승차일시5", "하차일시1", "하차일시2",  "하차일시3",
+                           "하차일시4", "하차일시5", "최초승차일시", "최종하차일시",
+                           "승차역ID1", "승차역ID2", "승차역ID3",  "승차역ID4",
+                           "승차역ID5", "하차역ID1", "하차역ID2",  "하차역ID3",
+                           "하차역ID4", "하차역ID5", "최초승차역ID", "최종하차역ID",
+                           "총이용금액", "수집건수", "트립체인완료코드")
> head(trip_chain)
  암호화카드번호 트랜잭션ID 환승횟수 교통카드발행사ID 총이용객수 사용자구분 교통수단CD1 교통수단CD2 교통수단CD3
1   900079696430         56        2          9000923          1          1         500         500          NA
2   900079697651          5        1          9000923          1          1         500          NA          NA
3   900079698254         32        1          9000923          1          1         500          NA          NA
4   900079699257         80        1          9000923          1          1         500          NA          NA
5   900079701419         64        1          9000923          1          1         530          NA          NA
6   900079701419         65        1          9000923          1          1         530          NA          NA
  교통수단CD4 교통수단CD5 버스노선ID1 버스노선ID2 버스노선ID3 버스노선ID4 버스노선ID5   차량ID1   차량ID2 차량ID3
1          NA          NA    41002045    41002044          NA          NA          NA 141771735 141771587      NA
2          NA          NA    41031040          NA          NA          NA          NA 141701792        NA      NA
3          NA          NA    41031121          NA          NA          NA          NA 141701843        NA      NA
4          NA          NA    41031013          NA          NA          NA          NA 141701450        NA      NA
5          NA          NA    41020001          NA          NA          NA          NA 141703985        NA      NA
6          NA          NA    41020001          NA          NA          NA          NA 141703953        NA      NA
  차량ID4 차량ID5 총통행거리 총탑승시간 총소요시간   승차일시1   승차일시2 승차일시3 승차일시4 승차일시5   하차일시1
1      NA      NA      11170         25         25 2.01807e+13 2.01807e+13        NA        NA        NA 2.01807e+13
2      NA      NA       1700          3          3 2.01807e+13          NA        NA        NA        NA 2.01807e+13
3      NA      NA      23180         66         66 2.01807e+13          NA        NA        NA        NA 2.01807e+13
4      NA      NA        500          1          1 2.01807e+13          NA        NA        NA        NA 2.01807e+13
5      NA      NA       3240          8          8 2.01807e+13          NA        NA        NA        NA 2.01807e+13
6      NA      NA       2940          8          8 2.01807e+13          NA        NA        NA        NA 2.01807e+13
    하차일시2 하차일시3 하차일시4 하차일시5 최초승차일시    최종하차일시 승차역ID1 승차역ID2 승차역ID3 승차역ID4
1 2.01807e+13        NA        NA        NA  2.01807e+13 20180701064826    4116828   4150144        NA        NA
2          NA        NA        NA        NA  2.01807e+13 20180701072520    4117280        NA        NA        NA
3          NA        NA        NA        NA  2.01807e+13 20180701134223    4199619        NA        NA        NA
4          NA        NA        NA        NA  2.01807e+13 20180701224543    4108130        NA        NA        NA
5          NA        NA        NA        NA  2.01807e+13 20180701085910    4100122        NA        NA        NA
6          NA        NA        NA        NA  2.01807e+13 20180701221723    4100098        NA        NA        NA
  승차역ID5 하차역ID1 하차역ID2 하차역ID3 하차역ID4 하차역ID5 최초승차역ID 최종하차역ID 총이용금액 수집건수
1        NA        NA        NA        NA        NA        NA           NA      4116708       1350        2
2        NA        NA        NA        NA        NA        NA           NA      4117269       1250        1
3        NA        NA        NA        NA        NA        NA           NA      4107936       1550        1
4        NA        NA        NA        NA        NA        NA           NA      4116717       1250        1
5        NA        NA        NA        NA        NA        NA           NA      4116848       2050        1
6        NA        NA        NA        NA        NA        NA           NA      4100121       2050        1
  트립체인완료코드
1                ;
2                ;
3                ;
4                ;
5                ;
6                ;

> ## 이동 시작 일시
> # 옵션 변경(지수 => 숫자)
> options("scipen" = 100)
> head(trip_chain$최초승차일시)
[1] 20180701052543 20180701072156 20180701123653 20180701224424 20180701085058 20180701220922
> # 이동 시작 날짜(start_day) => 1일 ~ 4일 중 하나
> trip_chain$start_day <- as.numeric(substr(trip_chain[, c("최초승차일시")], 7, 8))
> head(trip_chain[, c("최초승차일시", "start_day")])
    최초승차일시 start_day
1 20180701052543         1
2 20180701072156         1
3 20180701123653         1
4 20180701224424         1
5 20180701085058         1
6 20180701220922         1
> # 이동 시작 시간(start_hour) => 새벽 4시 ~ 밤 23시 중 하나
> trip_chain$start_hour <- as.numeric(substr(trip_chain[, c("최초승차일시")], 9, 10))
> head(trip_chain[, c("최초승차일시", "start_hour")])
    최초승차일시 start_hour
1 20180701052543          5
2 20180701072156          7
3 20180701123653         12
4 20180701224424         22
5 20180701085058          8
6 20180701220922         22

> ## 이동 종료 일시
> head(trip_chain$최종하차일시)
[1] "20180701064826 " "20180701072520 " "20180701134223 " "20180701224543 " "20180701085910 " "20180701221723 "
> # 이동 시작 날짜(end_day) => 1일 ~ 4일 중 하나
> trip_chain$end_day <- as.numeric(substr(trip_chain[, c("최종하차일시")], 7, 8))
> head(trip_chain[, c("최종하차일시", "end_day")])
     최종하차일시 end_day
1 20180701064826        1
2 20180701072520        1
3 20180701134223        1
4 20180701224543        1
5 20180701085910        1
6 20180701221723        1
> # 이동 시작 시간(end_hour) => 새벽 4시 ~ 밤 23시 중 하나
> trip_chain$end_hour <- as.numeric(substr(trip_chain[, c("최종하차일시")], 9, 10))
> head(trip_chain[, c("최종하차일시", "end_hour")])
     최종하차일시 end_hour
1 20180701064826         6
2 20180701072520         7
3 20180701134223        13
4 20180701224543        22
5 20180701085910         8
6 20180701221723        22

> head(trip_chain[, c("최초승차일시", "start_day", "start_hour", "최종하차일시", "end_day", "end_hour")])
    최초승차일시 start_day start_hour    최종하차일시 end_day end_hour
1 20180701052543         1          5 20180701064826        1        6
2 20180701072156         1          7 20180701072520        1        7
3 20180701123653         1         12 20180701134223        1       13
4 20180701224424         1         22 20180701224543        1       22
5 20180701085058         1          8 20180701085910        1        8
6 20180701220922         1         22 20180701221723        1       22
> # 저장
> save(trip_chain, file = "./01_save/02_001_trip_chain_full.rdata")


> # Step 2 : 개별 이동 + 정류장 정보 매핑

> ## 출발-도착 정류장 정보에 좌표값 결합
> library(dplyr)
> load("./01_save/01_003_sta_table.rdata")
> # 버스 정류장 정보(sta_table) 컬럼 이름 변경
> colnames(sta_table) <- c("표준정류장ID", "이비카드정류장ID", "S_WGS84위도", "S_WGS84경도", "S_시군명", "S_정류소명")
> # 출발점 기준 => 이비카드정류당ID + 승차역ID1 결합
> trip_chain <- merge(trip_chain, sta_table, by.x = "승차역ID1", by.y = "이비카드정류장ID")
> # 결측치 확인
> sum(is.na(trip_chain$S_정류소명))
[1] 0
> # 버스 정류장 정보(sta_table) 컬럼 이름 변경
> colnames(sta_table) <- c("표준정류장ID", "이비카드정류장ID", "E_WGS84위도", "E_WGS84경도", "E_시군명", "E_정류소명")
> # 도착점 기준 => 이비카드정류당ID + 최종하차역ID 결합
> trip_chain <- merge(trip_chain, sta_table, by.x = "최종하차역ID", by.y = "이비카드정류장ID")
> # 결측치 확인
> sum(is.na(trip_chain$E_정류소명))
[1] 0
> # 저장
> save(trip_chain, file = "./01_save/02_002_trip_chain_full.rdata")

> ## 승하차가 많은 지역은 어디인가?
> # 피벗 테이블 만들기
> trip_chain_pivot <- as.matrix(table(trip_chain$S_시군명, trip_chain$E_시군명))
> trip_chain_pivot
          
           가평시 고양시 과천시 광명시 광주시 구리시 군포시 김포시 남양주시 동두천시 부천시 성남시 수원시 시흥시 안산시
  가평시        0      0      0      0      0      0      0      0        0        0      0      0      6      0      0
  고양시        0      0      0      0      0      0      0      0        0        0      0      4     72      0      0
  과천시        0      0      0      0      0      0      0      0        0        0      0      5     78      0      0
  광명시        0      0      0      3      0      0      9      0        0        0      0      1    228      0      0
  광주시        0      0      0      0     13      0      0      0        0        0      0    517    167      0      0
...
> # 어느 지역에서 가장 많이 승차하였는가?
> sort(colSums(trip_chain_pivot), decreasing = T)
  수원시   화성시   용인시   오산시   성남시   안양시   의왕시   안산시   평택시   군포시   광주시   광명시   시흥시 
  413067   146978    53494    53252    40199    13821     4764     4232     2246     1757      859      502      363 
  부천시   안성시   고양시 남양주시   과천시   구리시   하남시   김포시 의정부시   파주시   이천시   양주시   여주시 
     327      197      167      100       89       85       85       72       69       45       43       22       16 
  가평시   양평군   포천시 동두천시   연천군 
       8        4        4        3        3 
> # 어느 지역에서 가장 많이 하차하였는가?
> sort(rowSums(trip_chain_pivot), decreasing = T)
  수원시   화성시   용인시   오산시   성남시   안양시   의왕시   안산시   평택시   군포시   광주시   광명시   시흥시 
  417521   142403    54215    52650    39624    13798     4913     4259     2560     1676      894      531      354 
  부천시   안성시   고양시 남양주시   구리시   과천시 의정부시   하남시   김포시   파주시   이천시   양주시   여주시 
     348      198      153      142      132      122       99       96       67       36       30       27       10 
  가평시   포천시   양평군   연천군 동두천시 
       6        4        2        2        1 

>

> # Step 3 : 분석의 공간적 범위 결정 - 승하차가 가장 많은 지역

> ## 데이터 필터링
> # 출발 또는 도착지가 화성시인 경우만 필터링
> trip_chain <- trip_chain %>%
+   filter(S_시군명 == "화성시" | E_시군명 == "화성시")
> # 저장
> save(trip_chain, file = "./01_save/02_003_trip_chain.rdata")

> ## OD 행렬 생성
> trip_chain_pivot <- as.matrix(table(trip_chain$S_시군명, trip_chain$E_시군명))
> OD <- as.data.frame(trip_chain_pivot)
> # 통행 발생 순서대로 정렬
> OD <- OD %>%
+   arrange(desc(Freq))
> # 일련번호 재정렬
> rownames(OD) <- 1:nrow(OD)
> head(OD)
    Var1   Var2  Freq
1 화성시 화성시 82556
2 수원시 화성시 45200
3 화성시 수원시 40940
4 오산시 화성시 10762
5 화성시 오산시 10577
6 화성시 용인시  4250

> ## 지역별 OD 발생량 누적 비율 보기
> # 누적 비율 보기
> OD <- OD %>%
+   mutate(cum = round((cumsum(Freq)/sum(Freq)) * 100, 1))
> # 컬럼명 변경
> colnames(OD) <- c("From", "To", "Freq", "Cum")
> head(OD)
    From     To  Freq  Cum
1 화성시 화성시 82556 39.9
2 수원시 화성시 45200 61.8
3 화성시 수원시 40940 81.6
4 오산시 화성시 10762 86.8
5 화성시 오산시 10577 91.9
6 화성시 용인시  4250 93.9
> # OD 매트릭스 저장
> save(OD, file = "./01_save/02_003_OD.rdata")

> ## 통행량 집중도 확인 : 지니계수와 로렌츠 곡선
> install.packages("ineq")
> library(ineq)
> # 지니계수
ineq(OD$Freq, type = "Gini")
[1] 0.9941338
> # 로렌츠 곡선 그리기
plot(OD$Freq, col = "red", type = "l", lwd = 2)


> ## 대상 지역 교통량 데이터만 추출
> # 출발 또는 도착 지역 지정
> patterns <- c("수원시", "화성시", "용인시", "오산시")
> # 필요 변수 지정
> colnames(sta_table) <- c("표준정류장ID", "이비카드정류장ID", "WGS84위도", "WGS84경도", "시군명", "정류소명")
> # 필요한 지역만 필터링
> sta_table <- filter(sta_table, grepl(paste(patterns, collapse = "|"), 시군명))
> # 일련번호 다시 부여
> rownames(sta_table) <- 1:nrow(sta_table)
> # 필터링 결과 확인
> unique(sta_table$시군명)
[1] "화성시" "용인시" "수원시" "오산시"
> # 저장
> save(sta_table, file = "./01_save/02_003_sta_table.rdata")

 

 

 

 

 

 

출처 : 김철민, ⌜공공데이터로 배우는 R 데이터분석 with 샤이니⌟, 이지스퍼블리싱, 2022