앉아서 출근할 수 있을까 — 버스 정류장 승하차 인원 분석
요즘 본가(죽전)에서 을지로에 있는 위워크로 출근하고 있다. 어디서 버스를 타야 출근시간에 앉아서 갈 수 있을까 하고 해봤다.
집에서 버스로 한번에 갈 수 있는 역은 오리역, 미금역, 정자역이다. 오리역에선 8100번, 미금역에서는 8100과 M4102, 정자역에서는 8100, M4102, 8110을 타고 회사가 위치한 서울백병원,평화방송 정류장으로 갈 수 있다.
8시 30분~ 9시 30분대에서, 각각의 배차간격은 최대 10분이다. 만약 내가 오리역에서 기다린다면 최대 10분을 기다려야하지만, 정자역에서는 세개의 버스 노선 중 빨리 오는것을 타면 되니까 내가 기다려야 하는 시간이 더 적다. 고로 똑같이 앉아서 갈 수 있다면 정자역에서 타는게 시간적으로 이득이다.
근데 정자역에서 버스를 타면 기점이 정자역인 8110번이 아닌 이상 자칫하면 서서 가야 할 수도 있다. 그렇다고 내가 매번 정자역에서 서서 출근하는 손해를 감수하면서까지 10분을 단축해야 할 이유는 없다. 그래서 8시 반~9시 반 사이에서 정자역을 거치는 버스들의 남은 좌석의 수를 조사해 보기로 했다.
API 검색
우선 분석을 위한 API를 찾아 봤다. 서울시의 경우, 각 정류장별 승하차 인원 데이터를 제공했지만 G-Bus(빨간 버스, 경기버스)의 경우 따로 그 데이터를 제공하지 않았다. 버스 앱에서 쓰이는 현재 버스 노선별 위치와 남은 좌석 API 밖에 없었다. 일요일 밤에 구상했는데, 지속적으로 서비스할 생각도 없었고, 별도의 승인이 필요한 API를 쓰면 당장 내일 아침의 데이터를 수집할 수가 없었다. 그래서 네이버 지도, 다음 지도에서 제공하는 버스 정보 API를 크롬 개발자 도구를 통해 살펴봤다. 네이버 지도는 API인터페이스가 G-Bus 공식 API와 조금 달라서, 정류소 정보나 버스 노선 ID등이 문서로 존재하지 않았다. 다음지도도 비슷했던 것 같다. gbis.go.kr 이 가장 날로 먹기 좋아보여서 저기서 발생하는 Ajax 요청을 봤다.
정류소 ID와 노선 ID는
activateStaFunc('sta7', '정자역', ' 07624', '7', 'U', '206000725', '234000878');
이런 함수가 있길래 원형을 봤더니
function activateStaFunc(staId, staNm, staNo, staIdx, UD, stationId, routeId)
이렇게 친절하게 명시돼 있어서 공식 문서 보다 가독성이 좋았다 😅
데이터 수집
당장 내일 아침에 출근해야 됐으니 최대한 간단하게 짜고 자고 싶었다. 데이터 전처리도 하기 귀찮아서 MongoDB에 그대로 때려 박았는데 이게 결과적으로 좋은 선택이었다. 분석 과정에서 나는 에러는 얼마든지 다시 try 할 수 있지만 크롤링과정에서 에러가 난다면 그 시간의 정보는 다시 돌아오지 않는다. 또 지금은 필요하지 않을 줄 알았던 데이터가 나중에 필요할 수도 있고.
from datetime import datetime
from requests import post
import pymongo
import sys
# 8100: 234000878
# 8110: 204000082
# m4102: 234001159
def get_buses(bus_no):
bus_dic = {"b8100": 234000878, "b8110": 204000082, "m4102": 234001159}
user = ""
passwd = ""
db = ""
client = pymongo.MongoClient('mongodb://{}:{}@127.0.0.1:27017/{}'.format(user, passwd, db))
db = client.busDB
url = "http://www.gbis.go.kr/gbis2014/schBusAPI.action"
route_id = bus_dic[bus_no]
data = post(url, data={"cmd": "searchRouteJson", "routeId": route_id}).json()
buses = []
for bus in data["result"]["realTime"]["list"]:
bus.update(crawled_at=datetime.now())
buses.append(bus)
return db, buses1분 간격으로 돌도록 crontab에 설정해줬다.
그렇게 일주일동안 데이터를 쌓아두고, redash로 시각화를 시도하다가 원하는 쿼리를 만들기가 힘들어서 MongoDB Compass + Jupyter로 했다.
Jupyter를 켜기 전에, MongoDB Client인 MongoDB Compass 로 간단하게 데이터를 살펴봤다. 인터페이스가 예뻐서 뭔가 이거만으로도 할 수 있는게 있겠다 싶었고, MongoDB를 처음 써보는거라 쿼리를 어떻게 할지 감을 잡고 싶었다.
crawled_at에 8시 30분부터 9시 10분까지 쿼리한다. {"crawled_at":{"$gte":{"$date":"2017-07-10T09:30:00.000+09:00"},"$lt":{"$date":"2017-07-10T09:10:00.000+09:00"}},"fromStationId":"206000725"}

