4. 결론 - 지하철 이용 계획 수립

위에서 도출한 편차 그래프를 통해 최적의 지하철 통학 시간대를 파악해보겠습니다. 오렌지 색깔로 된 그래프는 집에서 학교로 통학하는 경우이며 아래 빨간색 그래프는 학교에서 집으로 가는 경우입니다. 

4.1 주중 지하철 통학 스케줄

기상 시간을 고려했을 때 최소 7시부터 집에서 출발이 가능합니다. 7시 이후부터 수업시간대인 14시 30분 전까지 가장 혼잡도가 낮은 시간대는 아침 10시 경입니다. 10시에서 10시 30분 사이에 지하철을 이용하는 것이 가장 합리적으로 보입니다. 한편, 학교에서 집으로 올 때에는 18시 30분부터 혼잡도가 급격히 하락하긴 하지만 20시 30분에서 21시 사이에 지하철을 이용하는 것이 최선으로 생각됩니다. 

4.2 주말 지하철 통학 스케줄

출근 시간대를 기점으로 피크를 찍고 내려오는 주중과 달리 주말에는 혼잡도가 점심 시간대까지 지속적으로 증가하는 것을 볼 수 있습니다. 따라서, 가능하다면 일찍 출발할수록 더 유리하다고 할 수 있습니다. 하지만 8시를 기점으로 혼잡도가 급격히 증가하기 때문에 8시 직전인 7시 30분 정도가 가장 최적의 시간대라고 할 수 있습니다. 반대로 학교에서 집으로 돌아올 때에는 19시 이후에 지하철을 이용하는 것이 좋으며 21시가 가장 좋은 선택이 될 것으로 보입니다. 

3. 대책 수립

3.1 최적의 통학 시간대 파악

이전의 분석을 통해 지하철의 혼잡한 시간대를 파악할 수 있었습니다. 지금부터는 평균 혼잡도와의 편차로 변환하여 최적의 지하철 통학 시간대를 찾아보도록 하겠습니다

3.1.1 집 -> 학교

집에서 학교로 통학하는 경우를 확인해보겠습니다. 학교로 통학하는 길에 2호선(내선)과 1호선(상선)을 이용하게 되는데 네이버 지도를 기준으로 약 55분이 소요됩니다. 도보 25분 + 2호선 5분 + 1호선 19분 정도로 실질적으로 지하철을 이용하는 시간은 25분 정도입니다. 지하철 환승 시간과 대기 시간을 포함한다면 최대 35분으로  할 수 있습니다.

분석에 사용된 데이터를 살펴보면 30분 단위로 혼잡도를 나타내고 있습니다. 정각을 기준으로 30분 간격인데 실제 지하철 이용 시간이 35분이기 때문에 복잡한 경우의 수 없이 분석이 가능할 것으로 생각됩니다. 우선, 집에서 학교로 가는 경로에 해당하는 2호선(내선)과 1호선(상선) 데이터를 결합하도록 하겠습니다. weekday_toschool과 weekend_toschool 각각은 주중과 주말 학교로 가는 노선을 나타낸다고 할 수 있습니다. 

# 집에서 학교로 이동하는 경우를 분석하기 위해 2호선(내선)과 1호선(상선) 데이터를 결합하겠습니다. 
weekday_toschool = df_wd_up_1.join(df_wd_in_2).T
weekend_toschool = df_we_up_1.join(df_we_in_2).T

데이터 조작이 용이하도록 인덱스를 리셋하였습니다.

# 데이터프레임 조작이 용이하도록 인덱스 리셋 
weekday_toschool = weekday_toschool.reset_index(drop=True)
weekend_toschool = weekend_toschool.reset_index(drop=True)

1호선과 2호선의 혼잡도를 더하여 혼잡도 합계(Total) 행을 추가하고 혼잡도 합계의 평균을 mean 변수에 저장하도록 하겠습니다. 그리고 평균을 통해 각 시간대별 혼잡도 합계의 편차를 구하여 혼잡도 편차(Deviate) 행을 추가하겠습니다. 

# 시간대별 평균 혼잡도, 혼잡도 편차 집계
weekday_toschool.loc['Total', :] = weekday_toschool.sum()
mean = weekday_toschool.loc['Total', :].mean()
print('주중 혼잡도 평균: ', mean)
weekday_toschool.loc['Deviate', :] = weekday_toschool.loc['Total', :] - mean

# 시간대별 평균 혼잡도, 혼잡도 편차 집계
weekend_toschool.loc['Total', :] = weekend_toschool.sum()
mean = weekend_toschool.loc['Total', :].mean()
print('주말 혼잡도 평균: ', mean)
weekend_toschool.loc['Deviate', :] = weekend_toschool.loc['Total', :] - mean

위에서 구한 편차(Deviate) 행을 가지고 시각적으로 비교가 가능하도록 편차 그래프를 그려보겠습니다. 지하철 이용 시간대를 결정하기 위해서는 시간 제약 조건을 고려해야 하기 때문에 주중 혼잡도 편차 그래프에 학교 수업 시간대를 점선으로 추가하였습니다.

