- 운행일자를 기준으로 2019년 10월 한 달 간 운행열차의 운행내역 데이터를 사용하여 정차역에 따른 승,하차,통과 인원 및 해당 역 별 지가지수를 파악하여 광역 철도 데이터의 탐색적 분석 및 공간 데이터 시각화 분석이 이루어짐.
- 일자별전철역승차인원 데이터
- 여객역코드 데이터
- 역 별 위치 데이터(위,경도)
- 역세권 별 지가지수 데이터(2014~2017)
import pandas as pd
import pandas_profiling
pd.options.mode.chained_assignment = None
from pyproj import Proj, transform
import numpy as np
import matplotlib.pyplot as plt
import ipywidgets as widgets
from ipywidgets import interact, interact_manual, GridspecLayout
import ipywidgets
from collections import OrderedDict
import geopandas as gpd
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
import warnings
warnings.filterwarnings(action='ignore')
path = '/home/yubin90/pyWork/diamond/'
def stp_cols(df):
cols = list(map(str.strip, df.columns.tolist()))
df.columns = cols
return df
ride_acvm_df = pd.read_csv(path+'TB_COL_KR_TRVL_STNP_CLSF_RIDE_ACVM.csv', low_memory=False)
ride_acvm_df = stp_cols(ride_acvm_df)
ride_acvm_df = ride_acvm_df.drop(labels='AGGR_YMDHMS', axis=1)
trvl_stn_cd_df = pd.read_csv(path+'TB_COL_KR_TRVL_STN_CD.csv', low_memory=False)
trvl_stn_cd_df = stp_cols(trvl_stn_cd_df)
trvl_stn_cd_df = trvl_stn_cd_df.drop(labels='AGGR_YMDHMS', axis=1)
trvl_stn_cd_df = trvl_stn_cd_df.drop_duplicates(subset='STN_CD')
stn_loc_df = pd.read_csv(path+'stn_loc_no_dup.csv', encoding='cp949')
#년월 생성
ride_acvm_df['YYMM'] = ride_acvm_df['RUN_DT'].astype(str).str[:-2]
# 역명코드 dict 생성
trvl_stn_cd_dict= dict(zip(trvl_stn_cd_df.STN_CD, trvl_stn_cd_df.KOR_STN_NM))
# dict mapping(역명 한글이름 매칭)
ride_acvm_df['KOR_STOP_STN'] = ride_acvm_df['STOP_STN'].map(trvl_stn_cd_dict)
ride_acvm_with_loc = pd.merge(ride_acvm_df,stn_loc_df, how='left', left_on='KOR_STOP_STN', right_on='STN_NM')
ride_acvm_with_loc
| RUN_DT | STOP_STN | STLB_TRN_CLSF_CD | UP_DN_DV_CD | ABRD_PRNB | GOFF_PRNB | NSTP_PRNB | YYMM | KOR_STOP_STN | STN_NM | LAT | LNG |
0 | 20191001 | 3900280 | 0 | D | 1148 | 3021 | 15849 | 201910 | 오송 | 오송 | 36.620098 | 127.327582 |
1 | 20191001 | 3900280 | 7 | D | 630 | 1422 | 6902 | 201910 | 오송 | 오송 | 36.620098 | 127.327582 |
2 | 20191001 | 3900280 | 10 | D | 49 | 127 | 615 | 201910 | 오송 | 오송 | 36.620098 | 127.327582 |
3 | 20191001 | 3900023 | 7 | U | 19 | 4749 | 378 | 201910 | 서울 | 서울 | 37.554073 | 126.970702 |
4 | 20191001 | 3900096 | 7 | U | 1332 | 710 | 4545 | 201910 | 동대구 | 동대구 | 35.879437 | 128.628784 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
29277 | 20191031 | 3900292 | 1 | U | 10 | 6 | 60 | 201910 | 충주 | 충주 | 36.975892 | 127.909136 |
29278 | 20191031 | 3900556 | 1 | U | 0 | 0 | 41 | 201910 | 춘양 | 춘양 | 36.937810 | 128.919938 |
29279 | 20191031 | 3900611 | 1 | U | 5 | 0 | 36 | 201910 | 민둥산 | 민둥산 | 37.243701 | 128.773657 |
29280 | 20191031 | 3900703 | 1 | D | 0 | 1 | 61 | 201910 | 북천 | 북천 | 35.111383 | 127.883501 |
29281 | 20191031 | 3900703 | 1 | U | 0 | 0 | 39 | 201910 | 북천 | 북천 | 35.111383 | 127.883501 |
29282 rows × 12 columns
for_top_df = ride_acvm_df[['ABRD_PRNB', 'GOFF_PRNB', 'NSTP_PRNB', 'KOR_STOP_STN']]
#승차 top 10
sum_abrd_by_stn = pd.DataFrame(for_top_df.groupby('KOR_STOP_STN')['ABRD_PRNB'].sum())
sum_abrd_by_stn = sum_abrd_by_stn[sum_abrd_by_stn.index != '청량리']
sum_abrd_by_stn.sort_values('ABRD_PRNB', ascending=False).head(10)
| ABRD_PRNB |
KOR_STOP_STN | |
--- | --- |
서울 | 1527592 |
동대구 | 748905 |
용산 | 720647 |
대전 | 654794 |
부산 | 636205 |
수원 | 503330 |
광명 | 422479 |
영등포 | 358667 |
오송 | 270318 |
천안아산 | 250665 |
#하차 top 10
sum_goff_by_stn = pd.DataFrame(for_top_df.groupby('KOR_STOP_STN')['GOFF_PRNB'].sum())
sum_goff_by_stn.sort_values('GOFF_PRNB', ascending=False).head(10)
| GOFF_PRNB |
KOR_STOP_STN | |
--- | --- |
서울 | 1545800 |
동대구 | 754050 |
용산 | 722747 |
대전 | 656247 |
부산 | 632395 |
수원 | 508386 |
광명 | 416549 |
영등포 | 362172 |
청량리 | 277838 |
오송 | 261490 |
#통과 top 10
sum_nstp_by_stn = pd.DataFrame(for_top_df.groupby('KOR_STOP_STN')['NSTP_PRNB'].sum())
sum_nstp_by_stn.sort_values('NSTP_PRNB', ascending=False).head(10)
| NSTP_PRNB |
KOR_STOP_STN | |
--- | --- |
대전 | 3271135 |
광명 | 3027384 |
동대구 | 2769875 |
오송 | 2181114 |
천안아산 | 2039345 |
수원 | 1731419 |
서울 | 1632729 |
천안 | 1445134 |
익산 | 1362569 |
평택 | 1353513 |
abrd_top = sum_abrd_by_stn.sort_values('ABRD_PRNB', ascending=False).head(10).index
abrd_top = abrd_top.tolist()
abrd_top
['서울', '동대구', '용산', '대전', '부산', '수원', '광명', '영등포', '오송', '천안아산']
- 한국감정원 월별 KTX역 역세권 지가지수(2014년 11월 ~ 2019년 7월)
*지가지수 : 기준시점의 지수를 100으로 보았을 때, 기준시점 대비 가격상승분을 반영한 해당시점의 지수를 나타냄
- 각 역 단위 실 주소 및 위,경도
land_value = pd.read_csv(path+'land_value_2017_2019.csv', encoding='cp949')
land_value[['YYMM', '오송']].head()
| YYMM | 오송 |
0 | 201701 | 100.226 |
1 | 201702 | 99.968 |
2 | 201703 | 100.988 |
3 | 201704 | 101.482 |
4 | 201705 | 102.830 |
#승차인원 상위 역 지가지수 변화 그래프
#land_value.set_index(land_value['YYMM'], inplace=True)
cols = ['YYMM'] + abrd_top
land_value_plt = land_value[cols]
land_value_plt['YYMM'] = land_value_plt['YYMM'].astype(str)
font = {'family' : 'nanumgothic', 'size':10}
plt.rc('font', **font) #font option
plt.rcParams["figure.figsize"] = (25,10)
land_value_plt.plot(x='YYMM', y=abrd_top)
plt.title('월별 역의 지가지수 흐름', fontsize=18)
plt.xlabel('Date', fontsize=18)
plt.ylabel('지가지수', fontsize=18)
plt.legend(fontsize=15, loc='best')
<matplotlib.axes._subplots.AxesSubplot at 0x7f16f307b2e8>
Text(0.5, 1.0, '월별 역의 지가지수 흐름')
Text(0.5, 0, 'Date')
Text(0, 0.5, '지가지수')
<matplotlib.legend.Legend at 0x7f16f30715f8>

