실전 데이터 분석 56: 도시 및 계절별 초미세먼지(PM2.5) 오염도 대조 및 도시 교통량 대비 미세먼지 상관 분석

📥 실습 주피터 노트북(.ipynb) 다운로드

📌 강의 개요 (30분 완성)

코믹 일러스트 주요 도시의 대기 오염 정보와 교통 지수를 수집한 환경 과학 데이터셋입니다. 도시 및 계절에 따른 초미세먼지 농도의 평균차를 막대 차트로 다차원 비교하고, 도로 교통량(Traffic Index)의 증가와 미세먼지 수치 간의 양의 선형 인과관계를 산점도로 규명합니다.

학습 목표:

  • 계절 조건부 평균 대치 (transform): 미세먼지는 계절별 풍향과 난방 등의 영향이 전적으로 다르므로 계절별 평균 농도로 결측치를 채웁니다.
  • 다선 계절성 막대 차트 (barplot): 도시별 초미세먼지 총합/평균을 나타낼 때 계절(Season)을 오버레이 범주로 분할해 계절적 농도 편차를 대조합니다.

🧐 실무 도메인 지식 가이드 (Domain Knowledge Guide)

💰 금융 및 핀테크 (Finance & FinTech) 금융 데이터 분석은 자산의 리스크 관리(Credit Risk), 투자 자산 평가, 이상 금융 거래 감지(FDS) 등을 해결하기 위한 통계적 검정 분야입니다.

  • 리스크와 대출 부도(Default): 대출 연체 여부나 투자 손실 위험은 금융 자산 건전성을 지키기 위해 고도화된 스코어링 모형으로 분석됩니다.
  • 소득 대비 부채 비율(DTI): 개인이 갚을 수 있는 능력 한도를 객관적으로 판단하는 지표로 금융 신용 여신 관리의 핵심 척도입니다.
  • 자산 변동성(Volatility): 주가나 크립토 시세의 표준편차는 위험 자산의 위험도를 계산(VaR)하고 투자 포트폴리오를 다변화하는 기준이 됩니다.

Step 1: 데이터 구조 살펴보기 (Data Overview)

Step 1 데이터 구조 개념도

csv_data 폴더에 준비해 둔 air_quality.csv 파일을 판다스로 불러옵니다.

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# 그래프 설정 (한글 폰트 및 마이너스 기호 깨짐 방지)
import koreanize_matplotlib
plt.rcParams['axes.unicode_minus'] = False
sns.set_theme(style="whitegrid")

# 로컬 CSV 파일 불러오기
df = pd.read_csv('./air_quality.csv')

# 데이터 구조 및 첫 5행 확인
print(df.info())
print(df.head())

💻 [실행 결과]

<class 'pandas.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 6 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   Date           1000 non-null   str    
 1   City           1000 non-null   str    
 2   PM2_5          986 non-null    float64
 3   PM10           1000 non-null   float64
 4   Season         1000 non-null   str    
 5   Traffic_Index  1000 non-null   float64
dtypes: float64(3), str(3)
memory usage: 74.1 KB
None
               Date     City  PM2_5   PM10  Season  Traffic_Index
0  2023-01-01 00:00  Daejeon   63.2   77.3  Winter           17.5
1  2023-01-01 08:00    Seoul   97.5  166.1  Winter           78.9
2  2023-01-01 16:00  Incheon   60.7   79.9  Autumn           95.1
3  2023-01-02 00:00    Daegu   36.2   54.5  Autumn           23.3
4  2023-01-02 08:00  Incheon   72.2  121.9  Spring           39.9