## 주중 집 -> 학교 지하철 혼잡도 편차 그래프 ##
a = weekday_toschool.loc['Deviate', :].plot.bar(figsize = (20, 5), rot = 60, color='#ff7f0e') # 편차 행에 대한 바그래프
a.set_title('주중 혼잡도 편차 (집 -> 학교)', fontweight = 'semibold')
a.set_xlabel('시간')
a.set_ylabel('혼잡도 편차')

axis_range = a.axis() # 축 정보
a.axvline(19, axis_range[2], axis_range[3], color='red', linestyle='--', linewidth=2)
a.axvline(19, axis_range[2], axis_range[3], color='red', linestyle='--', linewidth=2)
a.axvline(26, axis_range[2], axis_range[3], color='red', linestyle='--', linewidth=2)
a.text(21, axis_range[3] - 10, '수업 시간대', fontweight = 'semibold')

plt.show()
## 주말 집 -> 학교 지하철 혼잡도 편차 그래프 ##
a = weekend_toschool.loc['Deviate', :].plot.bar(figsize = (20, 5), rot = 60, color='#ff7f0e') # 편차 행에 대한 바그래프
a.set_title('주말 혼잡도 편차 (집 -> 학교)', fontweight = 'semibold')
a.set_xlabel('시간')
a.set_ylabel('혼잡도 편차')

plt.show()

주중, 주말 혼잡도 편차 그래프를 그려보면 아래와 같이 나타나게 됩니다. 

3.1.2 학교 -> 집

학교에서 집으로 가는 경우도 위와 유사하기 때문에 추가적인 설명 없이 지나가도록 하겠습니다. 집에서 학교로 가는 경우와 반대로 1호선(하선)과 2호선(외선)을 이용하게 되며 동일하게 약 55분 소요됩니다. 지하철을 이용하는 시간 또한 35분으로 비슷하기 때문에 전반적인 분석 과정도 앞 절과 동일하다고 할 수 있습니다.

# 학교에서 집으로 이동하는 경우를 분석하기 위해 1호선(하선)과 2호선(외선) 데이터를 결합
weekday_tohome = df_wd_down_1.join(df_wd_out_2).T
weekend_tohome = df_we_down_1.join(df_we_out_2).T

# 인덱스 리셋
weekday_tohome = weekday_tohome.reset_index(drop=True)
weekend_tohome = weekend_tohome.reset_index(drop=True)

# 시간대별 전체 혼잡도, 혼잡도 편차 집계
weekday_tohome.loc['Total', :] = weekday_tohome.sum()
mean = weekday_tohome.loc['Total', :].mean()
print('주중 혼잡도 평균: ', mean)
weekday_tohome.loc['Deviate', :] = weekday_tohome.loc['Total', :] - mean

# 시간대별 전체 혼잡도, 혼잡도 편차 집계
weekend_tohome.loc['Total', :] = weekend_tohome.sum() # Total
mean = weekend_tohome.loc['Total', :].mean()
print('주말 혼잡도 평균: ', mean)
weekend_tohome.loc['Deviate', :] = weekend_tohome.loc['Total', :] - mean
## 주말 학교 -> 집 지하철 혼잡도 편차 그래프 ##
a = weekday_tohome.loc['Deviate', :].plot.bar(figsize = (20, 5), rot = 60, color='#d62728')
a.set_title('from 학교 to 집 평일 혼잡도 편차', fontweight = 'semibold')
a.set_xlabel('시간')
a.set_ylabel('혼잡도 편차')

axis_range = a.axis() # 축 정보
a.axvline(19, axis_range[2], axis_range[3], color='red', linestyle='--', linewidth=2)
a.axvline(19, axis_range[2], axis_range[3], color='red', linestyle='--', linewidth=2)
a.axvline(26, axis_range[2], axis_range[3], color='red', linestyle='--', linewidth=2)
a.text(21, axis_range[3] - 10, '수업 시간대', fontweight = 'semibold')

plt.show()
## 주말 학교 -> 집 지하철 혼잡도 편차 그래프 ##
a = weekend_tohome.loc['Deviate', :].plot.bar(figsize = (20, 5), rot = 60, color='#d62728')
a.set_title('from 학교 to 집 주말 혼잡도 편차', fontweight = 'semibold')
a.set_xlabel('시간')
a.set_ylabel('혼잡도 편차')

plt.show()

위의 코드를 통해 아래와 같은 그래프를 그려볼 수 있습니다. 

3.2 지하철 이용 계획 수립

위에서 도출한 편차 그래프를 통해 최적의 지하철 통학 시간대를 파악해보겠습니다. 오렌지 색깔로 된 그래프는 집에서 학교로 통학하는 경우이며 아래 빨간색 그래프는 학교에서 집으로 가는 경우입니다. 

3.2.1 주중 지하철 통학 스케줄

