본문 바로가기
프로젝트

[분석프로젝트] 히트맵, 이동평균을 통해 날씨 분석하기

by kime2 2024. 2. 28.
스파르타 코딩클럽 기초학습

 

 

목적

 날짜별 기언데이터를 통해 (1)기온변화의 추세, (2)계절적 패턴, (3)이상치를 탐색하여 행후 기온을 예측한다

 

방법

  1. 데이터 정제 및 조작 : 결측치 처리, 날짜데이터 변환 등의 전처리 수행
  2. 기초통계분석 : 연도별, 월별 기온의 평균과 분포분석
  3. 이동평균 분석 : 장기적인 기온 추세를 파악하기 위해 이동평균 계산
  4. 상자그림 및 산점도 분석 : 계절적 패턴과 이상치 식별

 

사용데이터

"Daily Minimum Temperatures in Melbourne" 

https://raw.githubusercontent.com/jbrownlee/Datasets/master/daily-min-temperatures.csv


1단계: 데이터로드 및 전처리

-> 데이터를 불러오고 결측치를 제거한다

# 필요한 라이브러리 임포트
import pandas as pd  # 데이터를 다루는데 사용되는 라이브러리
import matplotlib.pyplot as plt  # 시각화를 위한 라이브러리
import seaborn as sns  # Matplotlib을 기반으로 한 시각화 라이브러리

 

# 데이터 로드
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/daily-min-temperatures.csv'  # 데이터가 저장된 URL -> csv가 아닐경우는?
data = pd.read_csv(url, parse_dates=['Date'], index_col='Date')
  • parse_dates : Date열을 데이트타입으로 전환하도록 지정 = pd.datetime
  • index_col : 인덱스로 부르고자하는 열 = set_index
# 데이터 확인
print(data.head())
print(data.tail())

# 결측치 확인 및 처리
if data.isnull().values.any():  # 데이터에 결측치(NaN)가 있는지 확인
    data.dropna(inplace=True)  # 결측치가 있으면 해당 행을 삭제함 -> 너무 많은 경우 다른 값으로 대체

# 데이터 타입 확인
print(data.dtypes)

 

 

  • isnull() 함수는 각 요소가 결측치인지를 검사 -> values 속성을 통해 결과 얻기 -> any() 함수는 하나 이상의 True 값을 가지면 True를 반환
  • => 데이터에 결측치가 있을 경우True를 반환
  • dropna() 함수는 결측치를 포함한 행을 제거 -> inplace=True 옵션은 data 데이터프레임이 직접 변경
  • => 결측치가 있는 행은 제가된 후 데이터프레임에 바로반영

 

2단계 : 기초통계 분석

-> 연도별, 월별 평균을 계산한다

# 연도와 월 컬럼 추가
# data인덱스에서 연도를 추출하여 Year의 컬럼의 저장
# data인덱스에서 월을 추출하여 Month의 컬럼의 저장
data['Year'] = data.index.year
data['Month'] = data.index.month

# 연도별 평균 기온 계산
yearly_avg_temp = data.groupby('Year')['Temp'].mean()  # 'Year'로 그룹화하여 각 연도별 평균 기온을 계산

# 월별 평균 기온 계산
monthly_avg_temp = data.groupby(['Year', 'Month'])['Temp'].mean().unstack()
# 'Year'와 'Month'로 그룹화하여 각 연도별, 월별 평균 기온을 계산하고,
# unstack() 함수를 사용하여 다중 인덱스에서 월을 열로 변환하여 표 형태로 만듦 -> 중요!

# 연도별 평균 기온 통계 정보 출력
print(yearly_avg_temp.describe())
# 연도별 평균 기온에 대한 통계 정보(평균, 표준편차, 최솟값, 1사분위수, 중위값, 3사분위수, 최댓값)를 출력

 

3단계 : 이동평균 분석 및 회귀분석을 통한 추세선

-> 이동평균분석을 통해 연도별 기온의 추세를 파악한다

 

  • 이동평균분석이란

    과거로부터 현재까지 시계열 자료를 대상으로 일정기간별 이동평균을 계산하고, 이들의 추세를 파악하여 다음 기간을 예측하는 법
    시계열 자료에서 계절변동과 불규칙변동을 제거하여 추세변동과 순환변동만 가진 시계열로 변환하는 벙법으로 사용됨

    간단하고 쉽게 미래를 예측할 수 있으며, 자료수가 많고 안정된 패턴이 있는 경우 품질이 좋다
    뚜렷한 추세가 있거나 불규칙 변동이 심하지 않은 경우 짧은 주기
