Posts 제주버스 승하차 예측 회귀분석 프로젝트
Post
Cancel

제주버스 승하차 예측 회귀분석 프로젝트

jeju bus dataset Liner Regression project

00. 목차


1. project 개요

    1. 배경
    1. 목적
    1. Data Preprocessing
    1. 데이터 셋 개요

02. Data EDA

    1. 실수형 Columns의 histogram
    1. 위치 정보
    1. 종속 변수
    1. 날짜 데이터
    1. 시내, 시외 데이터
    1. 정류장의 버스 노선 데이터
    1. 승하차 인원 분석
    1. 배차간격
    1. 온도
    1. 강수량
    1. 상관관계

03. Liner Regression Model 생성

    1. 시행착오
    1. 3563개의 모델을 생성
    1. 과최적화 확인
    1. 다중공선성 확인
    1. 모델 성능 확인

04. 프로젝트 회고


01. project 개요


1. 배경

  • 제주도내 주민등록인구 2019년 11월 기준 69만명이며,외국인과 관광객까지 고려하면 전체 상주인구는 90만명을 넘을 것으로 추정된다.
  • 제주도민과 외국인의 증가로 교통체증이 심각한 문제로 대두되고 있으며, 2017년 한국은행 제주본부에 따르면 제주도 일부 지역은 교통체증이 서울보다 심각하다고 발표하였다.

2. 목적

  • 제주도내의 효율적인 자원분배를 위해 특정 정류장별, 노선별 18시 ~ 20시(퇴근시간)의 버스 승차 인원을 예측