<class ‘pandas.DataFrame’> RangeIndex: 1000 entries, 0 to 999 Data columns (total 6 columns): # Column Non-Null Count Dtype
— —— ————– —–
0 Date 1000 non-null object 1 City 1000 non-null object 2 PM2_5 986 non-null float64 3 PM10 1000 non-null float64 4 Season 1000 non-null object 5 Traffic_Index 1000 non-null float64 dtypes: float64(3), object(3) memory usage: 47.0 KB None Date City PM2_5 PM10 Season Traffic_Index 0 2023-01-01 00:00 Daejeon 63.2 77.3 Winter 17.5 1 2023-01-01 08:00 Seoul 97.5 166.1 Winter 78.9 2 2023-01-01 16:00 Incheon 60.7 79.9 Autumn 95.1 3 2023-01-02 00:00 Daegu 36.2 54.5 Autumn 23.3 4 2023-01-02 08:00 Incheon 72.2 121.9 Spring 39.9

```

💡 코드 딥다이브 (Code Deep Dive)

주요 분석 대상 컬럼:

  • Date: 대기 오염도 정보 관측 시간 (년-월-일 시:분)
  • City: 대기 오염 물질 측정 도시 (서울, 부산, 인천, 대구, 대전 등)
  • PM2_5: 초미세먼지 농도 지수 (PM2.5, ㎍/㎥, 결측치 존재)
  • PM10: 일반 미세먼지 농도 지수 (PM10, ㎍/㎥)
  • Season: 관측 시점의 계절 정보 (Spring, Summer, Autumn, Winter)
  • Traffic_Index: 해당 관측 지점 인근의 도로 차량 교통량 인덱스 (10.0~100.0)

Step 2: 전처리와 결측치 정제 (Preprocess)

Step 2 데이터 정제 개념도

현실의 데이터는 항상 누락이 있거나 유효성 정제가 필요합니다. 데이터 전처리 단계에서 결측 상태를 확인하고 올바르게 보정합니다.

# 1. 미세먼지 결측치 개수 파악
print("--- 정제 전 결측치 개수 ---")
print(df['PM2_5'].isnull().sum())

# 2. 계절(Season)별 평균 초미세먼지 농도로 결측 대치
df['PM2_5'] = df.groupby('Season')['PM2_5'].transform(lambda x: x.fillna(x.mean()))

print("\n--- 정제 후 결측치 개수 ---")
print(df['PM2_5'].isnull().sum())

💻 [실행 결과]

--- 정제 전 결측치 개수 ---
14

--- 정제 후 결측치 개수 ---
0

— 정제 전 결측치 개수 — 14

— 정제 후 결측치 개수 — 0

```

💡 분석가의 통찰 (Analyst’s Insight)

  • 계절 가중 대치의 필요성: 대기 오염 수치는 계절적 특성(봄철 황사, 여름철 잦은 비로 인한 대기 정화, 겨울철 대기 정체 및 보일러 가동 등)에 따른 기저 변화가 매우 큽니다. 만약 결측을 단순히 1년 전체 평균값으로 대치하면, 깨끗한 여름철 관측 데이터의 결측치가 터무니없이 높게 들어가 왜곡을 부릅니다. 따라서 계절 그룹 평균을 구하여 대입하는 것이 합당합니다.

Step 3: 단변수 분포 분석 (Univariate EDA)

Step 3 시각화 개념도

가장 먼저 핵심 변수가 전체 데이터에서 어떤 빈도와 분포를 가졌는지 단일 변수 시각화를 통해 파악해 봅니다.

plt.figure(figsize=(8, 5))

# 도시별 초미세먼지 농도 평균을 계절별 범주로 대조하여 막대 차트 생성
sns.barplot(data=df, x='City', y='PM2_5', hue='Season', palette='YlOrRd', errorbar=None)

plt.title('도시 및 계절별 평균 초미세먼지(PM2.5) 오염도', fontsize=14, fontweight='bold')
plt.xlabel('도시')
plt.ylabel('평균 초미세먼지 (㎍/㎥)')
plt.show()

💻 [실행 결과] 실행 결과 시각화

💡 시각화 차트 읽는 법 & 인사이트

  • 시각적으로 드러나는 계절성과 도시 특성: 전반적으로 봄(Spring, 노란색)과 겨울(Winter, 붉은색) 막대 높이가 우뚝 솟아 있으며, 여름(Summer)은 눈에 띄게 낮게 하강합니다. 또한 서울(Seoul)과 인천(Incheon)과 같은 수도권 수도 벨트 지역의 연간 농도가 남부 거점 지역 대비 다소 높게 오버레이되어 밀집되어 있음을 보여줍니다.