stn_addr = pd.read_csv(path+'stn_addr_2016.csv', encoding='cp949')
stn_addr.head()
| 역명 | 주소 |
0 | 가수원 | 대전 서구 가수원동 547-1 |
1 | 가야 | 부산 부산진구 백양대로 91 |
2 | 각계 | 충북 영동군 심천면 각계리 |
3 | 감곡 | 전북 정읍시 감곡면 유정리 196-1 |
4 | 강경 | 충남 논산시 강경읍 대흥리 32 |
#merge
ride_acvm_with_loc_addr = pd.merge(ride_acvm_with_loc, stn_addr, how='left', left_on='KOR_STOP_STN', right_on='역명')
ride_acvm_with_loc_addr.head()
| RUN_DT | STOP_STN | STLB_TRN_CLSF_CD | UP_DN_DV_CD | ABRD_PRNB | GOFF_PRNB | NSTP_PRNB | YYMM | KOR_STOP_STN | STN_NM | LAT | LNG | 역명 | 주소 |
0 | 20191001 | 3900280 | 0 | D | 1148 | 3021 | 15849 | 201910 | 오송 | 오송 | 36.620098 | 127.327582 | 오송 | 충청북도 청원군 강외면 봉산리 370-1 |
1 | 20191001 | 3900280 | 7 | D | 630 | 1422 | 6902 | 201910 | 오송 | 오송 | 36.620098 | 127.327582 | 오송 | 충청북도 청원군 강외면 봉산리 370-1 |
2 | 20191001 | 3900280 | 10 | D | 49 | 127 | 615 | 201910 | 오송 | 오송 | 36.620098 | 127.327582 | 오송 | 충청북도 청원군 강외면 봉산리 370-1 |
3 | 20191001 | 3900023 | 7 | U | 19 | 4749 | 378 | 201910 | 서울 | 서울 | 37.554073 | 126.970702 | 서울 | 서울 용산구 동자동 43-205 |
4 | 20191001 | 3900096 | 7 | U | 1332 | 710 | 4545 | 201910 | 동대구 | 동대구 | 35.879437 | 128.628784 | 동대구 | 대구 동구 동대구로 550(신암동) |
- 좌표계 포맷 변경
- 역 별 위경도(WG84 -> UTM-K)
proj_UTMK = Proj(init='epsg:5178') # UTM-K(Bassel)
proj_WGS84 = Proj(init='epsg:4326') # Wgs84 경도/위도, GPS
def transform_w84_to_utmk(df):
return pd.Series(transform(proj_WGS84, proj_UTMK, df['LNG'], df['LAT']), index=['LNG', 'LAT'])
ride_acvm_with_loc_addr[['LNG_utmk', 'LAT_utmk']] = ride_acvm_with_loc_addr.apply(transform_w84_to_utmk, axis=1)
ride_acvm_with_loc_addr.head()
| Unnamed: 0 | RUN_DT | STOP_STN | STLB_TRN_CLSF_CD | UP_DN_DV_CD | ABRD_PRNB | GOFF_PRNB | NSTP_PRNB | YYMM | KOR_STOP_STN | STN_NM | LAT | LNG | 역명 | 주소 | LNG_utmk | LAT_utmk |
0 | 0 | 20191001 | 3900280 | 0 | D | 1148 | 3021 | 15849 | 201910 | 오송 | 오송 | 36.620098 | 127.327582 | 오송 | 충청북도 청원군 강외면 봉산리 370-1 | 9.847740e+05 | 1.846622e+06 |
1 | 1 | 20191001 | 3900280 | 7 | D | 630 | 1422 | 6902 | 201910 | 오송 | 오송 | 36.620098 | 127.327582 | 오송 | 충청북도 청원군 강외면 봉산리 370-1 | 9.847740e+05 | 1.846622e+06 |
2 | 2 | 20191001 | 3900280 | 10 | D | 49 | 127 | 615 | 201910 | 오송 | 오송 | 36.620098 | 127.327582 | 오송 | 충청북도 청원군 강외면 봉산리 370-1 | 9.847740e+05 | 1.846622e+06 |
3 | 3 | 20191001 | 3900023 | 7 | U | 19 | 4749 | 378 | 201910 | 서울 | 서울 | 37.554073 | 126.970702 | 서울 | 서울 용산구 동자동 43-205 | 9.534382e+05 | 1.950351e+06 |
4 | 4 | 20191001 | 3900096 | 7 | U | 1332 | 710 | 4545 | 201910 | 동대구 | 동대구 | 35.879437 | 128.628784 | 동대구 | 대구 동구 동대구로 550(신암동) | 1.102084e+06 | 1.765044e+06 |
- 승차,하차,통과인원수 0이거나 음수인 경우 제외
ride_acvm_with_loc_addr = ride_acvm_with_loc_addr[ride_acvm_with_loc_addr['STLB_TRN_CLSF_CD'] == 0]
ride_fin_df = ride_acvm_with_loc_addr[(ride_acvm_with_loc_addr[['ABRD_PRNB','GOFF_PRNB', 'NSTP_PRNB']] != 0).all(axis=1)]
ride_fin_df = ride_fin_df[['RUN_DT', 'STOP_STN', 'UP_DN_DV_CD', 'ABRD_PRNB', 'GOFF_PRNB', 'NSTP_PRNB','YYMM','KOR_STOP_STN', 'LNG_utmk', 'LAT_utmk']]
ride_fin_df_cols = ['운행년월일', '정차역코드', '상하행구분', '승차인원수', '하차인원수', '통과인원수', '운행년월','역명', 'LNG_utmk', 'LAT_utmk']
ride_fin_df.columns = ride_fin_df_cols
updown_dict = {'U':'상행', 'D':'하행'}
ride_fin_df['상하행구분'] = ride_fin_df['상하행구분'].map(updown_dict)
ride_fin_df.head()
| 운행년월일 | 정차역코드 | 상하행구분 | 승차인원수 | 하차인원수 | 통과인원수 | 운행년월 | 역명 | LNG_utmk | LAT_utmk |
0 | 20191001 | 3900280 | 하행 | 1148 | 3021 | 15849 | 201910 | 오송 | 9.847740e+05 | 1.846622e+06 |
6 | 20191001 | 3900047 | 하행 | 968 | 124 | 1819 | 201910 | 수원 | 9.558390e+05 | 1.918382e+06 |
12 | 20191001 | 3900685 | 하행 | 1 | 208 | 148 | 201910 | 창원 | 1.100874e+06 | 1.696025e+06 |
13 | 20191001 | 3900902 | 상행 | 980 | 17 | 1666 | 201910 | 창원중앙 | 1.109497e+06 | 1.694457e+06 |
16 | 20191001 | 3900229 | 상행 | 2269 | 72 | 3222 | 201910 | 광주송정 | 9.355993e+05 | 1.682427e+06 |
- 해당 운행일자의 각 역 별 상-하행 구분에 따른 승차,하차,통과 인원 및 역의 지가지수 변동을 인터렉티브하게 표현함
widget_time = sorted(list(ride_fin_df['운행년월'].unique()))
widget_stn = list(ride_fin_df['역명'].unique())
widget_updown = ['하행', '상행']
widget_prnb_Value = ['승차인원수', '하차인원수', '통과인원수']
ride_acvm_gpd = gpd.GeoDataFrame(
ride_fin_df, geometry=gpd.points_from_xy(ride_fin_df.LNG_utmk, ride_fin_df.LAT_utmk)
)
ride_acvm_gpd.head()
| 운행년월일 | 정차역코드 | 상하행구분 | 승차인원수 | 하차인원수 | 통과인원수 | 운행년월 | 역명 | LNG_utmk | LAT_utmk | geometry |
0 | 20191001 | 3900280 | 하행 | 1148 | 3021 | 15849 | 201910 | 오송 | 9.847740e+05 | 1.846622e+06 | POINT (984774.029 1846622.323) |
6 | 20191001 | 3900047 | 하행 | 968 | 124 | 1819 | 201910 | 수원 | 9.558390e+05 | 1.918382e+06 | POINT (955838.951 1918382.084) |
12 | 20191001 | 3900685 | 하행 | 1 | 208 | 148 | 201910 | 창원 | 1.100874e+06 | 1.696025e+06 | POINT (1100874.105 1696025.006) |
13 | 20191001 | 3900902 | 상행 | 980 | 17 | 1666 | 201910 | 창원중앙 | 1.109497e+06 | 1.694457e+06 | POINT (1109496.880 1694456.908) |
16 | 20191001 | 3900229 | 상행 | 2269 | 72 | 3222 | 201910 | 광주송정 | 9.355993e+05 | 1.682427e+06 | POINT (935599.278 1682426.726) |
#base map loading
whole_map = pd.read_pickle(path+'whole_map.pkl')
whole_map.columns = ['geometry']
#b_time
w1 = widgets.Dropdown(
options=widget_time,
value=widget_time[0],
description='운행년월 :',
disabled=False,
)
#b_stn
w2 = widgets.Dropdown(
options=widget_stn,
value=widget_stn[0],
description='역명 :',
disabled=False,
)
#b_dir
w3 = widgets.Dropdown(
options=widget_updown,
value=widget_updown[0],
description='상하행 :',
disabled=False,
)
#want_view
w4 = widgets.Dropdown(
options=widget_prnb_Value,
value=widget_prnb_Value[0],
description='기준인원 :',
disabled=False,
)
def view(b_time = '', b_stn = '', b_dir = '', want_view = ''):
if b_time=='All':
return ride_acvm_gpd
temp_df = ride_acvm_gpd
temp_df = temp_df[temp_df['운행년월']==b_time]
temp_df = temp_df[temp_df['역명']==b_stn]
if temp_df.shape[0] == 0:
return "데이터가 없습니다"
font = {'family' : 'NanumGothic', 'size':10}
plt.rc('font', **font) #font option
fig = plt.figure(figsize=(28,8), constrained_layout=False)
gs = fig.add_gridspec(nrows=5, ncols=8, left=0.05, right=0.48, wspace=0.5)
ax1 = fig.add_subplot(gs[:5, :4]) #row, col
ax2 = fig.add_subplot(gs[:2, 4:8]) #row, col
ax3 = fig.add_subplot(gs[3:5, 4:8])
whole_map.plot(color='white', edgecolor='black', legend=True, ax=ax1, linewidth=0.2)
gpd.GeoDataFrame(temp_df).plot(column='역명', marker='*', color="r", markersize=40,legend=True, ax=ax1)
ax1.axes.get_xaxis().set_visible(False)
ax1.axes.get_yaxis().set_visible(False)
ax1.set_title('역 위치')
#1. 역명,기준연월, 상하행
df = temp_df[(temp_df['역명'] == b_stn) & (temp_df['상하행구분'] == b_dir)]
bar_1 = df[['운행년월일',want_view]]
bar_1.sort_index().plot.bar(x='운행년월일',y=want_view, ax=ax2, fontsize=10, color="orange", rot=90)
ax2.set_title('2019년 한달 간 기준인원 변화')
#2. 해당 역 지가지수
bar_2 = pd.DataFrame(land_value[['YYMM', b_stn]])
bar_2.sort_index().plot.bar(x='YYMM',y=b_stn, ax=ax3, fontsize=10, color="purple", rot=90)
ax3.set_ylim([80, 140])
ax3.set_title('17~19년 해당 역 지가지수 변화')
interact_manual(view, b_time=w1, b_stn=w2, b_dir=w3, want_view = w4)
#from IPython.display import Image
#Image('/home/yubin90/pyWork/diamond/widget_result.png')
<function main.view(b_time='', b_stn='', b_dir='', want_view='')>