# 12개월 이동 평균 계산
data['Moving_Avg'] = data['Temp'].rolling(window=365).mean()
# 'Temp' 열을 기준으로 12개월 이동 평균을 계산하여 'Moving_Avg' 열에 저장
# rolling(window=365) 메서드는 이동 평균을 계산하는데 사용되며, 창(window) 크기를 12로 지정하여 365일로 이동 평균을 계산
# 인덱스가 꼭 datetime이어야 함

# 이동 평균 시각화
plt.figure(figsize=(12, 6))  # 시각화 영역의 크기를 설정
plt.plot(data['Moving_Avg'], color='orange', label='12-Month Moving Average')  # 이동 평균 데이터를 주황색으로 선 그래프로 플롯
plt.title('12-Month Moving Average of Temperature')  # 그래프 제목 설정
plt.xlabel('Date')  # x축 레이블 설정
plt.ylabel('Temperature')  # y축 레이블 설정
plt.legend()  # 라벨을 했으면 꼭 범례 표시
plt.show()  # 그래프 출력

data.rolling(window = n).mean()

 

  • 이동평균 주기에 다른 그래프 변화
window = 30 (한달) window = 120(분기) window = 730 (2년)

 

주기가 길 수록 장기 추세를 확인이 가능하며 단기적으로는 일정한 경향을 알 수 있다

상승과 하강을 반복하지만 상승의 최고점이 점점 높아지는 것을 볼 수 있다

특히, 1988년 이후 더욱 온도가 놀라가고 있다

 

import numpy as np  # 수치 연산을 위한 라이브러리
from sklearn.linear_model import LinearRegression  # 선형 회귀 모델 라이브러리 임포트

# 연도별 이동 평균 기온 준비
yearly_moving_avg = data['Moving_Avg'].resample('Y').mean()
# 'Moving_Avg' 열의 연도별 이동 평균을 계산하기 위해 연도(Year)별로 리샘플링하고, 각 연도별 평균값을 계산함
# .resample : 계열 index를 연도 단위의 동일 간격별로 데이터를 뽑으라는 뜻 -> 인덱스가 데이트타입이어야 가능(resample, rolling, year,month), 그룹바이랑 비슷

# 선형 회귀 모델을 사용하여 연도별 평균 추세선 계산
model = LinearRegression().fit(np.arange(len(yearly_moving_avg)).reshape(-1, 1), yearly_moving_avg.values)
# 선형 회귀 모델을 초기화하고, 연도별 이동 평균 기온 데이터에 대해 학습함
# np.arange(len(yearly_moving_avg)).reshape(-1, 1)은 데이터를 피팅하기 위한 형태로 변환함 -> 해석필요
# fit(X_train,y_train)

# 추세선을 위한 예측 값 계산
trend = model.predict(np.arange(len(yearly_moving_avg)).reshape(-1, 1))
# 학습된 모델을 사용하여 각 연도별 추세 값을 예측함

# 이동 평균과 함께 추세선 그리기
plt.figure(figsize=(12, 6))  # 그래프 영역의 크기를 설정함
plt.plot(yearly_moving_avg.index, yearly_moving_avg.values, label='12-Month Moving Average')  # 12개월 이동 평균을 플롯함
plt.plot(yearly_moving_avg.index, trend, label='Trend', color='red')  # 추세선을 빨간색으로 플롯함
plt.title('12-Month Moving Average with Trend Line')  # 그래프 제목 설정
plt.xlabel('Year')  # x축 레이블 설정
plt.ylabel('Temperature')  # y축 레이블 설정
plt.legend()  # 범례 표시
plt.show()  # 그래프 출력

 

model = LinearRegression().fit(X_train,y_train)에 대한 풀이

  1. LinearRegression()은 선형 회귀 모델을 생성하는 클래스,  이 클래스를 호출하여 모델 객체를 생성
  2. fit(X_train, y_train)은 생성된 모델 객체에 대해 학습을 수행하는 메서드
  3. X_train은 학습 데이터로, 모델이 입력으로 사용할 특징 데이터를 나타냄
  4. y_train은 해당 데이터에 대한 타겟 변수로, 모델이 예측하고자 하는 결과값