기상 시간을 고려했을 때 최소 7시부터 집에서 출발이 가능합니다. 7시 이후부터 수업시간대인 14시 30분 전까지 가장 혼잡도가 낮은 시간대는 아침 10시 경입니다. 10시에서 10시 30분 사이에 지하철을 이용하는 것이 가장 합리적으로 보입니다. 한편, 학교에서 집으로 올 때에는 18시 30분부터 혼잡도가 급격히 하락하긴 하지만 20시 30분에서 21시 사이에 지하철을 이용하는 것이 최선으로 생각됩니다. 

3.3.2 주말 지하철 통학 스케줄

출근 시간대를 기점으로 피크를 찍고 내려오는 주중과 달리 주말에는 혼잡도가 점심 시간대까지 지속적으로 증가하는 것을 볼 수 있습니다. 따라서, 가능하다면 일찍 출발할수록 더 유리하다고 할 수 있습니다. 하지만 8시를 기점으로 혼잡도가 급격히 증가하기 때문에 8시 직전인 7시 30분 정도가 가장 최적의 시간대라고 할 수 있습니다. 반대로 학교에서 집으로 돌아올 때에는 19시 이후에 지하철을 이용하는 것이 좋으며 21시가 가장 좋은 선택이 될 것으로 보입니다. 

 

2. 문제 파악

2.1 데이터 전처리

본격적인 문제 파악에 앞서 데이터 전처리 작업을 진행하겠습니다. 먼저, 필요한 라이브러리를 불러오고 환경설정을 해줍니다.

from IPython.core.display import display, HTML
display(HTML("<style>.container {width:80% !important;}</style>"))
%matplotlib inline

import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy import stats
import warnings
warnings.filterwarnings('ignore')

mpl.rcParams['figure.figsize'] = (8,6)  #시각화 figure default 설정
mpl.rcParams['font.family'] = 'NanumGothic' #폰트 디폴트 설정
mpl.rcParams['font.size'] = 20    #폰트 사이즈 디폴트 설정
plt.rcParams['axes.unicode_minus'] = False
%config InlineBackend.figure_format='retina' # 그래프 글씨 뚜렷

수집한 csv파일을 데이터프레임으로 불러와 확인해보겠습니다. 데이터에는 총 1704개의 인스턴스와 43개의 컬럼으로 구성되어 있습니다. 전체 컬럼 중 앞의 6개 열은 '연번', '조사일자', '호선', '역번호', '역명', '구분'이며 이를 제외한 나머지는 5시 30분부터 23시 30분까지 30분 단위의 시간 간격입니다. 

# csv 파일을 불러와 데이터프레임에 저장
df = pd.read_csv('서울교통공사_지하철혼잡도정보_20211231.csv', encoding = 'cp949')

전체 컬럼 중에서 '연번', '역번호'는 필요가 없기 때문에 삭제하도록 하겠습니다. 그리고 '조사일자'에서 '토요일'과 '일요일' 값은 '평일'값과 통일성을 주기 위해 모두 '주말'로 변경하겠습니다. 

# 불필요한 '연번', '역번호' 컬럼 제거
df = df.drop(['연번', '역번호'], axis = 1)
# '조사일자'의 '토요일'과 '일요일'은 주말로 변경
df.replace('토요일', '주말', inplace = True)
df.replace('일요일', '주말', inplace = True)

2.2 1호선 혼잡도 분석

전처리 작업을 마쳤으니 본격적으로 분석을 진행 해보겠습니다. 우선, 어떤 내용을 확인하고 싶은지 구체화하여 정리해보고 이에 따라 데이터를 정리해보겠습니다. 최적의 지하철 통학 시간대를 결정하기 위해서는 시간대별 지하철 혼잡도를 파악해야 합니다. 이때, 정확한 분석을 위해서는 데이터를 범주에 따라 그룹화하여 살펴보아야 합니다.

첫 번째로 '평일'과 '주말'에 따른 그룹입니다. 사실 '요일'별로 혼잡도를 파악하는 것이 보다 정확한 분석이 되겠지만 현재 가지고 있는 데이터에서는 '평일'과 '주말' 정도로 데이터가 수집되어 있습니다. '평일'과 '주말' 지하철 이용객의 수가 각각 다를 수 있기 때문에 분리해서 분석하도록 하겠습니다.

두 번째로 '호선'('역')에 따른 그룹입니다. 원본 데이터는 1호선부터 8호선까지 모든 호선의 데이터를 포함하고 있습니다. 이 중 제가 이용하는 1호선과 2호선만 따로 분리해서 확인해야 할 필요가 있습니다. 이때 '호선' 뿐만 아니라 실재 이용하는 '역'에 대해서만 데이터를 필터링하여 살펴보겠습니다.

세 번째로 '방향'에 따른 그룹입니다. 원본 데이터를 보면 같은 역이라도 방향에 따라 '상선/하선', '외선/내선'으로 구분되어 있습니다. 집에서 학교를 갈 때와 학교에서 집으로 올 때 각각 이용하는 방향이 다르기 때문에 이 부분도 분석에서 구분되어야 할 중요한 요소일 것입니다.