Step 4: 다변수 상관관계 및 이상치 분석 (Multivariate EDA)

Step 4 상관관계 분석 개념도

두 개 이상의 변수를 동시에 결합하여, 조건에 따른 수치 차이나 독립 변수와 종속 변수 간의 통계적 경향을 분석합니다.

plt.figure(figsize=(9, 6))

# 교통량과 초미세먼지 간의 산점도 시각화
sns.scatterplot(data=df, x='Traffic_Index', y='PM2_5', hue='Season', alpha=0.7)

plt.title('교통량 지수와 초미세먼지(PM2.5) 농도 상관성', fontsize=14, fontweight='bold')
plt.xlabel('교통량 지수')
plt.ylabel('초미세먼지 농도 (㎍/㎥)')
plt.show()

💻 [실행 결과] 실행 결과 시각화

💡 코드 딥다이브 & 비즈니스 통찰 (Analyst’s Insight)

  • 교통 정체와 대기 환경의 정비례 선형 연동: 산점도 내 분포를 보면, 교통량 지수(X축)가 증가함에 따라 초미세먼지 오염도(Y축) 수치도 조밀하게 우상향 방향으로 뻗어가고 있습니다. 차량 배기가스가 지역 대기 질 하락의 중요한 독립 변수로서 통계적 기울기를 제공하고 있음을 명확히 증명합니다.

Step 5: 통계적 직관과 해석 (Statistical Logic)

💡 [시간 가중치와 교란변수의 다중 통계 분석 직관] 단순히 ‘교통량이 높은 날에 미세먼지가 높다’는 결과가 단순히 ‘바람이 불지 않는 겨울철에 교통 체증이 우연히 겹쳐서’ 나온 가짜 결과 아닐까? 하는 의구심이 생길 수 있습니다. 이렇게 원래 인과관계를 흐리는 외부 요인을 교란변수(Confounding Variable)라고 합니다.

  • 이 경우 ‘계절(Season)’이 미세먼지 기저치와 교통량 모두에 영향을 주며 교란을 유발할 수 있습니다.
  • 통계 모델링에서는 계절 효과를 더미 변수(Dummy Variable)화해 모형의 우변에 얹어 ‘통제변수’로 세운 뒤 교통량의 회귀 계수를 돌립니다.
  • 계절 효과를 완전히 보정한 상태에서도 교통량 1단위 증가당 초미세먼지 증가율이 p-value 0.05 미만으로 유의하다면, 비로소 ‘차량 배기가스는 기상 조건과 무관하게 독자적으로 대기오염을 유발한다’는 인과적 가치를 정책 제안서에 쓸 수 있게 됩니다.

🎯 30분 강의 마무리 및 심화 과제

오늘 우리는 실전 데이터셋을 분석하여 판다스로 데이터를 가공 및 정제하고, 시각화를 활용하여 핵심 변수 간의 통계적 유의성을 검증했습니다. 데이터 속에서 숨겨진 패턴을 올바른 시각으로 탐색하는 능력이 데이터 사이언티스트의 가장 강력한 무기입니다.

📝 심화 과제 (Advanced Challenge)

  1. 미세먼지(PM10)와 초미세먼지(PM2.5)의 상관계수 산출: 두 오염 물질 간의 피어슨 상관분석(corr())을 수행해 보세요. 둘은 선형으로 얼마나 결합되어 있나요?
  2. 미세먼지 경보일 파생변수 생성 및 빈도 대조: 초미세먼지(PM2_5)가 80㎍/㎥ 이상인 날을 ‘Bad’, 미만인 날을 ‘Good’으로 분류한 파생변수 AirStatus를 만들고 도시별 나쁜 날의 점유 건수를 요약해 보세요.
서브목차