구글 코랩에서 실행하기
|
2.2 스마트카드 데이터 분석#
대중교통 이용자들의 승하차 정보를 담고 있는 스마트카드 데이터는 도시 교통 패턴을 이해하는 데 핵심적인 자료이다.
본 섹션에서는 수도권의 버스 및및 지하철 승하차 데이터를 활용하여 기본적인 분석 및 시각화를 수행한다.
본 분석에서 사용하는 데이터는 정류소 위치는 실제 위치지만 이용내역은 실제 데이터에 Noise를 입힌 가상의 데이터이다.
본 섹션에서는
교통약자에 집중하여 분석을 수행합니다. 교통약자란 누구일까요? 스마트카드 데이터에서 해당 정보를 추출할 수 있을까?
연구 질문 (Research Question)#
교통약자들의 이동 패턴은 일반 사람들과 비교해서 어떻게 다를까?
import pandas as pd
import os
from datetime import datetime, timedelta
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[1], line 1
----> 1 import pandas as pd
2 import os
3 from datetime import datetime, timedelta
ModuleNotFoundError: No module named 'pandas'
# 시각화 라이브러리
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns
import platform
# 운영체제별 한글 폰트 설정
system = platform.system()
if system == 'Windows':
plt.rcParams['font.family'] = 'Malgun Gothic'
font_name = 'Malgun Gothic'
elif system == 'Darwin': # macOS
plt.rcParams['font.family'] = 'AppleGothic'
font_name = 'AppleGothic'
else: # Linux
plt.rcParams['font.family'] = 'DejaVu Sans'
font_name = 'DejaVu Sans'
plt.rcParams['axes.unicode_minus'] = False # 마이너스 기호 깨짐 방지
# seaborn 스타일 적용
sns.set(font=font_name, rc={"axes.unicode_minus": False})
# 그래프 크기 기본 설정
plt.rcParams['figure.figsize'] = (10, 6)
2.2.1 데이터 읽기#
def load_trip_data(date):
"""
특정 날짜의 TCD 데이터를 불러오는 함수
Args:
date (str): 날짜 문자열 (예: '20240923')
Returns:
pd.DataFrame: 데이터프레임
"""
file_path = f'../data/smartcard/TCD_{date}_modify.parquet'
df = pd.read_parquet(file_path)
return df
# 날짜 설정 및 데이터 로드
date = '20240923'
df = load_trip_data(date)
print("=" * 50)
print("📊 스마트카드 승하차 이력 데이터 (TCD) 정보")
print("=" * 50)
print(f"- 데이터 크기: {df.shape[0]:,}건의 승하차 이력")
print(f"- 컬럼 수: {df.shape[1]}개")
print("- 설명: 대중교통(버스, 지하철) 이용자들의 승차/하차 정보를 담고 있는 교통카드 이용 내역")
print("- 포함 정보: 승하차 시간, 정류장/역 정보, 요금, 이동거리, 환승정보, 이용자 구분 등")
print("- 주요 컬럼:")
print(" * 교통카드 사용자 구분 코드: 1(일반), 2(어린이), 3(청소년), 4(경로), 5(장애인)")
print(" * 환승 건수: 해당 통행에서의 환승 횟수")
print(" * 총 통행 거리: 미터 단위")
print(" * 총 소요 시간: 초 단위")
print("=" * 50)
==================================================
📊 스마트카드 승하차 이력 데이터 (TCD) 정보
==================================================
- 데이터 크기: 770,455건의 승하차 이력
- 컬럼 수: 123개
- 설명: 대중교통(버스, 지하철) 이용자들의 승차/하차 정보를 담고 있는 교통카드 이용 내역
- 포함 정보: 승하차 시간, 정류장/역 정보, 요금, 이동거리, 환승정보, 이용자 구분 등
- 주요 컬럼:
* 교통카드 사용자 구분 코드: 1(일반), 2(어린이), 3(청소년), 4(경로), 5(장애인)
* 환승 건수: 해당 통행에서의 환승 횟수
* 총 통행 거리: 미터 단위
* 총 소요 시간: 초 단위
==================================================
정류소 정보 불러오기#
# STTN 파일 불러오기 (20240923 데이터만)
def load_sttn_data(date):
"""
특정 날짜의 STTN 데이터를 불러오는 함수
Args:
date (str): 날짜 문자열 (예: '20240923')
Returns:
pd.DataFrame: 정류장 데이터프레임
"""
file_path = f'../data/smartcard/STTN_{date}.parquet'
sttn_df = pd.read_parquet(file_path)
return sttn_df
# STTN 데이터 로드
sttn_df = load_sttn_data(date)
# 필요한 컬럼들의 고유값만 남기기
sttn_unique = sttn_df[['정류장 ID', '정류장 명칭', '정류장 ARS번호', '정류장 X 좌표',
'정류장 Y 좌표', '시도코드', '시도명', '시군구코드', '시군구명',
'읍면동코드', '읍면동명']].drop_duplicates()
sttn_unique
| 정류장 ID | 정류장 명칭 | 정류장 ARS번호 | 정류장 X 좌표 | 정류장 Y 좌표 | 시도코드 | 시도명 | 시군구코드 | 시군구명 | 읍면동코드 | 읍면동명 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 3100001 | 동울산우체국 | ~ | 35.51093 | 129.4297 | 31 | 울산광역시 | 31170 | 동구 | 3117010400 | 전하동 |
| 1 | 3100002 | 청량초등학교 | ~ | 35.48747 | 129.2983 | 31 | 울산광역시 | 31710 | 울주군 | 3171026221 | 청량읍 |
| 2 | 3100003 | 청량초등학교 | ~ | 35.48757 | 129.29838 | 31 | 울산광역시 | 31710 | 울주군 | 3171026221 | 청량읍 |
| 3 | 3100004 | 화봉쌍용예가 | ~ | 35.5938 | 129.3676 | 31 | 울산광역시 | 31200 | 북구 | 3120012500 | 화봉동 |
| 4 | 3100005 | 화봉휴먼시아2단지 | ~ | 35.59398 | 129.3677 | 31 | 울산광역시 | 31200 | 북구 | 3120012500 | 화봉동 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 129824 | 8019 | 남창 | ~ | 35.418539 | 129.28289 | 31 | 울산광역시 | 31710 | 울주군 | 3171025624 | 온양읍 |
| 129825 | 8020 | 망양 | ~ | 35.456522 | 129.287872 | 31 | 울산광역시 | 31710 | 울주군 | 3171025623 | 온양읍 |
| 129826 | 8021 | 덕하 | ~ | 35.493964 | 129.303064 | 31 | 울산광역시 | 31710 | 울주군 | 3171026224 | 청량읍 |
| 129827 | 8022 | 개운포 | ~ | 35.507907 | 129.321188 | 31 | 울산광역시 | 31140 | 남구 | 3114011000 | 상개동 |
| 129828 | 8023 | 태화강 | ~ | 35.53844 | 129.353792 | 31 | 울산광역시 | 31140 | 남구 | 3114010600 | 삼산동 |
128554 rows × 11 columns
print("=" * 50)
print("🚏 정류장 정보 데이터 (STTN) 정보")
print("=" * 50)
print(f"- 데이터 크기: {sttn_unique.shape[0]:,}개의 고유 정류장")
print(f"- 컬럼 수: {sttn_unique.shape[1]}개")
print("- 설명: 대중교통 정류장 및 지하철역의 위치 정보와 행정구역 정보를 담고 있는 데이터")
print("- 포함 정보: 정류장 ID, 명칭, 좌표, 행정구역코드, 행정구역명 등")
print("- 주요 컬럼:")
print(" * 정류장 ID: 각 정류장의 고유 식별번호")
print(" * 정류장 X/Y 좌표: 위경도 좌표 (WGS84)")
print(" * 시도/시군구/읍면동: 행정구역 코드 및 명칭")
print("- 활용: TCD 데이터의 승하차 정류장 정보와 매핑하여 공간분석 수행")
print("=" * 50)
==================================================
🚏 정류장 정보 데이터 (STTN) 정보
==================================================
- 데이터 크기: 128,554개의 고유 정류장
- 컬럼 수: 11개
- 설명: 대중교통 정류장 및 지하철역의 위치 정보와 행정구역 정보를 담고 있는 데이터
- 포함 정보: 정류장 ID, 명칭, 좌표, 행정구역코드, 행정구역명 등
- 주요 컬럼:
* 정류장 ID: 각 정류장의 고유 식별번호
* 정류장 X/Y 좌표: 위경도 좌표 (WGS84)
* 시도/시군구/읍면동: 행정구역 코드 및 명칭
- 활용: TCD 데이터의 승하차 정류장 정보와 매핑하여 공간분석 수행
==================================================
2.2.2 O-D 데이터 만들기#
현재의 데이터는 O-D 데이터라고는 할 수 없습니다. 개인의 통행이 여러번에 걸쳐서 발생하기 때문에 개별 Trip 마다 컬럼의 수가 달라지는 문제가 있습니다.
이 문제를 어떻게 해결할 수 있을까요?
완결성 있는 정류소 단위의 출발지-목적지 데이터를 만들어 봅시다.
출발지 정류소 ID | 교통카드 사용자 구분 코드 | 출발시간 | 도착시간 | 도착지 정류소 ID | 환승횟수 | 총 통행 거리 | 총 탑승 시간 | 총 소요 시간 컬럼을 포함해서 만들어보세요
from datetime import timedelta
# '시작'과 '종료'로 시작하는 컬럼 추출
start_end_cols = [col for col in df.columns if col.startswith(('시작', '종료'))]
# 기본 컬럼 리스트
base_cols = [
"교통카드 사용자 구분 코드",
"환승 건수",
"총 통행 거리",
"총 탑승 시간",
"총 소요 시간"
]
# 모든 컬럼 합치기
all_cols = start_end_cols + base_cols
# 원하는 컬럼만 정제
df_refined = df[all_cols].rename(columns={
"시작 승차 역 ID": "출발지 정류소 ID",
"종료 하차 역 ID": "도착지 정류소 ID",
"환승 건수": "환승횟수"
})
print(df_refined.head())
시작 승차 일시 시작 교통수단 코드 시작 교통수단 구분 코드 시작 승차 노선 ID 출발지 정류소 ID 시작 승차 금액 \
0 20240923221246.0 202 T 106 1853 0
1 20240923124248.0 203 T 208 2826 0
2 20240923081625.0 203 T 208 2823 0
3 20240923163554.0 202 T 106 1855 0
4 20240923160512.0 203 T 208 2826 0
종료 하차 일시 종료 교통수단 코드 종료 교통수단 구분 코드 종료 하차 노선 ID 도착지 정류소 ID \
0 20240923222933.0 202 T 106 1857
1 20240923125036.0 203 T 208 2827
2 20240923082515.0 203 T 208 2827
3 20240923164257.0 202 T 106 1856
4 20240923162457.0 203 T 208 2821
교통카드 사용자 구분 코드 환승횟수 총 통행 거리 총 탑승 시간 총 소요 시간
0 1 0 8100 1007 1007.0
1 4 0 1000 468 468.0
2 4 0 3500 530 530.0
3 2 0 1100 423 423.0
4 1 0 5300 1185 1185.0
# 문자열에서 뒤의 ".0" 제거
df_refined["출발시간"] = (
df_refined["시작 승차 일시"]
.astype(str) # 문자열로 변환
.str.replace(r"\.0$", "", regex=True) # 뒤의 ".0" 제거
.pipe(pd.to_datetime, format="%Y%m%d%H%M%S", errors="coerce") # datetime 변환
)
df_refined["도착시간"] = (
df_refined["종료 하차 일시"]
.astype(str)
.str.replace(r"\.0$", "", regex=True)
.pipe(pd.to_datetime, format="%Y%m%d%H%M%S", errors="coerce")
)
print(df_refined[["시작 승차 일시", "출발시간", "종료 하차 일시", "도착시간"]].head())
시작 승차 일시 출발시간 종료 하차 일시 도착시간
0 20240923221246.0 2024-09-23 22:12:46 20240923222933.0 2024-09-23 22:29:33
1 20240923124248.0 2024-09-23 12:42:48 20240923125036.0 2024-09-23 12:50:36
2 20240923081625.0 2024-09-23 08:16:25 20240923082515.0 2024-09-23 08:25:15
3 20240923163554.0 2024-09-23 16:35:54 20240923164257.0 2024-09-23 16:42:57
4 20240923160512.0 2024-09-23 16:05:12 20240923162457.0 2024-09-23 16:24:57
# 컬럼명 통일을 위한 매핑 딕셔너리
column_mapping = {
"시작 승차 일시": "출발 승차 일시",
"시작 교통수단 코드": "출발 교통수단 코드",
"시작 교통수단 구분 코드": "출발 교통수단 구분 코드",
"시작 승차 노선 ID": "출발 승차 노선 ID",
"시작 승차 금액": "출발 승차 금액",
"종료 하차 일시": "도착 하차 일시",
"종료 교통수단 코드": "도착 교통수단 코드",
"종료 교통수단 구분 코드": "도착 교통수단 구분 코드",
"종료 하차 노선 ID": "도착 하차 노선 ID"
}
# 컬럼명 변경
df_refined = df_refined.rename(columns=column_mapping)
# 변경된 컬럼명 확인
df_refined.columns
Index(['출발 승차 일시', '출발 교통수단 코드', '출발 교통수단 구분 코드', '출발 승차 노선 ID', '출발지 정류소 ID',
'출발 승차 금액', '도착 하차 일시', '도착 교통수단 코드', '도착 교통수단 구분 코드', '도착 하차 노선 ID',
'도착지 정류소 ID', '교통카드 사용자 구분 코드', '환승횟수', '총 통행 거리', '총 탑승 시간', '총 소요 시간',
'출발시간', '도착시간'],
dtype='object')
2.2.3 기초 통계 분석#
시간대별 승하차 패턴을 분석해봅시다. 언제 가장 이용량이 많을까요?
비교통약자, 어린이, 경로, 장애인별로 평균탑승시간 및 평균이동거리, 환승횟수수를 시각화 해보고 해석해 봅시다.
2.2.4 공간 분석#
스마트카드 데이터에는 위치 정보 (위도 및 경도)가 존재하지 않습니다.
어떻게 하면 스마트카드 데이터를 공간상에 시각화 할 수 있을까요? 아래 작업을 수행해 봅시다.
승차량이 많은 지역을 읍면동 단위로 공간상에 나타내봅시다
하차량이 많은 지역을 읍면동 단위로 공간상에 나타내봅시다
## 1. 연산효율화를 위해 출발지-도착지 단위로 df_refined aggregation. 출발정류소ID, 도착정류소 ID, 교통카드 사용자 구분 코드, cnt
## 2. df_refined랑 sttn_unique merge. key 값은 df_refined: (출발지 정류소 ID, 도착지 정류소 ID), sttn_unique: 정류장 ID
## 3. 공간분석을 위한 데이터 생성. 승차량 기준 geodataframe 및 하차량 기준 geodataframe 생성
df_refined
| 출발 승차 일시 | 출발 교통수단 코드 | 출발 교통수단 구분 코드 | 출발 승차 노선 ID | 출발지 정류소 ID | 출발 승차 금액 | 도착 하차 일시 | 도착 교통수단 코드 | 도착 교통수단 구분 코드 | 도착 하차 노선 ID | 도착지 정류소 ID | 교통카드 사용자 구분 코드 | 환승횟수 | 총 통행 거리 | 총 탑승 시간 | 총 소요 시간 | 출발시간 | 도착시간 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 20240923221246.0 | 202 | T | 106 | 1853 | 0 | 20240923222933.0 | 202 | T | 106 | 1857 | 1 | 0 | 8100 | 1007 | 1007.0 | 2024-09-23 22:12:46 | 2024-09-23 22:29:33 |
| 1 | 20240923124248.0 | 203 | T | 208 | 2826 | 0 | 20240923125036.0 | 203 | T | 208 | 2827 | 4 | 0 | 1000 | 468 | 468.0 | 2024-09-23 12:42:48 | 2024-09-23 12:50:36 |
| 2 | 20240923081625.0 | 203 | T | 208 | 2823 | 0 | 20240923082515.0 | 203 | T | 208 | 2827 | 4 | 0 | 3500 | 530 | 530.0 | 2024-09-23 08:16:25 | 2024-09-23 08:25:15 |
| 3 | 20240923163554.0 | 202 | T | 106 | 1855 | 0 | 20240923164257.0 | 202 | T | 106 | 1856 | 2 | 0 | 1100 | 423 | 423.0 | 2024-09-23 16:35:54 | 2024-09-23 16:42:57 |
| 4 | 20240923160512.0 | 203 | T | 208 | 2826 | 0 | 20240923162457.0 | 203 | T | 208 | 2821 | 1 | 0 | 5300 | 1185 | 1185.0 | 2024-09-23 16:05:12 | 2024-09-23 16:24:57 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 770450 | 20240923151149.0 | 533 | B | 41002019 | 4160070 | 2800 | 20240923160141.0 | 582 | B | 41382002 | 4118191 | 1 | 2 | 24657 | 1826 | 2992.0 | 2024-09-23 15:11:49 | 2024-09-23 16:01:41 |
| 770451 | 20240923134139.0 | 582 | B | 41213002 | 4100527 | 1350 | 20240923135016.0 | 582 | B | 41213002 | 4100415 | 1 | 0 | 3221 | 517 | 517.0 | 2024-09-23 13:41:39 | 2024-09-23 13:50:16 |
| 770452 | 20240923082721.0 | 202 | T | 106 | 1851 | 1400 | 20240923084049.0 | 202 | T | 106 | 1854 | 1 | 0 | 4200 | 808 | 808.0 | 2024-09-23 08:27:21 | 2024-09-23 08:40:49 |
| 770453 | 20240923155132.0 | 202 | T | 106 | 1854 | 1400 | 20240923160420.0 | 202 | T | 106 | 1852 | 1 | 0 | 3200 | 768 | 768.0 | 2024-09-23 15:51:32 | 2024-09-23 16:04:20 |
| 770454 | 20240923165823.0 | 582 | B | 41221003 | 4115022 | 1350 | 20240923170307.0 | 582 | B | 41221003 | 4115021 | 1 | 0 | 918 | 284 | 284.0 | 2024-09-23 16:58:23 | 2024-09-23 17:03:07 |
770455 rows × 18 columns
구글 코랩에서 실행하기