먼저, 원본 데이터 중 1호선에 대한 데이터를 따로 분리하여 df_1에 저장하겠습니다. 이때 이용하는 역을 필터링하여 필요한 역만 추출하겠습니다.

# 1호선 역명 확인
df[df['호선'] == 1]['역명'].unique()

# 2호선 역명 확인
df[df['호선'] == 2]['역명'].unique()
# 전체 역 중 실제 이용하는 역을 리스트화
station = ['이대', '아현', '충정로', '시청', '종각', '종로3가','종로5가', '동대문', '동묘앞', '신설동', '제기동', '청량리', '회기']

# 1호선 중 실제 이용하는 역만 필터링하여 새로운 데이터프레임 생성
df_1 = df[(df['호선'] == 1) & (df['역명'].isin(station))]

그리고 데이터를 '시간(조사일자)'과 '방향(구분)'에 따라 그룹화하도록 하겠습니다. 그러면 아래와 같이 '주말/평일' 그리고 '상선/하선'에 따라 데이터가 그룹화되어 정리될 것입니다. 

# 요일(조사일자), 방향(구분)을 기준으로 그룹화
df_1 = df_1.groupby(['조사일자', '구분']).mean()

공간 상 일부 컬럼이 생략되었습니다

그룹화 한 데이터를 각 행별로 분리해서 따로 저장하겠습니다. 나중에 통학 시간대를 분석하기 위한 단계에서 조금 더 작업이 수월해질 수 있기 때문입니다. 그러면 아래와 같이 'df_wd_up_1(주중-상선-1)'과 같은 데이터 프레임이 총 4개가 생성될 것입니다.

(왜 그랬는지 모르겠지만 이때부터 '평일'이라는 단어를 '주중'이라고 표현하였습니다. 아마 '주말'과 통일성 있는 단어를 사용하려다 보니 바뀌게 된 것 같은데 이때 데이터에 있는 '평일'이라는 용어를 바꿨어야 했는데 그러지 못했습니다. 제 불찰이지만 너그러이 이해해주시면 감사하겠습니다.)

df_wd_up_1 = pd.DataFrame(df_1.iloc[2,:]) #주중 1호선 상선 혼잡도 
df_wd_down_1 = pd.DataFrame(df_1.iloc[3,:]) #주중 1호선 하선 혼잡도 

df_we_up_1 = pd.DataFrame(df_1.iloc[0,:]) #주말 1호선 상선 혼잡도 
df_we_down_1 = pd.DataFrame(df_1.iloc[1,:])#주말 1호선 하선 혼잡도
df_we_down_1.T.iloc[:,:16]

2.3 1호선 혼잡도 시각화

앞서 그룹화한 데이터를 살펴보게 되면 1) 조건(날짜, 방향)에 따라 혼잡도 차이가 있는 것으로 보이고 또한 2)시간대별로도 혼잡도 차이가 있는 것으로 파악됩니다. 하지만 너무 많은 수치가 있고 수치간 scale차이가 크지가 않다보니 명확하게 차이가 있다고 말하기가 어렵다고 느껴집니다. 이번에는 위의 표 데이터들을 시각적으로 확인할 수 있도록 그래프로 그려보겠습니다. '주중'과 '주말'에 대한 데이터 각각에 대해 막대 그래프를 그려보았고 방향을 label로 하여 막대를 2개씩 표현하였습니다. 말로 설명하기 어려운데 아래 그래프를 직접 보면 이해가 수월할 것입니다. 

### 주중/주말 1호선 평균 혼잡도 그래프 ###
plt.figure(figsize = (20, 15)) #subplot 2개를 그리기 위한 크기 지정

## 주중 1호선 평균 혼잡도 그래프 ##

index = np.arange(len(df_wd_up_1.index)) #가로축 index 개수만큼 array 생성
x1 = df_wd_up_1.index 
ya = df_wd_up_1.values.flatten() # 데이터프레임의 2차원 value값을 1차원 배열로 변환
yb = df_wd_down_1.values.flatten() # 데이터프레임의 2차원 value값을 1차원 배열로 변환
mean1 = ((np.mean(df_wd_up_1.values) + np.mean(df_wd_down_1.values)) / 2).round(1) # 주중 상선/하선 혼잡도 평균

plt.subplot(2,1,1) # 1X2 subplot의 첫번째 그래프
p1 = plt.bar(index - 0.2, ya, color = '#1f77b4', alpha = 0.8, width = 0.4) # 상선에 대한 바그래프
p2 = plt.bar(index + 0.2, yb, color = '#1f77b4', alpha = 0.4, width = 0.4) # 하선에 대한 바그래프

plt.legend((p1[0], p2[0]), ('상선', '하선'), loc = 'upper right', fontsize=20) # 범례 지정

