2023.07.21
교통카드(선후불교통카드 및 1회용 교통카드)를 이용한 지하철 호선별 역별(서울교통공사, 한국철도공사, 공항철도, 9호선) 승하차인원을 나타내는 정보입니다. (일단위)
data.seoul.go.kr/dataList/OA-12914/S/1/datasetView.do
서울교통공사에서 제공하는 관할역명으로 지하철역 검색하는 정보에 관한 API 입니다. 해당 정보는 전철역코드, 전철역명, 호선 등을 제공합니다.
data.seoul.go.kr/dataList/OA-121/S/1/datasetView.do
... 지하철 역별 승하차 인원 정보를 분석하고 싶은데 승하차 인원 정보만으로는 역 순서를 알 수가 없음. 따라서 지하철 역 정보를 활용하여 전철역코드를 맵핑하여 분석할 예정.
import pandas as pd
import pandas_profiling
from scipy.stats import ttest_1samp
import matplotlib as mpl
from matplotlib import font_manager, rc
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.family'] = 'UnDotum'
plt.rcParams['font.size'] = 13
subway_congestion = pd.read_csv('data/서울시 지하철호선별 역별 승하차 인원 정보.csv', encoding='cp949')
subway_station = pd.read_csv('data/서울교통공사 노선별 지하철역 정보.csv', encoding='cp949')
subway_congestion
사용일자 | 호선명 | 역명 | 승차총승객수 | 하차총승객수 | 등록일자 | |
---|---|---|---|---|---|---|
0 | 20230715 | 우이신설선 | 4.19민주묘지 | 2341 | 2133 | 20230718 |
1 | 20230715 | 경원선 | 가능 | 5015 | 4756 | 20230718 |
2 | 20230715 | 3호선 | 가락시장 | 6230 | 5775 | 20230718 |
3 | 20230715 | 8호선 | 가락시장 | 5445 | 5562 | 20230718 |
4 | 20230715 | 7호선 | 가산디지털단지 | 13892 | 12723 | 20230718 |
... | ... | ... | ... | ... | ... | ... |
23108 | 20230607 | 경원선 | 회룡 | 14770 | 14560 | 20230610 |
23109 | 20230607 | 4호선 | 회현(남대문시장) | 29123 | 30778 | 20230610 |
23110 | 20230607 | 6호선 | 효창공원앞 | 8705 | 8129 | 20230610 |
23111 | 20230607 | 경의선 | 효창공원앞 | 3958 | 4225 | 20230610 |
23112 | 20230607 | 9호선 | 흑석(중앙대입구) | 11418 | 11731 | 20230610 |
23113 rows × 6 columns
pr = subway_congestion.profile_report()
pr
HBox(children=(HTML(value='Summarize dataset'), FloatProgress(value=0.0, max=5.0), HTML(value='')))
HBox(children=(HTML(value='Generate report structure'), FloatProgress(value=0.0, max=1.0), HTML(value='')))
HBox(children=(HTML(value='Render HTML'), FloatProgress(value=0.0, max=1.0), HTML(value='')))
모든 컬럼에 결측치가 없다.
subway_station
전철역코드 | 전철역명 | 전철명명(영문) | 호선 | 외부코드 | 전철명명(중문) | 전철명명(일문) | |
---|---|---|---|---|---|---|---|
0 | 0244 | 용답 | Yongdap | 02호선 | 211-1 | 龍踏 | 龍踏 |
1 | 0245 | 신답 | Sindap | 02호선 | 211-2 | 新踏 | 新踏 |
2 | 0250 | 용두 | Yongdu | 02호선 | 211-3 | 龍頭 | 龍頭 |
3 | 0336 | 학여울 | Hangnyeoul | 03호선 | 346 | Hangnyeoul | ハンニョウル |
4 | 0428 | 삼각지 | Samgakji | 04호선 | 428 | 三角地 | 三角地 |
... | ... | ... | ... | ... | ... | ... | ... |
768 | 0159 | 동묘앞 | Dongmyo | 01호선 | 127 | 東廟 | 東廟前 |
769 | 0200 | 까치산 | Kkachisan | 02호선 | 234-4 | 喜鵲山 | カチ山 |
770 | 0201 | 시청 | City Hall | 02호선 | 201 | 市廳 | 市廳 |
771 | 0202 | 을지로입구 | Euljiro 1(il)-ga | 02호선 | 202 | 乙支路入口 | 乙支路入口 |
772 | 0300 | 대곡 | Daegok | 경의선 | K322 | 大谷 | 大谷 |
773 rows × 7 columns
pr = subway_station.profile_report()
pr
HBox(children=(HTML(value='Summarize dataset'), FloatProgress(value=0.0, max=5.0), HTML(value='')))
HBox(children=(HTML(value='Generate report structure'), FloatProgress(value=0.0, max=1.0), HTML(value='')))
HBox(children=(HTML(value='Render HTML'), FloatProgress(value=0.0, max=1.0), HTML(value='')))
우리에게 필요한 전철역코드, 전철역명, 호선에만 결측치가 없으면 된다.
# 등록일자 컬럼 삭제
subway_congestion = subway_congestion.drop('등록일자', axis=1)
subway_congestion
사용일자 | 호선명 | 역명 | 승차총승객수 | 하차총승객수 | |
---|---|---|---|---|---|
0 | 20230715 | 우이신설선 | 4.19민주묘지 | 2341 | 2133 |
1 | 20230715 | 경원선 | 가능 | 5015 | 4756 |
2 | 20230715 | 3호선 | 가락시장 | 6230 | 5775 |
3 | 20230715 | 8호선 | 가락시장 | 5445 | 5562 |
4 | 20230715 | 7호선 | 가산디지털단지 | 13892 | 12723 |
... | ... | ... | ... | ... | ... |
23108 | 20230607 | 경원선 | 회룡 | 14770 | 14560 |
23109 | 20230607 | 4호선 | 회현(남대문시장) | 29123 | 30778 |
23110 | 20230607 | 6호선 | 효창공원앞 | 8705 | 8129 |
23111 | 20230607 | 경의선 | 효창공원앞 | 3958 | 4225 |
23112 | 20230607 | 9호선 | 흑석(중앙대입구) | 11418 | 11731 |
23113 rows × 5 columns
subway_station = subway_station[['전철역코드','전철역명']]
subway_station
전철역코드 | 전철역명 | |
---|---|---|
0 | 0244 | 용답 |
1 | 0245 | 신답 |
2 | 0250 | 용두 |
3 | 0336 | 학여울 |
4 | 0428 | 삼각지 |
... | ... | ... |
768 | 0159 | 동묘앞 |
769 | 0200 | 까치산 |
770 | 0201 | 시청 |
771 | 0202 | 을지로입구 |
772 | 0300 | 대곡 |
773 rows × 2 columns
subway_station = subway_station.rename(columns={'전철역명': '역명'})
df = pd.merge(subway_congestion, subway_station, on='역명', how='left')
df
사용일자 | 호선명 | 역명 | 승차총승객수 | 하차총승객수 | 전철역코드 | |
---|---|---|---|---|---|---|
0 | 20230715 | 우이신설선 | 4.19민주묘지 | 2341 | 2133 | NaN |
1 | 20230715 | 경원선 | 가능 | 5015 | 4756 | 1907 |
2 | 20230715 | 3호선 | 가락시장 | 6230 | 5775 | 0340 |
3 | 20230715 | 3호선 | 가락시장 | 6230 | 5775 | 2818 |
4 | 20230715 | 8호선 | 가락시장 | 5445 | 5562 | 0340 |
... | ... | ... | ... | ... | ... | ... |
30924 | 20230607 | 6호선 | 효창공원앞 | 8705 | 8129 | 1261 |
30925 | 20230607 | 6호선 | 효창공원앞 | 8705 | 8129 | 2628 |
30926 | 20230607 | 경의선 | 효창공원앞 | 3958 | 4225 | 1261 |
30927 | 20230607 | 경의선 | 효창공원앞 | 3958 | 4225 | 2628 |
30928 | 20230607 | 9호선 | 흑석(중앙대입구) | 11418 | 11731 | NaN |
30929 rows × 6 columns
우이신설선 같은 경우 전철역코드가 없어도 상관없다. 그런데 흑석(중앙대입구)는 9호선인데 왜 없을까?
subway_station[subway_station['역명'].str.contains('흑석')]
전철역코드 | 역명 | |
---|---|---|
635 | 4119 | 흑석 |
두 테이블 간 역명이 다른 게 있음을 발견했다.
df.isnull().sum()
사용일자 0 호선명 0 역명 0 승차총승객수 0 하차총승객수 0 전철역코드 3002 dtype: int64
전철역코드에 문제가 있다.
df[df.isnull().any(axis=1)]
사용일자 | 호선명 | 역명 | 승차총승객수 | 하차총승객수 | 전철역코드 | |
---|---|---|---|---|---|---|
0 | 20230715 | 우이신설선 | 4.19민주묘지 | 2341 | 2133 | NaN |
26 | 20230715 | 2호선 | 강변(동서울터미널) | 26375 | 26395 | NaN |
43 | 20230715 | 3호선 | 경복궁(정부서울청사) | 20705 | 19318 | NaN |
48 | 20230715 | 6호선 | 고려대(종암) | 5401 | 5021 | NaN |
79 | 20230715 | 7호선 | 공릉(서울과학기술대) | 8680 | 8376 | NaN |
... | ... | ... | ... | ... | ... | ... |
30882 | 20230607 | 5호선 | 하남시청(덕풍?신장) | 8533 | 8131 | NaN |
30890 | 20230607 | 4호선 | 한성대입구(삼선교) | 16124 | 15593 | NaN |
30914 | 20230607 | 6호선 | 화랑대(서울여대입구) | 14447 | 11144 | NaN |
30923 | 20230607 | 4호선 | 회현(남대문시장) | 29123 | 30778 | NaN |
30928 | 20230607 | 9호선 | 흑석(중앙대입구) | 11418 | 11731 | NaN |
3002 rows × 6 columns
대략 살펴보니 .이나 () 같은 특수문자가 들어간 경우 뭔가 맵핑이 안된 것 같다.
subway_station[subway_station['역명'].str.contains('흑석')]
전철역코드 | 역명 | |
---|---|---|
635 | 4119 | 흑석 |
subway_station[subway_station['역명'].str.contains('공릉')]
전철역코드 | 역명 | |
---|---|---|
493 | 2718 | 공릉 |
subway_station[subway_station['역명'].str.contains('하남시청')]
전철역코드 | 역명 | |
---|---|---|
450 | 2565 | 하남시청 |
subway_station[subway_station['역명'].str.contains('민주묘지')]
전철역코드 | 역명 | |
---|---|---|
743 | 4703 | 4?19민주묘지 |
subway_station에 있는 ?를 .으로 바꾸고 괄호 안의 글자는 괄호와 함께 날려버려야겠다
subway_station
전철역코드 | 역명 | |
---|---|---|
0 | 0244 | 용답 |
1 | 0245 | 신답 |
2 | 0250 | 용두 |
3 | 0336 | 학여울 |
4 | 0428 | 삼각지 |
... | ... | ... |
768 | 0159 | 동묘앞 |
769 | 0200 | 까치산 |
770 | 0201 | 시청 |
771 | 0202 | 을지로입구 |
772 | 0300 | 대곡 |
773 rows × 2 columns
# ?를 .으로 바꾸기
subway_station['역명'] = subway_station['역명'].str.replace('?', '.')
subway_station[subway_station['역명'].str.contains('민주묘지')]
전철역코드 | 역명 | |
---|---|---|
743 | 4703 | 4.19민주묘지 |
# 괄호 안 날려버리기
subway_congestion['역명'] = subway_congestion['역명'].str.replace(r'\([^)]*\)', '', regex=True)
subway_congestion
사용일자 | 호선명 | 역명 | 승차총승객수 | 하차총승객수 | |
---|---|---|---|---|---|
0 | 20230715 | 우이신설선 | 4.19민주묘지 | 2341 | 2133 |
1 | 20230715 | 경원선 | 가능 | 5015 | 4756 |
2 | 20230715 | 3호선 | 가락시장 | 6230 | 5775 |
3 | 20230715 | 8호선 | 가락시장 | 5445 | 5562 |
4 | 20230715 | 7호선 | 가산디지털단지 | 13892 | 12723 |
... | ... | ... | ... | ... | ... |
23108 | 20230607 | 경원선 | 회룡 | 14770 | 14560 |
23109 | 20230607 | 4호선 | 회현 | 29123 | 30778 |
23110 | 20230607 | 6호선 | 효창공원앞 | 8705 | 8129 |
23111 | 20230607 | 경의선 | 효창공원앞 | 3958 | 4225 |
23112 | 20230607 | 9호선 | 흑석 | 11418 | 11731 |
23113 rows × 5 columns
이제 다시 병합해보자
df = pd.merge(subway_congestion, subway_station, on='역명', how='left')
df
사용일자 | 호선명 | 역명 | 승차총승객수 | 하차총승객수 | 전철역코드 | |
---|---|---|---|---|---|---|
0 | 20230715 | 우이신설선 | 4.19민주묘지 | 2341 | 2133 | 4703 |
1 | 20230715 | 경원선 | 가능 | 5015 | 4756 | 1907 |
2 | 20230715 | 3호선 | 가락시장 | 6230 | 5775 | 0340 |
3 | 20230715 | 3호선 | 가락시장 | 6230 | 5775 | 2818 |
4 | 20230715 | 8호선 | 가락시장 | 5445 | 5562 | 0340 |
... | ... | ... | ... | ... | ... | ... |
32824 | 20230607 | 6호선 | 효창공원앞 | 8705 | 8129 | 1261 |
32825 | 20230607 | 6호선 | 효창공원앞 | 8705 | 8129 | 2628 |
32826 | 20230607 | 경의선 | 효창공원앞 | 3958 | 4225 | 1261 |
32827 | 20230607 | 경의선 | 효창공원앞 | 3958 | 4225 | 2628 |
32828 | 20230607 | 9호선 | 흑석 | 11418 | 11731 | 4119 |
32829 rows × 6 columns
df.isnull().sum()
사용일자 0 호선명 0 역명 0 승차총승객수 0 하차총승객수 0 전철역코드 0 dtype: int64
짠
df['날짜'] = pd.to_datetime(df['사용일자'], format='%Y%m%d')
df = df.drop(columns='사용일자', axis=1)
df
호선명 | 역명 | 승차총승객수 | 하차총승객수 | 전철역코드 | 날짜 | |
---|---|---|---|---|---|---|
0 | 우이신설선 | 4.19민주묘지 | 2341 | 2133 | 4703 | 2023-07-15 |
1 | 경원선 | 가능 | 5015 | 4756 | 1907 | 2023-07-15 |
2 | 3호선 | 가락시장 | 6230 | 5775 | 0340 | 2023-07-15 |
3 | 3호선 | 가락시장 | 6230 | 5775 | 2818 | 2023-07-15 |
4 | 8호선 | 가락시장 | 5445 | 5562 | 0340 | 2023-07-15 |
... | ... | ... | ... | ... | ... | ... |
32824 | 6호선 | 효창공원앞 | 8705 | 8129 | 1261 | 2023-06-07 |
32825 | 6호선 | 효창공원앞 | 8705 | 8129 | 2628 | 2023-06-07 |
32826 | 경의선 | 효창공원앞 | 3958 | 4225 | 1261 | 2023-06-07 |
32827 | 경의선 | 효창공원앞 | 3958 | 4225 | 2628 | 2023-06-07 |
32828 | 9호선 | 흑석 | 11418 | 11731 | 4119 | 2023-06-07 |
32829 rows × 6 columns
df['요일'] = df['날짜'].dt.day_name()
df
호선명 | 역명 | 승차총승객수 | 하차총승객수 | 전철역코드 | 날짜 | 요일 | |
---|---|---|---|---|---|---|---|
0 | 우이신설선 | 4.19민주묘지 | 2341 | 2133 | 4703 | 2023-07-15 | Saturday |
1 | 경원선 | 가능 | 5015 | 4756 | 1907 | 2023-07-15 | Saturday |
2 | 3호선 | 가락시장 | 6230 | 5775 | 0340 | 2023-07-15 | Saturday |
3 | 3호선 | 가락시장 | 6230 | 5775 | 2818 | 2023-07-15 | Saturday |
4 | 8호선 | 가락시장 | 5445 | 5562 | 0340 | 2023-07-15 | Saturday |
... | ... | ... | ... | ... | ... | ... | ... |
32824 | 6호선 | 효창공원앞 | 8705 | 8129 | 1261 | 2023-06-07 | Wednesday |
32825 | 6호선 | 효창공원앞 | 8705 | 8129 | 2628 | 2023-06-07 | Wednesday |
32826 | 경의선 | 효창공원앞 | 3958 | 4225 | 1261 | 2023-06-07 | Wednesday |
32827 | 경의선 | 효창공원앞 | 3958 | 4225 | 2628 | 2023-06-07 | Wednesday |
32828 | 9호선 | 흑석 | 11418 | 11731 | 4119 | 2023-06-07 | Wednesday |
32829 rows × 7 columns
# 최종 dataframe
df = df[['날짜','요일','호선명','전철역코드','역명','승차총승객수','하차총승객수']].sort_values(by='전철역코드')
df
날짜 | 요일 | 호선명 | 전철역코드 | 역명 | 승차총승객수 | 하차총승객수 | |
---|---|---|---|---|---|---|---|
27218 | 2023-06-13 | Tuesday | 경의선 | 0150 | 서울역 | 5566 | 7032 |
27222 | 2023-06-13 | Tuesday | 공항철도 1호선 | 0150 | 서울역 | 11550 | 10131 |
28071 | 2023-06-12 | Monday | 공항철도 1호선 | 0150 | 서울역 | 12439 | 11032 |
16028 | 2023-06-26 | Monday | 공항철도 1호선 | 0150 | 서울역 | 13304 | 11568 |
9969 | 2023-07-03 | Monday | 경부선 | 0150 | 서울역 | 8099 | 2581 |
... | ... | ... | ... | ... | ... | ... | ... |
17471 | 2023-06-24 | Saturday | 공항철도 1호선 | 4929 | 김포공항 | 11363 | 5609 |
26086 | 2023-06-14 | Wednesday | 공항철도 1호선 | 4929 | 김포공항 | 11790 | 6843 |
17466 | 2023-06-24 | Saturday | 9호선 | 4929 | 김포공항 | 8612 | 12637 |
30390 | 2023-06-09 | Friday | 5호선 | 4929 | 김포공항 | 8706 | 7762 |
14033 | 2023-06-28 | Wednesday | 5호선 | 4929 | 김포공항 | 8760 | 8104 |
32829 rows × 7 columns
df.호선명.unique()
array(['경의선', '공항철도 1호선', '경부선', '1호선', '4호선', '2호선', '3호선', '5호선', '우이신설선', '경원선', '6호선', '7호선', '8호선', '9호선2~3단계', '분당선', '신림선', '9호선', '일산선', '중앙선', '경춘선', '장항선', '과천선', '경강선', '안산선', '경인선', '수인선', '서해선'], dtype=object)
조사를 하면서 알게 된 사실이지만 9호선에 2, 3단계가 있었다.
2단계 및 3단계 구간은 신논현역 ↔ 중앙보훈병원역인데 9호선과 9호선2~3단계를 합치면 되겠다.
df['호선명'] = df['호선명'].replace('9호선2~3단계','9호선')
df.호선명.unique()
array(['경의선', '공항철도 1호선', '경부선', '1호선', '4호선', '2호선', '3호선', '5호선', '우이신설선', '경원선', '6호선', '7호선', '8호선', '9호선', '분당선', '신림선', '일산선', '중앙선', '경춘선', '장항선', '과천선', '경강선', '안산선', '경인선', '수인선', '서해선'], dtype=object)
df['호선명'] = df['호선명'].replace('경의선','경의중앙선')
df['호선명'] = df['호선명'].replace('중앙선','경의중앙선')
df.호선명.unique()
array(['경의중앙선', '공항철도 1호선', '경부선', '1호선', '4호선', '2호선', '3호선', '5호선', '우이신설선', '경원선', '6호선', '7호선', '8호선', '9호선', '분당선', '신림선', '일산선', '경춘선', '장항선', '과천선', '경강선', '안산선', '경인선', '수인선', '서해선'], dtype=object)
df = df.loc[df['호선명'].isin(['1호선','2호선','3호선','4호선','5호선','6호선','7호선','8호선','9호선','분당선','경의중앙선'])]
df
날짜 | 요일 | 호선명 | 전철역코드 | 역명 | 승차총승객수 | 하차총승객수 | |
---|---|---|---|---|---|---|---|
27218 | 2023-06-13 | Tuesday | 경의중앙선 | 0150 | 서울역 | 5566 | 7032 |
28067 | 2023-06-12 | Monday | 경의중앙선 | 0150 | 서울역 | 5611 | 7107 |
30652 | 2023-06-09 | Friday | 1호선 | 0150 | 서울역 | 60513 | 58360 |
9977 | 2023-07-03 | Monday | 1호선 | 0150 | 서울역 | 53435 | 51030 |
9981 | 2023-07-03 | Monday | 경의중앙선 | 0150 | 서울역 | 5001 | 6614 |
... | ... | ... | ... | ... | ... | ... | ... |
26076 | 2023-06-14 | Wednesday | 9호선 | 4929 | 김포공항 | 7728 | 11699 |
26081 | 2023-06-14 | Wednesday | 5호선 | 4929 | 김포공항 | 8397 | 7594 |
17466 | 2023-06-24 | Saturday | 9호선 | 4929 | 김포공항 | 8612 | 12637 |
30390 | 2023-06-09 | Friday | 5호선 | 4929 | 김포공항 | 8706 | 7762 |
14033 | 2023-06-28 | Wednesday | 5호선 | 4929 | 김포공항 | 8760 | 8104 |
22000 rows × 7 columns
# 승하차총승객수
df['승하차총승객수'] = df['승차총승객수'] + df['하차총승객수']
# 승하차비율
df['승하차비율'] = df['하차총승객수']/df['승차총승객수']
# 승하차차이
df['승하차차이'] = df['승차총승객수'] - df['하차총승객수']
df
날짜 | 요일 | 호선명 | 전철역코드 | 역명 | 승차총승객수 | 하차총승객수 | 승하차총승객수 | 승하차비율 | 승하차차이 | |
---|---|---|---|---|---|---|---|---|---|---|
27218 | 2023-06-13 | Tuesday | 경의중앙선 | 0150 | 서울역 | 5566 | 7032 | 12598 | 1.263385 | -1466 |
28067 | 2023-06-12 | Monday | 경의중앙선 | 0150 | 서울역 | 5611 | 7107 | 12718 | 1.266619 | -1496 |
30652 | 2023-06-09 | Friday | 1호선 | 0150 | 서울역 | 60513 | 58360 | 118873 | 0.964421 | 2153 |
9977 | 2023-07-03 | Monday | 1호선 | 0150 | 서울역 | 53435 | 51030 | 104465 | 0.954992 | 2405 |
9981 | 2023-07-03 | Monday | 경의중앙선 | 0150 | 서울역 | 5001 | 6614 | 11615 | 1.322535 | -1613 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
26076 | 2023-06-14 | Wednesday | 9호선 | 4929 | 김포공항 | 7728 | 11699 | 19427 | 1.513846 | -3971 |
26081 | 2023-06-14 | Wednesday | 5호선 | 4929 | 김포공항 | 8397 | 7594 | 15991 | 0.904371 | 803 |
17466 | 2023-06-24 | Saturday | 9호선 | 4929 | 김포공항 | 8612 | 12637 | 21249 | 1.467371 | -4025 |
30390 | 2023-06-09 | Friday | 5호선 | 4929 | 김포공항 | 8706 | 7762 | 16468 | 0.891569 | 944 |
14033 | 2023-06-28 | Wednesday | 5호선 | 4929 | 김포공항 | 8760 | 8104 | 16864 | 0.925114 | 656 |
22000 rows × 10 columns
df_pivot = pd.pivot_table(df, index = '호선명', aggfunc='mean').sort_values('승하차총승객수', ascending=False)
df_pivot
승차총승객수 | 승하차비율 | 승하차차이 | 승하차총승객수 | 하차총승객수 | |
---|---|---|---|---|---|
호선명 | |||||
2호선 | 28390.094467 | 1.019922 | -483.398448 | 57263.587382 | 28873.492915 |
1호선 | 24999.149886 | 0.972348 | 895.826087 | 49102.473684 | 24103.323799 |
4호선 | 17900.984211 | 1.089867 | -855.371053 | 36657.339474 | 18756.355263 |
3호선 | 16326.373832 | 0.958322 | 183.436137 | 32469.311526 | 16142.937695 |
7호선 | 13959.499308 | 0.969794 | 305.603965 | 27613.394652 | 13653.895343 |
9호선 | 10840.331579 | 1.037313 | -441.057895 | 22121.721053 | 11281.389474 |
분당선 | 10534.928246 | 0.998258 | -422.162301 | 21492.018793 | 10957.090547 |
5호선 | 10634.464698 | 1.000303 | 23.563222 | 21245.366175 | 10610.901476 |
8호선 | 10120.614035 | 1.011253 | -203.146930 | 20444.375000 | 10323.760965 |
6호선 | 8950.893822 | 0.959725 | 91.000000 | 17810.787645 | 8859.893822 |
경의중앙선 | 4743.158785 | inf | 107.070358 | 9379.247213 | 4636.088428 |
평균 승하차총승객수가 압도적인 2호선과 마의 1호선..
df_pivot = pd.pivot_table(df, index = '호선명', aggfunc='mean')
fig, ax = plt.subplots(figsize=(12,6))
plt.bar(df_pivot.index, df_pivot['승하차총승객수'],
color=['navy','green','orange','skyblue','purple','brown','olive','m','brown','cyan','yellow'], alpha=.7)
plt.show()
승차총승객수 평균과 하차총승객수 평균의 차이를 검정하기 위해 각 호선별로 승하차차이를 이용해 독립표본 t-test를 진행한다 (유의수준 0.05)
귀무가설 (Null Hypothesis, H0): 승차총승객수와 하차총승객수 사이의 평균 차이가 없다.
대립가설 (Alternative Hypothesis, Ha): 승차총승객수와 하차총승객수 사이의 평균 차이가 있다.
# 각 호선별로 t-검정 수행
for _, group in df.groupby('호선명'):
t_stat, p_value = ttest_1samp(group['승하차차이'], 0)
print(f"호선명: {group['호선명'].iloc[0]}, t-statistic: {t_stat}, p-value: {p_value}")
호선명: 1호선, t-statistic: 20.50019913211897, p-value: 1.5013539622146513e-76 호선명: 2호선, t-statistic: -9.838658564414812, p-value: 1.6902267990472966e-22 호선명: 3호선, t-statistic: 4.212337796836365, p-value: 2.644108225464244e-05 호선명: 4호선, t-statistic: -11.080636905833476, p-value: 1.7062088891958835e-27 호선명: 5호선, t-statistic: 1.5969611020451597, p-value: 0.11037580978645846 호선명: 6호선, t-statistic: 4.614486144117203, p-value: 4.181811741694999e-06 호선명: 7호선, t-statistic: 11.71863204182446, p-value: 8.502923159409516e-31 호선명: 8호선, t-statistic: -4.562058883875936, p-value: 5.758212190905045e-06 호선명: 9호선, t-statistic: -11.220672414457352, p-value: 2.059687383937915e-28 호선명: 경의중앙선, t-statistic: 10.397416079625671, p-value: 7.759509615199845e-25 호선명: 분당선, t-statistic: -11.15582563776182, p-value: 5.708295261912186e-28
모든 호선의 p-value가 0.05보다 크므로 귀무가설을 기각하지 않는다. 즉, 승차총승객수와 하차총승객수의 차이는 통계적으로 유의미하지 않다.
승차총승객수와 하차총승객수를 더해서 그날의 유동인구로 생각하고 분석해보자.
# pivot_df = pd.pivot_table(df, index='Name', columns='Subject', values='Score', aggfunc='mean')
df_pivot1 = pd.pivot_table(df, index='호선명', columns='요일', values='승하차총승객수', aggfunc='sum')
df_pivot1 = df_pivot1[['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']]
df_pivot1
요일 | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday |
---|---|---|---|---|---|---|---|
호선명 | |||||||
1호선 | 5881684 | 5974188 | 6086646 | 7309910 | 7835635 | 6173683 | 3653816 |
2호선 | 23499140 | 24166210 | 24659936 | 29410924 | 31242435 | 23210368 | 13540260 |
3호선 | 8698420 | 8871041 | 8991453 | 10775227 | 11585635 | 8599141 | 5014977 |
4호선 | 7729725 | 7926257 | 8148203 | 9601917 | 10109264 | 7663100 | 4540690 |
5호선 | 9530964 | 9753526 | 9941519 | 11777409 | 12187792 | 8211199 | 4798152 |
6호선 | 5260797 | 5401156 | 5525739 | 6520696 | 6721036 | 4674623 | 2799905 |
7호선 | 8783455 | 8923290 | 9087533 | 10668392 | 11029736 | 7157486 | 4243561 |
8호선 | 2665883 | 2730405 | 2776683 | 3287238 | 3408746 | 2369290 | 1407025 |
9호선 | 6459914 | 6601641 | 6674240 | 8032284 | 8525809 | 6123029 | 3817480 |
경의중앙선 | 3466470 | 3508015 | 3601247 | 4231985 | 4367435 | 3214428 | 2005842 |
분당선 | 5439070 | 5571209 | 5692796 | 6692931 | 7038966 | 4644148 | 2660865 |
fig, ax = plt.subplots(6, 2, figsize=(30, 50), gridspec_kw={'hspace': 0.4})
ax[0,0].bar(df_pivot1.columns, df_pivot1.iloc[0], color='navy', alpha=.7)
ax[0,0].set_title('1호선')
ax[0,1].bar(df_pivot1.columns, df_pivot1.iloc[1], color='green', alpha=.7)
ax[0,1].set_title('2호선')
ax[1,0].bar(df_pivot1.columns, df_pivot1.iloc[2], color='orange', alpha=.7)
ax[1,0].set_title('3호선')
ax[1,1].bar(df_pivot1.columns, df_pivot1.iloc[3], color='skyblue', alpha=.7)
ax[1,1].set_title('4호선')
ax[2,0].bar(df_pivot1.columns, df_pivot1.iloc[4], color='purple', alpha=.7)
ax[2,0].set_title('5호선')
ax[2,1].bar(df_pivot1.columns, df_pivot1.iloc[5], color='brown', alpha=.7)
ax[2,1].set_title('6호선')
ax[3,0].bar(df_pivot1.columns, df_pivot1.iloc[6], color='olive', alpha=.7)
ax[3,0].set_title('7호선')
ax[3,1].bar(df_pivot1.columns, df_pivot1.iloc[7], color='magenta', alpha=.7)
ax[3,1].set_title('8호선')
ax[4,0].bar(df_pivot1.columns, df_pivot1.iloc[8], color='brown', alpha=.7)
ax[4,0].set_title('9호선')
ax[4,1].bar(df_pivot1.columns, df_pivot1.iloc[6], color='cyan', alpha=.7)
ax[4,1].set_title('경의중앙선')
ax[5,0].bar(df_pivot1.columns, df_pivot1.iloc[7], color='yellow', alpha=.7)
ax[5,0].set_title('분당선')
plt.show()
[결론]
숫자 말고 추세만 보면 오히려 월, 화, 수 비슷하다가 목요일을 넘어 금요일에 제일 많다..
시간대별로 보면 다른 결과가 나올 수도 있지만 어쨌든 가지고 있는 데이터로만 봤을 때 하루 총 유동인구로 보면 큰 차이가 없다.
아무래도 마음의 문제인 것일까..? 월요일엔 출근하기 싫고 금요일엔 발걸음이 가벼운 탓일까.
df_bd = df[df['호선명'] == '분당선'].copy()
df_bd
날짜 | 요일 | 호선명 | 전철역코드 | 역명 | 승차총승객수 | 하차총승객수 | 승하차총승객수 | 승하차비율 | 승하차차이 | |
---|---|---|---|---|---|---|---|---|---|---|
7389 | 2023-07-06 | Thursday | 분당선 | 0220 | 선릉 | 15395 | 23455 | 38850 | 1.523547 | -8060 |
16910 | 2023-06-25 | Sunday | 분당선 | 0220 | 선릉 | 3100 | 4766 | 7866 | 1.537419 | -1666 |
6518 | 2023-07-07 | Friday | 분당선 | 0220 | 선릉 | 14924 | 22987 | 37911 | 1.540271 | -8063 |
9130 | 2023-07-04 | Tuesday | 분당선 | 0220 | 선릉 | 15526 | 23024 | 38550 | 1.482932 | -7498 |
17769 | 2023-06-24 | Saturday | 분당선 | 0220 | 선릉 | 5929 | 9331 | 15260 | 1.573790 | -3402 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
23481 | 2023-06-17 | Saturday | 분당선 | 4501 | 기흥 | 8049 | 7426 | 15475 | 0.922599 | 623 |
11421 | 2023-07-01 | Saturday | 분당선 | 4501 | 기흥 | 7481 | 7070 | 14551 | 0.945061 | 411 |
28649 | 2023-06-11 | Sunday | 분당선 | 4501 | 기흥 | 5316 | 5092 | 10408 | 0.957863 | 224 |
24345 | 2023-06-16 | Friday | 분당선 | 4501 | 기흥 | 11576 | 10821 | 22397 | 0.934779 | 755 |
7071 | 2023-07-06 | Thursday | 분당선 | 4501 | 기흥 | 11317 | 10301 | 21618 | 0.910224 | 1016 |
1756 rows × 10 columns
df_bd['역명'].unique()
array(['선릉', '도곡', '수서', '한티', '구룡', '개포동', '대모산입구', '복정', '이매', '수원', '서울숲', '압구정로데오', '강남구청', '선정릉', '가천대', '태평', '모란', '야탑', '서현', '수내', '정자', '미금', '오리', '보정', '죽전', '구성', '신갈', '기흥', '상갈', '청명', '영통', '망포', '매탄권선', '수원시청', '매교'], dtype=object)
# 분당선 역 리스트 by 나무위키
bd_station_list = ['왕십리','서울숲','압구정로데오','강남구청','선정릉','선릉','한티','도곡','구룡','개포동','대모산입구',
'수서','복정','가천대','태평','모란','야탑','이매','서현','수내','정자','미금','오리','죽전','보정','구성','신갈',
'기흥','상갈','청명','영통','망포','매탄권선','수원시청','매교','수원']
# 실제 역 갯수와 차이
len(bd_station_list) - len(df_bd['역명'].unique())
1
# df_bd에만 있는 값 찾기
unique_in_df_bd = [x for x in df_bd['역명'].unique() if x not in bd_station_list]
unique_in_df_bd
[]
# bd_station_list에만 있는 값 찾기
unique_in_bd_station_list = [x for x in bd_station_list if x not in df_bd['역명'].unique()]
unique_in_bd_station_list
['왕십리']
아쉽게 킹십리는 없지만 그 외 모든 역이 다 있다. 분당선 역 갯수는 원래 36개인데 우리가 가진 데이터는 35개.
df_bd
날짜 | 요일 | 호선명 | 전철역코드 | 역명 | 승차총승객수 | 하차총승객수 | 승하차총승객수 | 승하차비율 | 승하차차이 | |
---|---|---|---|---|---|---|---|---|---|---|
7389 | 2023-07-06 | Thursday | 분당선 | 0220 | 선릉 | 15395 | 23455 | 38850 | 1.523547 | -8060 |
16910 | 2023-06-25 | Sunday | 분당선 | 0220 | 선릉 | 3100 | 4766 | 7866 | 1.537419 | -1666 |
6518 | 2023-07-07 | Friday | 분당선 | 0220 | 선릉 | 14924 | 22987 | 37911 | 1.540271 | -8063 |
9130 | 2023-07-04 | Tuesday | 분당선 | 0220 | 선릉 | 15526 | 23024 | 38550 | 1.482932 | -7498 |
17769 | 2023-06-24 | Saturday | 분당선 | 0220 | 선릉 | 5929 | 9331 | 15260 | 1.573790 | -3402 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
23481 | 2023-06-17 | Saturday | 분당선 | 4501 | 기흥 | 8049 | 7426 | 15475 | 0.922599 | 623 |
11421 | 2023-07-01 | Saturday | 분당선 | 4501 | 기흥 | 7481 | 7070 | 14551 | 0.945061 | 411 |
28649 | 2023-06-11 | Sunday | 분당선 | 4501 | 기흥 | 5316 | 5092 | 10408 | 0.957863 | 224 |
24345 | 2023-06-16 | Friday | 분당선 | 4501 | 기흥 | 11576 | 10821 | 22397 | 0.934779 | 755 |
7071 | 2023-07-06 | Thursday | 분당선 | 4501 | 기흥 | 11317 | 10301 | 21618 | 0.910224 | 1016 |
1756 rows × 10 columns
df_pivot_bd_sum = pd.pivot_table(df_bd, index = '역명', aggfunc='sum').sort_values('승하차총승객수', ascending=False)
df_pivot_bd_sum.head(10)
승차총승객수 | 승하차비율 | 승하차차이 | 승하차총승객수 | 하차총승객수 | |
---|---|---|---|---|---|
역명 | |||||
모란 | 1508836 | 77.000437 | -18472 | 3036144 | 1527308 |
정자 | 1341788 | 78.390270 | -44328 | 2727904 | 1386116 |
선릉 | 912996 | 116.184006 | -480960 | 2306952 | 1393956 |
미금 | 1097734 | 80.620072 | -67266 | 2262734 | 1165000 |
수서 | 1092916 | 78.303639 | -35696 | 2221528 | 1128612 |
야탑 | 937380 | 40.009893 | -51968 | 1926728 | 989348 |
서현 | 911505 | 38.609891 | -15481 | 1838491 | 926986 |
압구정로데오 | 746415 | 42.338158 | -87584 | 1580414 | 833999 |
강남구청 | 685318 | 87.699120 | -100094 | 1470730 | 785412 |
기흥 | 756082 | 70.623955 | 54654 | 1457510 | 701428 |
df_pivot_bd_mean = pd.pivot_table(df_bd, index = '역명', aggfunc='mean').sort_values('승하차총승객수', ascending=False)
df_pivot_bd_mean.head(10)
승차총승객수 | 승하차비율 | 승하차차이 | 승하차총승객수 | 하차총승객수 | |
---|---|---|---|---|---|
역명 | |||||
야탑 | 24667.894737 | 1.052892 | -1367.578947 | 50703.368421 | 26035.473684 |
서현 | 23986.973684 | 1.016050 | -407.394737 | 48381.342105 | 24394.368421 |
압구정로데오 | 19642.500000 | 1.114162 | -2304.842105 | 41589.842105 | 21947.342105 |
모란 | 19853.105263 | 1.013164 | -243.052632 | 39949.263158 | 20096.157895 |
정자 | 17655.105263 | 1.031451 | -583.263158 | 35893.473684 | 18238.368421 |
한티 | 15326.815789 | 1.043554 | -760.105263 | 31413.736842 | 16086.921053 |
선릉 | 12013.105263 | 1.528737 | -6328.421053 | 30354.631579 | 18341.526316 |
미금 | 14443.868421 | 1.060790 | -885.078947 | 29772.815789 | 15328.947368 |
수서 | 14380.473684 | 1.030311 | -469.684211 | 29230.631579 | 14850.157895 |
망포 | 14656.763158 | 0.958116 | 717.710526 | 28595.815789 | 13939.052632 |
왜 순위에서 sum이랑 mean이랑 이렇게 많이 차이가 날까?
sum으로 봐야 할까 mean으로 봐야 할까?
특정 기간에 특정 역에 많은 사람이 몰렸을 수 있으므로 mean으로 보는 게 그런 편향을 조금이나마 상쇄할 수 있을까?
df_pivot_bd_mean
승차총승객수 | 승하차비율 | 승하차차이 | 승하차총승객수 | 하차총승객수 | |
---|---|---|---|---|---|
역명 | |||||
야탑 | 24667.894737 | 1.052892 | -1367.578947 | 50703.368421 | 26035.473684 |
서현 | 23986.973684 | 1.016050 | -407.394737 | 48381.342105 | 24394.368421 |
압구정로데오 | 19642.500000 | 1.114162 | -2304.842105 | 41589.842105 | 21947.342105 |
모란 | 19853.105263 | 1.013164 | -243.052632 | 39949.263158 | 20096.157895 |
정자 | 17655.105263 | 1.031451 | -583.263158 | 35893.473684 | 18238.368421 |
한티 | 15326.815789 | 1.043554 | -760.105263 | 31413.736842 | 16086.921053 |
선릉 | 12013.105263 | 1.528737 | -6328.421053 | 30354.631579 | 18341.526316 |
미금 | 14443.868421 | 1.060790 | -885.078947 | 29772.815789 | 15328.947368 |
수서 | 14380.473684 | 1.030311 | -469.684211 | 29230.631579 | 14850.157895 |
망포 | 14656.763158 | 0.958116 | 717.710526 | 28595.815789 | 13939.052632 |
수원시청 | 13254.894737 | 1.090423 | -1268.710526 | 27778.500000 | 14523.605263 |
죽전 | 13112.210526 | 1.104147 | -1330.552632 | 27554.973684 | 14442.763158 |
수내 | 13445.500000 | 0.989239 | 76.447368 | 26814.552632 | 13369.052632 |
태평 | 13117.342105 | 0.949621 | 634.921053 | 25599.763158 | 12482.421053 |
서울숲 | 12180.421053 | 1.021258 | -272.078947 | 24632.921053 | 12452.500000 |
오리 | 10963.947368 | 0.836179 | 1827.342105 | 20100.552632 | 9136.605263 |
강남구청 | 9017.342105 | 1.153936 | -1317.026316 | 19351.710526 | 10334.368421 |
기흥 | 9948.447368 | 0.929263 | 719.131579 | 19177.763158 | 9229.315789 |
영통 | 8694.052632 | 0.998206 | -33.736842 | 17421.842105 | 8727.789474 |
가천대 | 8173.973684 | 1.064463 | -457.157895 | 16805.105263 | 8631.131579 |
수원 | 8159.000000 | 0.879354 | 977.710526 | 15340.289474 | 7181.289474 |
선정릉 | 7161.710526 | 1.032849 | -282.315789 | 14605.736842 | 7444.026316 |
도곡 | 7170.131579 | 0.961123 | 235.815789 | 14104.447368 | 6934.315789 |
이매 | 5888.710526 | 0.939768 | 375.263158 | 11402.157895 | 5513.447368 |
대모산입구 | 5551.657895 | 0.940231 | 343.763158 | 10759.552632 | 5207.894737 |
상갈 | 5322.078947 | 0.978058 | 157.684211 | 10486.473684 | 5164.394737 |
매탄권선 | 5241.789474 | 0.962784 | 194.578947 | 10289.000000 | 5047.210526 |
구성 | 4797.605263 | 1.058980 | -261.131579 | 9856.342105 | 5058.736842 |
개포동 | 4653.315789 | 1.018432 | -102.052632 | 9408.684211 | 4755.368421 |
매교 | 4670.052632 | 0.986118 | 64.500000 | 9275.605263 | 4605.552632 |
신갈 | 4536.736842 | 0.984404 | 94.052632 | 8979.421053 | 4442.684211 |
청명 | 4313.000000 | 0.975457 | 148.263158 | 8477.736842 | 4164.736842 |
보정 | 2630.105263 | 0.897480 | 286.210526 | 4974.000000 | 2343.894737 |
구룡 | 2500.947368 | 0.968273 | 111.368421 | 4890.526316 | 2389.578947 |
복정 | 1.652174 | 0.000000 | 1.652174 | 1.652174 | 0.000000 |
df_line2 = df[df['호선명'] == '2호선']
df_line2
날짜 | 요일 | 호선명 | 전철역코드 | 역명 | 승차총승객수 | 하차총승객수 | 승하차총승객수 | 승하차비율 | 승하차차이 | |
---|---|---|---|---|---|---|---|---|---|---|
11791 | 2023-07-01 | Saturday | 2호선 | 0151 | 시청 | 17946 | 15957 | 33903 | 0.889168 | 1989 |
17816 | 2023-06-24 | Saturday | 2호선 | 0151 | 시청 | 14129 | 12396 | 26525 | 0.877344 | 1733 |
6567 | 2023-07-07 | Friday | 2호선 | 0151 | 시청 | 29142 | 27848 | 56990 | 0.955597 | 1294 |
9181 | 2023-07-04 | Tuesday | 2호선 | 0151 | 시청 | 29967 | 27781 | 57748 | 0.927053 | 2186 |
7440 | 2023-07-06 | Thursday | 2호선 | 0151 | 시청 | 31645 | 30506 | 62151 | 0.964007 | 1139 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
4004 | 2023-07-10 | Monday | 2호선 | 4713 | 신설동 | 3536 | 3432 | 6968 | 0.970588 | 104 |
14421 | 2023-06-28 | Wednesday | 2호선 | 4713 | 신설동 | 3591 | 3456 | 7047 | 0.962406 | 135 |
11835 | 2023-07-01 | Saturday | 2호선 | 4713 | 신설동 | 3668 | 3414 | 7082 | 0.930752 | 254 |
12695 | 2023-06-30 | Friday | 2호선 | 4713 | 신설동 | 3932 | 3797 | 7729 | 0.965666 | 135 |
20444 | 2023-06-21 | Wednesday | 2호선 | 4713 | 신설동 | 3722 | 3705 | 7427 | 0.995433 | 17 |
2964 rows × 10 columns
df_pivot_line2_mean = pd.pivot_table(df_line2, index = '역명', aggfunc='mean').sort_values('승하차총승객수', ascending=False)
df_pivot_line2_mean.head(10)
승차총승객수 | 승하차비율 | 승하차차이 | 승하차총승객수 | 하차총승객수 | |
---|---|---|---|---|---|
역명 | |||||
강남 | 78123.289474 | 0.963618 | 2820.131579 | 153426.447368 | 75303.157895 |
잠실 | 74608.947368 | 0.986895 | 822.368421 | 148395.526316 | 73786.578947 |
홍대입구 | 66732.473684 | 1.078049 | -5503.605263 | 138968.552632 | 72236.078947 |
삼성 | 55585.447368 | 0.993662 | 100.973684 | 111069.921053 | 55484.473684 |
구로디지털단지 | 54991.342105 | 0.983839 | 724.921053 | 109257.763158 | 54266.421053 |
신림 | 54992.710526 | 0.964236 | 2012.210526 | 107973.210526 | 52980.500000 |
역삼 | 46996.526316 | 1.124520 | -6147.868421 | 100140.921053 | 53144.394737 |
선릉 | 52571.578947 | 0.874674 | 6369.763158 | 98773.394737 | 46201.815789 |
신도림 | 48334.684211 | 0.984420 | 836.421053 | 95832.947368 | 47498.263158 |
을지로입구 | 45465.763158 | 1.022204 | -1228.789474 | 92160.315789 | 46694.552632 |
//끝//