np.arange(len(yearly_moving_avg)).reshape(-1, 1)에 대한 풀이

  1. len(yearly_moving_avg) 
    의미: yearly_moving_avg의 길이 
    >>> 10
  2. np.arange(yearly_moving_avg)
    의미: 시작 값부터 끝 값까지 (정해진 간격으로) 숫자들을 생성하여 배열
     >>> array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
  3. np.arange(len(yearly_moving_avg)).reshape(-1, 1)
    의미 : 1차원 array를 2차원으로 배열
    >>>array([[0],
            [1],
            [2],
            [3],
            [4],
            [5],
            [6],
            [7],
            [8],
            [9]])
질문

1. x_train에 왜 숫자데이터를 넣었을까?
     >[[0],        [1],        [2],        [3],        [4],        [5],        [6],        [7],        [8],        [9]]

2. x_train에 연도를 넣어야 되는 거 아닐까?
연도라는 거는 사람에게 중요한 거지 모델입장에서는 숫자의 증감이 중요함, 1000~2000과 2000~3000은 동일함,
즉 0부터 9까지 증가한다는 것을 나타내기 위해 숫자로 표시한 것

또한 데이터를 가로(컬럼별)로 입력해야 하기 때문(컬럼으로 넣어야 하기 때문)에 1차원 array를 2차원으로 배열

3. 추세선을 회귀분석으로 사용할 필요가 있을까? 각 연도의 평균을 선그래프로 그리는게 아닐까?

 

trend = model.predict(X_train)에 대한 풀이

predict(X_train) 의미: 학습된 모델(model = LinearRegression())을 사용하여 X_train에 대한 예측값을 1차원 배열로 반환

 

추세선의 기울기가 양수인 것을 보아 기온이 꾸준히 상승하는 것을 알 수 있다

 

 

4단계 : 상자그림 및 산점도 분석

-> 상자그림을 통해 중앙값, 사분위수, 이상치 등을 파악한다

 

  • 상자그림은 통해 확인하는 이상치

      IQR(inter quartile range) = 3Q(하위 75%가 되는 지점) - 1Q(하위 25%가 되는 지점)

      이상치 = Q1*1.5미만, Q3*1.5초과 범위에 있는 지점

 

이미지 Towards Data Science

 

# 월별 기온 분포 상자 그림
# x='Month'는 x축에 월을, y='Temp'는 y축에 기온을 나타냄
sns.boxplot(x='Month', y='Temp', data=data)

# 그래프 제목 설정
plt.title('Box Plot of Temperatures by Month')
plt.show()

 

 

 

유독 1,2월과 11,12월에 이상치가 많이 발생한것을 볼 수 있다.

그러나, 얼마나 많이 발생한 것인지는 알 수 없다

 

 

5단계 : 결과 시각화

-> 히트맵을 통해 월별 평균 기온의 변화를 확인한다

 

plt.figure(figsize=(12, 9))
# 그래프의 크기를 설정함

sns.heatmap(monthly_avg_temp, cmap='coolwarm', annot=True, fmt=".1f")
# seaborn의 heatmap 함수를 사용하여 월별 평균 기온 데이터를 열 지도(heat map)로 시각화함
# monthly_avg_temp는 월별 평균 기온 데이터를 포함하는 DataFrame이며, 각 셀에는 기온 값이 표시됨
# cmap='coolwarm'은 컬러맵을 설정하는데, coolwarm은 차가운 색과 따뜻한 색의 대비를 나타냄
# annot=True는 각 셀에 값의 주석을 추가하라는 의미이며, fmt=".1f"는 주석의 형식을 소수점 첫째 자리까지 표시하라는 의미임

plt.title('Heatmap of Monthly Average Temperatures')
# 그래프의 제목을 설정함

plt.xlabel('Month')
# x축 레이블을 설정함

plt.ylabel('Year')
# y축 레이블을 설정함

plt.show()
# 그래프를 출력함

 


결론

(1)기온변화의 추세

이동평균과 회귀분석을 통한 추세선을 통해 매년 평균온도가 상승하는 추세가 발견되었다

 

(2)계절적 패턴

산점도와 상자그림을 통해 1,2월달과 9,11,12월달에 이상치가 발견되었으며,

특별한 기후현상이나 수집의 오류일 수 있다

 

(3)이상치를 탐색

히트맵을 통해 12~3월이 여름, 6~9월이 경우로 파악되며 강력한 계절패턴이 확인된다.

또한 12과 특히 1,2월에 이상치가 많은것을 보야 여름일때 극단적인더위(데이터 수집오류가 아닐경우)가 많은것이 확인 되었다

 

 


썸네일 : https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExdTltaWV1bHBjN3RuZ2loamZiNGt3ajM4a2FwaDVkbnh2bWE5MHdsNCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/Q9G0c1I4vFnSllp3I8/giphy.gif