plt.xticks(index, x1, rotation = 60) #xticks 레이블 60도 회전
plt.title('주중 1호선 평균 혼잡도')
plt.xlabel('시간')
plt.ylabel('혼잡도(%)')
plt.ylim(0, 60) # y축 범위 지정

plt.axhline(34, color='grey', linestyle=':', linewidth=2) # 만석 기준점(34%) 수평선 표시
plt.text(-2, 35, '만석 기준점 = 34.0%') # 만석 기준점(34%) 레이블 표시

plt.axhline(mean1, color='#d62728', linestyle='--', linewidth=2) # 혼잡도 평균 수평선 표시
plt.text(-2, mean1 + 1, f'평균 = {mean1}%') # 혼잡도 평균 레이블 표시

## 주말 1호선 평균 혼잡도 그래프 ##

index = np.arange(len(df_we_up_1.index)) #가로축 index 개수만큼 array 생성
x1 = df_we_up_1.index
ya = df_we_up_1.values.flatten() # 데이터프레임의 2차원 value값을 1차원 배열로 변환
yb = df_we_down_1.values.flatten() # 데이터프레임의 2차원 value값을 1차원 배열로 변환
mean2 = ((np.mean(df_we_up_1.values) + np.mean(df_we_down_1.values)) / 2).round(1) # 주중 상선/하선 혼잡도 평균

plt.subplot(2,1,2) # 1X2 subplot의 두번째 그래프
# plt.bar(x2, y2, color = '#2ca02c')
p1 = plt.bar(index - 0.2, ya, color = '#1f77b4', alpha = 0.8, width = 0.4) # 상선에 대한 바그래프
p2 = plt.bar(index + 0.2, yb, color = '#1f77b4', alpha = 0.4, width = 0.4) # 하선에 대한 바그래프

plt.legend((p1[0], p2[0]), ('상선', '하선'), loc = 'upper right', fontsize=20) # 범례 지정

plt.xticks(index, x1, rotation = 60) #xticks 레이블 60도 회전
plt.title('주말 1호선 평균 혼잡도')
plt.xlabel('시간')
plt.ylabel('혼잡도(%)')
plt.ylim(0, 60) # y축 범위 지정

plt.axhline(34, color='grey', linestyle=':', linewidth=2) # 만석 기준점(34%) 수평선 표시
plt.text(-2, 35, '만석 기준점 = 34.0%') # 만석 기준점(34%) 레이블 표시

plt.axhline(mean2, color='#d62728', linestyle='--', linewidth=2) # 혼잡도 평균 수평선 표시
plt.text(-2, mean2 + 1, f'평균 = {mean2}%') # 혼잡도 평균 레이블 표시

plt.tight_layout()
plt.show()

2.4 1호선 혼잡도 분석 결과

1호선 혼잡도 그래프를 살펴보면 다음과 같은 사실을 확인할 수 있습니다.

1) '주중'과 '주말' 지하철 혼잡도 정도(양)의 차이가 존재하는 것으로 보입니다. '평균 혼잡도'에 있어서 '주중', '주말' 각각 21.1%, 17.5%로 3.6%p 차이가 있습니다. 또한, 만석 기준점인 34.0%를 기준으로 했을 때 '주중'에는 기준점을 상회하는 시간대가 다수(하루 전체 중 총 2시간 정도)있지만 '주말'에는 모든 시간대에서 기준점 아래인 것을 확인할 수 있습니다. 따라서, '주말'보다 '주중' 지하철 이용 승객이 평균적으로 더 많은 편이고 그에 따라 지하철 내부가 훨씬 더 혼잡하다고 정리할 수 있습니다.

2) '주중'과 '주말' 지하철 혼잡도 분포의 차이가 있다는 것을 확인할 수 있습니다. '주중'에는 주로 출근 시간대(7시 30분 ~ 9시)와 퇴근 시간대(17시 ~ 18시 30분)에 혼잡도가 높은 편이며 반대로 '주말'에는 점심과 저녁 사이(12시 ~ 18시) 사이에 혼잡도가 높은 편입니다. 이는 '주중'과 '주말' 사람들의 생활 양식에 차이가 있기 때문에 생기는 차이라고 생각됩니다.

3) '주중'과 '주말' 각각 '방향'에 따라 지하철 혼잡도의 차이가 있는 것으로 보입니다. 먼저 '주중' 그래프 살펴보면 아침 시간대에 '하선'이 '상선'보다 혼잡도가 더 높고 반대로 저녁 시간대에는 '상선'이 '하선'보다 혼잡도가 더 높습니다. 이 점은 그래프를 그려보기 전에는 파악하지 못했던 점인데 굉장히 흥미로운 인사이트라고 여겨집니다. 그래프의 중요성을 깨달을 수 있는 기회였고 비록 이번 분석에서는 이 부분에 대해 확장해서 다루지는 않았지만 추후에 기회가 되면 '방향'에 따라 혼잡도 차이가 발생하는 이유에 대해 분석해보도록 하겠습니다.