3. Data Preprocessing

  • 2019년 9월의 제주도 버스 승하차 기록 (오전 6시 ~ 11시)
    • 출처 : 데이콘 (https://dacon.io/competitions/official/229255/overview/)
  • 2019년 9월 제주도 날씨(기온, 강수량)
    • 출처 : 기상자료개방포털 (https://data.kma.go.kr/)
  • 정류장의 주소
    • 출처 : 위도, 경도를 통해 지번 주소와 위치 주소를 생성
  • 노선의 배차간격
    • 출처 : 탑승시간 정보를 기반으로 버스노선의 배차간격을 생성
  • 맞은편 정류장의 승하차 정보
    • 출처 : 정류장 코드, 이름, 위치 정보를 기반으로 맞은편 정류장의 승하차 정보를 생성

4. 데이터 셋 개요

  • 415423 개의 데이터
  • 종속변수
    • ride18 : 18:00:00부터 19:59:59까지 승차한 인원 수
  • 노선/정류장 정보 독립변수
    • id : 해당 데이터에서의 고유한 id
    • data : 날짜
    • bus_route_id : 노선 ID
    • in_out : 시내버스, 시외버스 구분
    • station_code : 해당 승하차 정류소의 id
    • station_name : 해당 승하차 정류소의 이름
    • in_ : 시내 버스 구분
    • out : 시외 버스 구분
    • bus_interval : 노선 배차 간격
    • bus_route_id_sum : 정류소, 일별 운행한 버스노선의 갯수
    • bus_route_id_all_sum : 정류소별 9월 전체 운행한 버스노선의 갯수
  • 정류장 위치 정보 독립변수
    • latitude : 해당 버스정류장의 위도
    • longitude : 해당 버스정류장의 경도
    • 지번주소 : EDA 및 제주 날씨 merge용 column
    • si :EDA 및 제주 날씨 merge용 column
    • city : EDA 및 제주 날씨 merge용 column
    • dong : EDA 및 제주 날씨 merge용 column
    • road_addr : EDA 및 제주 날씨 merge용 column
    • weather_addr : EDA 및 제주 날씨 merge용 column
  • 날씨 정보 독립변수
    • temperature : 온도
    • precipitation : 강수량
  • 날짜 정보 독립변수
    • date_day : 일 (1일 ~ 30일)
    • date_dayofweek : 요일 (0 ~ 6)
    • date_dayofname : 요일 (월 ~ 일)
    • weekday : 평일 여부
    • weekend : 주말 여부
    • holiday : 공휴일 여부
    • typhoon : 태풍이 온날
  • 승하차 정보 독립변수
    • X-Y_ride : X:00:00 ~ x:59:59까지 승차한 인원 수
    • X-Y_takeoff : X:00:00 ~ x:59:59까지 하차한 인원 수
    • ridexx : ride(탑승)의 2시간 간격 (ex 6시부터 7시 까지), 예측하는 y값이 18시 ~ 20시 까지이므로 생성함
    • offxx : off(하차)의 2시간 간격 (ex 6시부터 7시 까지), 예측하는 y값이 18시 ~ 20시 까지이므로 생성함
    • ride_sum : 승차인원의 합계
    • off_sum : 하차인원의 합계
    • ac_rideX : 맞은편 정류장의 탑승 인원(6시 ~ 11시)
    • ac_offX : 맞은편 정류장의 하차 인원(6시 ~ 11시)
    • ac_rideXX : 맞은편 정류장의 탑승의 2시간 간격 (ex 6시부터 7시 까지), 예측하는 y값이 18시 ~ 20시 까지이므로 생성함
    • ac_offXX : 맞은편 정류장의 하차의 2시간 간격 (ex 6시부터 7시 까지), 예측하는 y값이 18시 ~ 20시 까지이므로 생성함
    • ac_ride_sum : 맞은편 정류장의 승차인원 합계
    • ac_off_sum의 : 맞은편 정류장의 하차인원 합계

02. Data EDA


1. 실수형 Columns의 histogram

  • 승하차 Column에 대해서는 대부분 0에 값이 몰려있음을 알수 있다. 이는 제주도 전역의 버스는 대부분 승하차 인원이 없으나, 일부지역(시내)에 승하차 인원이 몰려있고, 그 지역만 교통체증이 심각하다는것을 의미한다.
  • 강수량(precipitation)은 150근처의 값이 보이는데, 이는 태풍이 온것으로 파악 된다.

2. 위치 정보

  • 데이터의 위도,경도를 기반으로 그래프를 그리면 제주도의 버스정류장의 위치를 볼수 있다. 자세히 보면 알겠지만, 제주시내와 서귀포 시내에 버스정류장이 몰려있음을 알수 있다.
  • 왼쪽 위 부분은 바다가 아닌 추자도이며, 오른쪽 끝은 우도이다.

3. 종속 변수

종속변수의 distplot

  • 종속 변수의 distplot을 보면 0부근에 몰려있는것으로 확인된다. 이는 종속 변수 또한 주도 전역의 버스는 대부분 승하차 인원이 없으나, 일부지역(시내)에 승하차 인원이 몰려있고, 그 지역만 교통체증이 심각하다는것을 의미 한다.

종속 변수(18시 ~ 20시 승차한 인원)가 50명 이상인 정류소

  • 종속 변수(18시 ~ 20시 승차한 인원)가 50명 이상인 정류장만 확인해보았고 해당 정류장은 대학교, 공항, 아파트근처의 정류장으로 파악되었으며,
  • 해당 정류장들을 지도에 표시해본 결과, 예상대로 제주시와 서귀포시의 시내 지역에 있는 정류장이 다수임을 확인할 수 있었다.

4. 날짜 데이터

일별, 요일별, 평일과 주말, 공휴일

  • 일별 데이터를 살펴보면, 9월 6일, 21일, 22일에 18시에 탑승한 인원이 다른날보다 상대적으로 낮은것을 알수 있다. 해당 날짜에 어떤 일이 있었는지 조금더 탐색이 필요할것 같다.
  • 요일별 데이터에서는 평일과 주말은 월 ~ 목까지의 탑승량이 금 ~ 일까지의 탑승량보다 많은것으로 나타났다. 18시 ~ 20시 사이의 탑승한 인원은 아무래도 직장인일 가능성이 높고, 해당 직장인들이 퇴근하는 시간대에 교통혼잡이 많을 가능이 높은것을 알수 있다.
  • 평일과 주말의 18시 ~ 20시의 탑승객도 차이를 보였다. 1이 평일이며 평일은 월 - 목까지의 데이터이다.
  • 공휴일과 공휴일이 아닌 데이터, 즉 9월은 추석연휴에 18시 ~ 20시에 탑승한 인원이 전체의 7%를 차지하고 있다. 전체 100%에서, 추석연휴는 3일이므로, 단순계산하더라도 전체 탑승인원 대비 10%정도가 나오는것이 맞았으나, 연휴에는 직장인들이 출근을 하지 않기 떄문에 18시 ~ 20시의 인원은 감소한것으로 보이며, 이는 18시 ~ 20시에 탑승하는 인원은 직장인들이 대부분인것으로 파악되고, 공휴일에 탑승하는 사람은 직장인보다 관광객일 가능성이 높다.

5. 시내, 시외 데이터

  • 시내, 시외 버스의 비율을 보면 시외 버스가 전체의 1.6%를 차지하는것으로 보인다. 시외버스보다는 시내버스의 이용률이 높으며, 시외버스를 이용하여 출퇴근을 하는 인원은 적다는 결론이 나온다.

6. 정류장의 버스 노선 데이터

3563개의 버스 정류장에서 운행된 버스 노선

  • 전체 3563개의 버스 정류장에서 한달간 운행된 버스 노선의 숫자이다. 가장 많은 버스노선이 운행된 정류장은 한달간 약 1600회 가량의 버스노선이 운행되었다.

상위 500개의 버스 정류장에서 운행된 버스 노선

  • 상위 500개의 버스정류장의 한달간 운행된 버스노선이다. 3563개의 정류장 중 많이 운행된 상위 500개의 정류장이 57%를 차지한다는 것은, 승하차 인원이 많을것으로 예상되며, 이는 전체 데이터에 대해 대표성을 가지지 않을까 생각해보았다.

상위 10개의 버스 정류장에서 운행된 버스 노선

  • 상위 10개의 버스정류장의 한달간 운행된 버스노선이다. 상위 정류장들은 1600회 ~ 1200회의 노선이 버스정류장에 지나갔다.

7. 승하차 인원 분석

전체 승하차 인원의 상위 10개 정류장

  • 전체 승하차 인원이 제일 많은 10군데의 정류장을 확인해 보았다. 위의 예상대로, 시내(시청, 한라병원)가 가장 많았다. 그리고 공항, 버스터미널도 상위권을 보였다. 다만, 제주대학교는 시내와는 거리가 있는데, 이는 제주대학생들이 대부분은 버스를 타고 이동하는것으로 생각된다. 참고로, 한라병원은 제주시내에 위치하고 있다.

승차 인원의 상위 10개 정류장

  • 관광도시 답게, 버스터미널정류장에서 많은 사람들이 탑승을 하는것을 알수 있었고 이어서 시내애서 많은 사람들이 탑승을 하는것으로 보인다.

하차 인원의 상위 10개 정류장

  • 위와 마찬가지로 공항과, 시내가 하차가 많은 정류장으로 파악되었다.

제주도 시/읍 별 승하차

  • 제주시가 전체 버스 승하차 인원 데이터의 49.03%를 차지하는것으로 파악되었다. 제주시가 유동인구도 많고, 기업이 밀집해있는 지역으로 알수 있다.

제주도 시/읍별 18시 ~ 20시 승차 인원

  • 제주시에서 18시 ~ 20시에 승차 인원이 그외 지역보다 월등히 많은것으로 파악되었다, 이는 위에서 본 전체 승하차 인원의 비율보다 큰것으로, 이는 제주시에서 18시 ~ 20시에 탑승하는 인원이 많고, 기업이 밀집해있는 지역이라는것을 파악 할수 있다.

승하차 인원에 대한 결론

  • 제주도는 대한민국의 대표적인 관광지이다. 공항과 버스터미널은 특정 시간에 구애없이 사람이 많이 승하차하는 지역으로 파악이 된다.
  • 제주공항과, 터미널등의 특수 관광정류장을 제외하면 제주시내에는 많은 인원이 승하차를 하는곳임을 알수 있다. 이를통해 제주도 전역의 교통체증이 아닌, 제주시내에 교통체증이 극심한것으로 알수 있다.

8. 배차간격

  • 노선별 배차간격을 본 결과 40분 미만의 배차간격을 가지는 노선이 많은것을 알수 있다, 이는 제주시내에 다니는 노선으로 파악되며,그 이상의 배차간격을 가지는 노선은 제주시내가 아닌 그외 지역으로 판단된다.

9. 온도

  • 제주도의 9월 평년 기온은 최저 20도, 최고 25도이다. 온도에 대한 그래프에서는 큰 특이점을 찾기가 어려워 보인다.

10. 강수량

9월 전체 강수량

  • 강수량을 보면 60 ~ 100, 120 ~ 160 사이에 강수량이 보인다. 그 아래 강수량은 일반적으로 비가오면 생기는 강수량인데, 그 이상은 조금 특이점이 있어 보인다.

태풍이 온날의 강수량

  • 2019년 9월6일 ~ 2019년 9월 7일은 태풍 링링이, 2019년 9월 21일 ~ 2019년 9월 22일은 태풍 타파가 제주도에 상륙한 날이다.
  • 따라서, 해당 날짜에 강수량이 많을수 밖에없었으며, 태풍이 온 날이면 버스 승하차에 영향을 미치지 않을까 생각해보았다.

태풍이 온날과 안온날의 차이

  • 태풍이 온날 18시 ~ 20시에 탑승한 인원의 평균은 0.76명이고, 태풍이 오지않은날은 1.3명이다.
  • 또한 태풍이 온날 탑승한 인원의 최고치는 105명이며, 태풍이 오지 않은날은 272명으로, 꽤 많은 차이가 나는것을 알수 있었다.
  • 이는 날씨(태풍)이 버스탑승인원에 영향을 주는것으로 판단하였다.

11. 상관관계

  • 종속 변수(ride18)과의 상관관계가 높은 변수는 오전에 많이 타고 내리는 곳으로 파악되었다.(ride67,ride89,ride1011, off67,off89,off1011) 아마 오전에 출근을 하며 승하차를 하고, 18시 ~ 20시에 퇴근을하며 승하차를 하기 때문이 아닌가 생각해본다.

03. Liner Regression Model 생성


1. 시행착오

너무많은 카테고리 변수

  • 선형회귀 예측에 꼭 필요한 카테고리 변수 중 정류장 코드는 3563개, 버스노선ID는 613개로 너무 많은 상황이었다. 심지어, 해당 변수들을 넣고 모델링을 위해 statsmodels의 OLS를 돌려도 메모리 오류가 나는 상황이었다.

정류장 코드와 버스노선ID를 제외

  • 어쩔수없이 정류장 코드와 버스노선 ID를 제외시키고 선형회귀 분석 결과 R-squared값이 0.443으로 생각보다 낮은 수치가 나왔다.
1-1. 정류장 코드와 버스노선ID를 대체할 변수 찾기

주소기반으로 정류장을 범주화

  • 제주도를 총 72개의 주소로 범주화 시키는 작업을 진행(읍면동 단위)

주소기반으로 정류장을 범주화를 추가한 R-squared

  • 정류장 코드와 버스노선ID를 대체할 변수를 넣어도 0.455를 넘지 않았다.
  • 그외의 도심과 정류장의 거리도 추가하였으나, 큰 변화가 있지는 않았다.

2. 3563개의 모델을 생성

  • 결국 하나의 모델을 포기하고 정류장 3563개의 모델을 각각 만들기로 결정하였다.
  • 정류장별로 모델을 각각 만들다보니 한달에 1번 운행한 불규칙한 데이터가 문제였고, 불규칙한 데이터는 따로 Zero-model로 만들어, 회귀모델을 생성하지 않았다.
  • 이후 다른 데이터에서 Zero-model의 정류장에 대해 예측을 해야한다면, zero-model의 종속변수(ride18)의 값을 그대로 예측하는것으로 코드를 작성하였다.
  • Zero-model을 제외한 나머지 3472개 정류장에 대해 각각의 선형회귀예측모델을 생성하기로 하였다.
  • 3472개의 모델을 생성하나, 각 모델별 formula는 동일하다.
  • OLS모델은 statsmodels의 OLS 클래스를 사용하였다.

3. 과최적화 확인

정규화 선형회귀 모델의 알파값에 따른 검증 데이터의 R-squared값 변화

  • 데이터가 많은 상위 10개의 정류장 기준으로 Ridge, Lasso, Elastic Net의 alpha값을 찾은 결과 R-squared값이 올라가는 지점없이, 쭉 떨어지기만 했다는것을 볼수 있었다. 이 결과로 인해 정규화를 진행하지 않는것이 맞다고 판단하였다.

4. 다중공선성 확인

실수형 변수별 VIF

  • 45개의 실수형 Columns들의 VIF를 확인결과 5개를 제외하고는 모두 다중공선성이 높은것으로 파악되었다.

  • VIF값이 높은 40개의 변수들을 6개의 차원으로 PCA를 진행하였으며, 이는 약 70%의 설명력을 가진다.

5. 모델 성능 확인

전처리 전 R-squared

  • 특별한 전처리를 하지 않고 기본적인 scale만 진행 후 train과 test의 R-squared는 train은 0.77, test는 0.62가 나온것으로보아 많은 차이가 있다. 아무래도 다중공선성 떄문이 아닐까 생각한다.

전처리 후 R-squared (No PCA)

  • EDA 과정에서 추가된 카테고리변수들과 각종 전처리된 변수를 적용하였을때 R-squared는 train은 0.79, test는 0.69가 나왔다. 전처리 전보다 성능이 좋아졌음을 뜻하지만, 아직도 train과 test의 갭차이가 큰것으로 보아, 다중공선성을 의심해볼수 있다.

PCA 적용 R-squared

  • 위의 전처리된 컬럼들을 PCA를 적용하였을때 R-squared는 train은 0.78, test는 0.72의 성능을 보여주었다. train의 R-squared은 떨어졌으나, 사실 test의 R-squared가 더욱 중요하기 떄문에 PCA가 의미있는 행동이였음을 알수 있었다. 다만, 아직도 train과 test의 갭 차이가 나는것은 카테고리변수 중 버스노선ID에서 생기는 다중공선성일 가능성이 높다.

대표모델 R-squared

  • 3472개의 정류장중 가장 데이터가 많은 정류장 1개의 모델의 OLS summary를 확인해 보았다. 가장 많은 데이터를 가진 정류장은 1610개의 데이터를 가지고 있었으며, 해당 정류장의 train의 R-squared값은 0.853이였으며, test 데이터의 R-squared값은 0.83으로 전체 데이터의 train, test의 R-squared 보다 높은 수치를 보였다. 아무래도 정류장의 데이터가 많으니, 학습과 예측을 잘 하는게 아닐까 싶다.

04. 프로젝트 회고


  • FastCampas에서 데이터 사이언티스트 스쿨 과정을 들으며 진행한 프로젝트 중 가장 오랜 기간, 그리고 큰 프로젝트였다.
  • 3473개의 모델을 만들기로 결정한뒤 생기는 많은 오류들을 해결하느라 엄청난 고민을 했었다. 정말 첩첩산중이라는 단어가 왜 생겼는지 알것 같은 기분이였다.
  • 그래도 결국엔 하나씩 해결해 나가는게 참으로 뿌듯했었고, 값진 경험인것 같다. 그리고 이게 마지막이 아니라 데이터 분석이라는 직무에 있어서, 첫 시작의 발걸음이 되는 프로젝트라 생각하니 많은 정감이 가는 프로젝트 이다.
  • 모든면에서 완벽하고 마음에 드는것은 아니나, 추후 스킬이 더 쌓인다면 다시한번 정리해보고 싶은 마음이 든다.
  • 마지막으로 프로젝트에 많은 도움을 주신 김도형 박사님과 이재근 클래스매니저님에게 감사의 인사를 보냅니다.
This post is licensed under CC BY 4.0 by the author.