이제 Jupyter를 켜보자
분석
from_station, to_station 구간을 지정하고, 정류장을 지날때 증가한 remain_seat를 구한다.
---------- 7월 10 일 ----------
경기77바1093 26 2017-07-10 08:30:02.091000 | -8명 탑승
경기77바1084 23 2017-07-10 08:34:02.605000 | 1명 탑승
경기77바3559 38 2017-07-10 08:43:02.494000 | 16명 탑승
경기77바1074 29 2017-07-10 08:52:02.602000 | 10명 탑승
경기77바1107 31 2017-07-10 09:01:02.203000 | -2명 탑승
경기77바1039 27 2017-07-10 09:10:02.070000 | -6명 탑승
경기77바1086 7 2017-07-10 09:28:02.262000 | -17명 탑승
10일: 버스는 평균 8.29분 간격으로 오고 평균 -0.8571428571428571명이 탑승함. 남은 자리 평균: 25.857142857142858석
---------- 7월 11 일 ----------
경기77바1084 20 2017-07-11 08:30:02.152000 | 0명 탑승
경기77바1193 14 2017-07-11 08:42:02.774000 | 1명 탑승
경기77바1085 17 2017-07-11 08:54:02.311000 | 4명 탑승
경기77바1077 25 2017-07-11 09:04:02.876000 | 20명 탑승
경기77바1070 13 2017-07-11 09:28:02.718000 | 8명 탑승
11일: 버스는 평균 11.6분 간격으로 오고 평균 6.6명이 탑승함. 남은 자리 평균: 17.8석
---------- 7월 12 일 ----------
경기77바1099 23 2017-07-12 08:30:02.334000 | 1명 탑승
경기77바1157 14 2017-07-12 08:43:02.123000 | -8명 탑승
경기77바1019 34 2017-07-12 08:48:02.512000 | 11명 탑승
경기77바1193 27 2017-07-12 08:52:02.247000 | 27명 탑승
경기77바1039 33 2017-07-12 08:58:02.141000 | 33명 탑승
경기77바1148 17 2017-07-12 09:15:02.666000 | -7명 탑승
경기77바1074 16 2017-07-12 09:19:02.773000 | -14명 탑승
경기77바1107 18 2017-07-12 09:28:02.631000 | -6명 탑승
12일: 버스는 평균 7.25분 간격으로 오고 평균 4.625명이 탑승함. 남은 자리 평균: 22.75석
---------- 7월 13 일 ----------
경기77바1148 22 2017-07-13 08:30:02.943000 | 13명 탑승
경기77바1107 12 2017-07-13 08:43:01.985000 | 6명 탑승
경기77바3531 15 2017-07-13 09:07:02.526000 | 5명 탑승
경기77바1099 26 2017-07-13 09:13:02.767000 | 16명 탑승
13일: 버스는 평균 10.75분 간격으로 오고 평균 10.0명이 탑승함. 남은 자리 평균: 18.75석
---------- 7월 14 일 ----------
경기77바3530 17 2017-07-14 08:31:02.598000 | 12명 탑승
경기77바3531 11 2017-07-14 08:44:01.876000 | 10명 탑승
경기77바3518 30 2017-07-14 08:57:02.820000 | 29명 탑승
경기77바1070 21 2017-07-14 09:09:02.120000 | 14명 탑승
경기77바1077 7 2017-07-14 09:22:02.006000 | -13명 탑승
14일: 버스는 평균 10.2분 간격으로 오고 평균 10.4명이 탑승함. 남은 자리 평균: 17.2석이정도면 정자역에서 8100번 탈 만하다. M4102번도 보자
---------- 7월 10 일 ----------
경기77바3547 38 2017-07-10 08:31:02.098000 | -2명 탑승
경기77바3539 44 2017-07-10 08:40:02.209000 | 4명 탑승
경기77바3599 37 2017-07-10 08:49:02.257000 | 3명 탑승
경기77바3540 42 2017-07-10 08:58:02.008000 | 8명 탑승
경기77바3537 44 2017-07-10 09:10:01.997000 | 10명 탑승
경기77바3598 41 2017-07-10 09:22:02.132000 | 7명 탑승
10일: 버스는 평균 8.5분 간격으로 오고 평균 5.0명이 탑승함. 남은 자리 평균: 41.0석
---------- 7월 11 일 ----------
경기77바3542 35 2017-07-11 08:31:02.129000 | -8명 탑승
경기77바3344 41 2017-07-11 08:40:02.056000 | 6명 탑승
경기77바3541 38 2017-07-11 08:52:02.660000 | 6명 탑승
경기77바3345 35 2017-07-11 09:10:02.384000 | 3명 탑승
경기77바1232 36 2017-07-11 09:19:01.922000 | 4명 탑승
11일: 버스는 평균 9.6분 간격으로 오고 평균 2.2명이 탑승함. 남은 자리 평균: 37.0석
---------- 7월 12 일 ----------
경기77바1273 31 2017-07-12 08:31:02.345000 | 0명 탑승
경기77바3598 35 2017-07-12 08:40:02.807000 | 4명 탑승
경기77바3545 44 2017-07-12 09:04:02.822000 | 15명 탑승
경기77바3541 43 2017-07-12 09:07:02.615000 | 14명 탑승
경기77바1237 34 2017-07-12 09:19:02.634000 | 5명 탑승
경기77바3537 37 2017-07-12 09:28:02.442000 | 8명 탑승
12일: 버스는 평균 9.5분 간격으로 오고 평균 7.666666666666667명이 탑승함. 남은 자리 평균: 37.333333333333336석
---------- 7월 13 일 ----------
경기77바1273 33 2017-07-13 08:43:01.857000 | -4명 탑승
경기77바3545 41 2017-07-13 08:52:02.260000 | 9명 탑승
경기77바3537 39 2017-07-13 09:01:02.663000 | 7명 탑승
경기77바3344 42 2017-07-13 09:13:02.681000 | 10명 탑승
경기77바3547 42 2017-07-13 09:22:02.763000 | 10명 탑승
경기77바3345 40 2017-07-13 09:28:02.070000 | 8명 탑승
13일: 버스는 평균 7.5분 간격으로 오고 평균 6.666666666666667명이 탑승함. 남은 자리 평균: 39.5석
---------- 7월 14 일 ----------
경기77바1277 30 2017-07-14 08:31:02.564000 | 0명 탑승
경기77바3544 38 2017-07-14 08:40:02.725000 | 15명 탑승
경기77바1232 43 2017-07-14 08:52:01.914000 | 20명 탑승
경기77바3344 42 2017-07-14 08:58:02.790000 | 19명 탑승
경기77바3599 43 2017-07-14 09:10:02.127000 | 20명 탑승
경기77바3539 36 2017-07-14 09:22:01.912000 | 13명 탑승
14일: 버스는 평균 8.5분 간격으로 오고 평균 14.5명이 탑승함. 남은 자리 평균: 38.666666666666664석넉넉하다.
결론
어느 역에서 타든 앉아서 갈 수 있다. 근데 해놓고 보니 정자역에서 버스 노선을 기다리는 시간이 너무 미미해서 별로 의미가 없다. 102번(집에서 역갈때 타는 버스)이 오리역~정자역구간을 통과하는데 걸리는 시간이 8100번이 동 구간을 통과하는데 걸리는 시간보다 길게는 5분정도 오래 걸려서, 이걸 감안하면 정자역에서 탄다고 해도 시간적 이득이 너무 미미하다.
느낀점
- 데이터 수집과 처리를 분리하자
- 쉬워보이는(분석에 필요한 파라메터가 적거나, 데이터 수가 적거나 등) 데이터로 먼저 컨셉을 잡은 후 복잡한 쿼리를 만드는게 낫다.
- MongoDB Compass 씁시다
- redash는 Custom Query를 만들기 너무 힘들다