2.5 2호선 혼잡도 분석

2호선에 대한 분석은 1호선과 동일하기 때문에 설명은 생략하도록 하겠습니다.

station = ['이대', '아현', '충정로', '시청', '종각', '종로3가','종로5가', '동대문', '동묘앞', '신설동', '제기동', '청량리', '회기']

# 2호선 중 실제 이용하는 역만 필터링하여 새로운 데이터프레임 생성
df_2 = df[(df['호선'] == 2) & (df['역명'].isin(station))]

# 요일(조사일자), 방향(구분), 2호선을 기준으로 그룹화
df_2 = df_2.groupby(['조사일자', '구분', '호선']).mean()

df_wd_in_2 = pd.DataFrame(df_2.iloc[2,:])
df_wd_out_2 = pd.DataFrame(df_2.iloc[3,:])

df_we_in_2 = pd.DataFrame(df_2.iloc[0,:])
df_we_out_2 = pd.DataFrame(df_2.iloc[1,:])

2.6 2호선 혼잡도 시각화

2호선에 대한 시각화 또한 1호선과 동일하기 때문에 설명은 생략하도록 하겠습니다.

### 주중/주말 2호선 평균 혼잡도 그래프 ###
plt.figure(figsize = (20, 15)) #subplot 2개를 그리기 위한 크기 지정

## 주중 2호선 평균 혼잡도 그래프 ##

index = np.arange(len(df_wd_up_1.index)) #가로축 index 개수만큼 array 생성
x1 = df_wd_in_2.index 
ya = df_wd_in_2.values.flatten() # 데이터프레임의 2차원 value값을 1차원 배열로 변환
yb = df_wd_out_2.values.flatten() # 데이터프레임의 2차원 value값을 1차원 배열로 변환
mean1 = ((np.mean(df_wd_in_2.values) + np.mean(df_wd_out_2.values)) / 2).round(1) # 주중 상선/하선 혼잡도 평균

plt.subplot(2,1,1) # 1X2 subplot의 첫번째 그래프
p1 = plt.bar(index - 0.2, ya, color = '#2ca02c', alpha = 0.8, width = 0.4) # 상선에 대한 바그래프
p2 = plt.bar(index + 0.2, yb, color = '#2ca02c', alpha = 0.4, width = 0.4) # 하선에 대한 바그래프

plt.legend((p1[0], p2[0]), ('내선', '외선'), loc = 'upper right', fontsize=20) # 범례 지정

plt.xticks(index, x1, rotation = 60) #xticks 레이블 60도 회전
plt.title('주중 2호선 평균 혼잡도', fontweight = 'semibold')
plt.xlabel('시간')
plt.ylabel('혼잡도(%)')
plt.ylim(0, 90) # y축 범위 지정

plt.axhline(34, color='grey', linestyle=':', linewidth=2) # 만석 기준점(34%) 수평선 표시
plt.text(-2, 35, '만석 기준점 = 34.0%') # 만석 기준점(34%) 레이블 표시

plt.axhline(mean1, color='#d62728', linestyle='--', linewidth=2) # 혼잡도 평균 수평선 표시
plt.text(-2, mean1 - 3, f'평균 = {mean1}%') # 혼잡도 평균 레이블 표시


## 주말 2호선 평균 혼잡도 그래프 ##

index = np.arange(len(df_we_up_1.index)) #가로축 index 개수만큼 array 생성
x1 = df_we_in_2.index
ya = df_we_in_2.values.flatten() # 데이터프레임의 2차원 value값을 1차원 배열로 변환
yb = df_we_out_2.values.flatten() # 데이터프레임의 2차원 value값을 1차원 배열로 변환
mean2 = ((np.mean(df_we_in_2.values) + np.mean(df_we_out_2.values)) / 2).round(1) # 주중 상선/하선 혼잡도 평균

plt.subplot(2,1,2) # 1X2 subplot의 두번째 그래프
# plt.bar(x2, y2, color = '#2ca02c')
p1 = plt.bar(index - 0.2, ya, color = '#2ca02c', alpha = 0.8, width = 0.4) # 상선에 대한 바그래프
p2 = plt.bar(index + 0.2, yb, color = '#2ca02c', alpha = 0.4, width = 0.4) # 하선에 대한 바그래프

plt.legend((p1[0], p2[0]), ('내선', '외선'), loc = 'upper right', fontsize=20) # 범례 지정

plt.xticks(index, x1, rotation = 60) #xticks 레이블 60도 회전
plt.title('주말 2호선 평균 혼잡도', fontweight = 'semibold')
plt.xlabel('시간')
plt.ylabel('혼잡도(%)')
plt.ylim(0, 70) # y축 범위 지정

plt.axhline(34, color='grey', linestyle=':', linewidth=2) # 만석 기준점(34%) 수평선 표시
plt.text(-2, 35, '만석 기준점 = 34.0%') # 만석 기준점(34%) 레이블 표시

plt.axhline(mean2, color='#d62728', linestyle='--', linewidth=2) # 혼잡도 평균 수평선 표시
plt.text(-2, mean2 + 1, f'평균 = {mean2}%') # 혼잡도 평균 레이블 표시

plt.tight_layout()
plt.show()

2.7 2호선 혼잡도 분석 결과

2호선 혼잡도 그래프를 살펴보면 다음과 같은 사실을 확인할 수 있습니다.

1) 1호선 보다 2호선의 혼잡도가 높은 편입니다. 평균 혼잡도를 비교했을 때 1호선은 21.1%, 17.5%인데 반해 2호선은 30.8%, 24.3%로 각각 9.7%p, 6.8%p 더 높습니다. 두 노선의 열차 칸 수와 수용량이 동일하기 때문에 혼잡도가 높다는 것은 그만큼 하루 평균 더 많은 승객이 탑승했다고 해석할 수 있습니다. 특히, 주중 2호선 8시 시간대에 혼잡도가 90% 가까이 육박하는 것을 볼 수 있는데 이는 지하철 수용량 인원인 160명에 가까운 인원이 타고 있다는 것을 의미합니다. 1호선의 평균 혼잡도의 최대치가 60%가 안 된다는 점과 비교했을 때 지하철 2호선의 출근 시간은 정말 혼잡하다라고 할 수 있습니다. 물론 지금의 분석은 2호선 전체 중 일부 구간(이대~시청)이기 때문에 전체를 일반화할 수는 없습니다. 다만, 제 경우에 이 시간대만큼은 가능한 피하는 것이 원활한 통학에 유리할 것으로 보입니다.

2) '주중'과 '주말' 지하철 혼잡도 분포의 차이가 있다는 것을 확인할 수 있습니다. '주중'이 '주말' 보다 혼잡도가 더 높은데 이는 1호선에서도 볼 수 있는 패턴이었습니다. 다만, 1호선과 비교했을 때 2호선의 경우 '주말' 저녁 시간대(18시~23시)에도 혼잡도가 높은 편이고 낮시간대에서 그다지 떨어지지 않는 양상을 보이고 있습니다. 이는 주말 저녁에 혼잡도가 급격히 떨어지는 1호선과는 다른 모습이라고 할 수 있습니다.  정확한 분석을 위해서는 하락율을 계산해서 비교해 보아야 겠지만 육안 상으로도 혼잡도가 계속 유지되고 있는 것을 확인할 수 있습니다. 2호선의 경우 저녁 시간대에도 혼잡도가 높은 것은 강남, 신촌 등 서울의 주요 유흥 지역들이 2호선에 위치해 있기 때문이라고 생각됩니다. 기회가 된다면 이 부분에 대해서도 추후에 다루어 보도록 하겠습니다.

1. 문제 정의 

1.1 프로젝트 배경

2학기부터 모든 수업이 대면으로 전환되면서 원치 않게 통학을 시작하게 되었습니다. 오랜 시간 기숙사 생활을 하다 코로나 기간에 맞추어 자취를 시작하게 된터라 먼거리를 통학하는 것이 아직 익숙하지는 않은 편입니다. 그간 친구를 통해서만 전해 듣던 통학의 매운 맛을 직접 맛보게 되니 전국의 모든 통학러들과 직장인들이 새삼 대단하다 느껴졌습니다.

통학을 시작한지 2달이 되어가고 있는 시점에서 이제는 그만 하고 싶은 마음이 밀려오고 있습니다. 매일 아침 만원인 지하철 안에서 1시간을 보내다 보면 종종 현타가 오고 하루가 금방 피로해지곤 합니다. 사실 통학 자체가 어렵고 힘든 것은 아닙니다. 이동하는 시간과 거리 보다는 이동할 때의 상황이 문제라고 보입니다. 널널하게 앉아서 가게 되면 그리 힘들다고 느껴지지 않는데 같은 거리를 사람들 사이에 끼어 타게 되면 체감되는 시간도 길어지고 피로도도 급격히 증가합니다.

통학을 아예 안 하는 것은 현실적으로 불가능합니다. 학교를 다녀야 하는 입장에서 통학은 불가피한 상황이고 다만 가능한 편하게 통학할 수 있는 방법을 고려해볼 수는 있을 것입니다. 지하철을 이용해 통학을 하기 때문에 지하철이 혼잡하지 않은 시간대에 맞추어 이동하면 될 것 같다는 생각이 들었고 이러한 이유로 다음과 같은 프로젝트 아닌 프로젝트를 진행하게 되었습니다. 

1.2 문제 정의

문제를 정의하기 전 다음과 같은 질문을 해볼 수 있습니다. 가능한 혼잡하지 않은 시간대에 지하철을 이용할 수 있을까? 이 질문에 대해 상식적인 선에서 우리는 출퇴근 시간을 피하라는 답변을 할 수 있을 것입니다. 누구나 예상할 수 있는 해결책입니다. 하지만 이는 우리의 경험에서 비롯된 직관적인 답변에 불과합니다. 사실일 가능성이 매우 높지만 정확한 검증이 필요합니다.

그리고 단순히 피해야 하는 시간대를 알아내는 것에 그치는 것이 아닌 가장 혼잡하지 않은 시간대까지 알아내는 것이 필요합니다. 또한, 우리는 이동 가능한 시간대에 제약이 있습니다. 가령, 그 제약에는 이동이 불가능한 수업 시간대가 있을 것입니다. 따라서, 최적의 통학 시간대를 파악하여 스케줄을 관리하는 것이 목표가 되며 이를 위해 해결해야 하는 문제는 지하철이 가장 여유로운 시간대가 언제인지 파악하는 것입니다. 이때, '최적의 통학 시간대'에서의 '최적'의 의미는 제약조건 하에서 혼잡도가 최저인 경우라고 정의할 수 있을 것입니다.

- 목표 : 가장 최적화된 지하철 통학 스케줄 관리

- 문제 : 지하철이 가장 여유로운 시간대 파악 (혼잡도가 최저인 경우)

- 분석 지표 : 혼잡도

1.3 제약 조건 확인

시간 제약 조건 : 현재 수업이 있는 화, 수, 목을 포함해 주 4일~5일 학교로 통학하고 있습니다. 주말에는 수업이 없지만 종종 스터디를 위해 학교에 가고 있습니다. 화요일은 오후 4시 ~ 6시, 수요일은 오후 3시 ~ 6시, 목요일은 오전 9시 ~ 오후 6시 수업이 있으며 나머지 요일은 자율적으로 시간 활용이 가능합니다.

경로 제약 조건 : 학교에 가기 위해서는 2가지 방법(지하철만 이용 시)이 있습니다. 방법 1은 이대역에서 시청역까지 2호선, 시청역에서 회기역까지 1호선을 이용하는 것입니다. 방법 2는 이대역에서 왕십리역까지 1호선, 왕십리역에서 회기역까지 경의중앙선을 이용하는 것입니다. 둘 다 네이버 지도 기준 소요되는 시간은 약 55분으로 동일합니다. 그래서 두 가지 경우의 수를 고려할 수 있지만 이번 분석에서는 방법1의 경우만 고려하겠습니다. 왜냐하면 경의중앙선은 배차간격이 길고 불규칙하다는 변수가 있기 때문에 최적의 통학 스케줄에는 적절하지 않기 때문입니다. 

1.4 데이터 수집

문제에 적합한 데이터를 수집하는 것은 데이터 분석에 있어 매우 중요한 과정입니다. ('적합하다'는 것은 여러 의미가 있을 것입니다. 이에 대해서는 나중에 시간 내어 다루어 보도록 하겠습니다.) 수집된 데이터에 따라 엉뚱한 결과가 나오기도 하고 결과가 달라질 수도 있기 때문입니다. 원하는 정보를 얻기 위해서는 문제를 표현할 수 있는 내용을 담은 데이터가 필요하며 이 과정에서 수 개의 데이터를 수집하여 이를 통합하는 과정이 요구되기도 합니다.

하지만 정말 운이 좋게도 이번 분석에 필요한 데이터를 단번에 구할 수 있었습니다. 서울교통공사에서 제공하는 '지하철 혼잡도 정보'인데 정확히 제가 원하는 분석 지표를 포함한 데이터입니다. 만약 이 데이터가 없었더라면 '시간대별 지하철 이용객' 이나 '호선별 지하철 수용량' 등의 데이터를 구해 분석 지표를 집계하여 새로운 데이터를 만들어야 했을 것입니다. 물론, 수집해야 할 데이터 수가 많아짐에 따라 필요한 데이터를 구하지 못하는 경우도 발생했을 것입니다. 여하튼, 필요한 데이터를 수집했으니 데이터에 대한 설명을 확인해보겠습니다.

데이터 설명 : 서울교통공사 1-8호선 30분 단위 평균 혼잡도로 30분간 지나는 열차들의 평균 혼잡도(정원대비 승차인원으로, 승차인과 좌석수가 일치할 경우를 혼잡도 34%로 산정) 입니다.(단위: %). 서울교통공사 혼잡도 데이터는 조사일자(평일, 토요일, 일요일), 호선, 역번호, 역명, 상하선구분, 30분단위 별 혼잡도 데이터로 구성되어 있습니다. (2년 단위 업데이트 자료 입니다.)

혼잡도 추가 설명 : 지하철 1칸의 정원은 160명으로 이를 기준 삼아 혼잡도(%)를 산정하게 됩니다. 즉, 지하철 1칸에 160명이 차게 되면 혼잡도 100%입니다. 한편, 지하철 1칸에는 7인용 좌석 6개, 노약자석 3인용 4개가 있어 총 54개의 좌석이 있습니다. 따라서, 모든 좌석에 한 사람씩 앉아 54명이 찼을 경우 34%의 혼잡도가 나오게 됩니다.

+ Recent posts