[python]서울의 여름은 얼마나 길어졌을까?: 1. 계절 길이 계산 코드

2025. 6. 11. 02:01·대기과학/프로그래밍

- 개요

유투브에서 여름이 길어지고, 겨울이 짧아졌다는 영상을 보았습니다(영상 링크).

 

지구온난화가 일어나니까 여름이 길어지는 것은 당연하지만 계절길이를 실제로 계산하고 시각화해보고자 합니다.

 

- 계절의 정의

영상에서 나온 기상청 자료는 기후정보포털의 계절길이 웹페이지 자료입니다.

일단, 중요한 점을 짚어보겠습니다.

1. 5도 기준으로 봄과 겨울의 시작일을 정하고, 20도를 기준으로 여름, 가을의 시작일을 정의

표 아래에 *을 보면

2. 최소 10년 이상의 기간의 일평균기온을 날짜별로 평균

3. 9일 이동평균

4. 웹페이지에 들어가서 보면 6개 지역의 평균 기온을 쓰지만 저는 서울 ASOS 47108 평균기온 자료만 씁니다.

 

- 데이터 전처리

1. 데이터 다운로드

서울 ASOS 47108 자료 다운로드에 대한 글은 이전에 포스트로 작성했습니다.

기상청 ASOS 일단위(daily) 기온, 강수량 자료를 가장 쉽고 빠르게 다운로드 하는 방법

 

2. 데이터 읽기, 필요한 칼럼 생성

위에서 다운로드한 데이터를 읽을 때 주의해야할 사항은 첫 7줄을 무시하는 것입니다.

전 csv파일로 저장해서 읽으면 날짜 칼럼에 \t(탭)이 들어가 있어 이 문자를 제거했습니다.

나중에 쓰일 칼럼(YYYY, MM, MMDD)을 따로 저장합니다.

(참고로 날짜 기준 오름차순 정렬이 되어있어야 이후 코드에서 문제가 없을겁니다. 위의 방식으로 다운로드 받으면 기본적으로 오름차순 정렬이 되어있습니다.)

import pandas as pd
fn = r'D:\bus2\ta_20250607132536.csv'
df = pd.read_csv(fn, skiprows=7, encoding='cp949') # skiprows=7로 7줄 무시
df['날짜'] = df['날짜'].str.replace('\t', '', regex=False) # \t문자를 없앰
df['날짜'] = pd.to_datetime(df['날짜']) # 날짜 칼럼은 string 타입이므로 datetime으로 변환

"""
날짜 칼럼으로 YYYY(연도), MM(월), MMDD(월-일)을 만듭니다.
"""

df['YYYY'] = df['날짜'].dt.year
df['MM'] = df['날짜'].dt.month
df['MMDD'] = df['날짜'].dt.strftime('%m-%d')

 

3. 결측 연도 확인

연도별 관측값을 확인하고자 합니다.

보통 1년은 365일이지만 윤년이면 2월 29일이 있어 1년이 366일입니다.

기후정보포털의 봄, 여름, 가을, 겨울 계절길이를 더해보면 365일이므로 그냥 2월 29일을 제거하겠습니다.

그리고 연도별로 365일이 아닌 연도를 파악합니다.

결측값이 있다면 1년에 365개보다 적은 수의 데이터가 있습니다.

"""
isin은 MMDD 칼럼에서 '02-29'인 인덱스를 찾는 것이고
~를 붙이면 True를 False로 False를 True로 바꿉니다.
즉 MMDD 칼럼에서 '02-29'이 아닌 날만 추출하는 것이죠(2월 29일 제거).
"""
df = df[~df['MMDD'].isin(['02-29'])].copy()

"""
groupby를 하고 size()를 하면 YYYY를 가진 행의 수를 샙니다.
year_size.values < 365로 1년이 365일보다 작은 연도를 추출합니다.
"""
year_size = df.groupby('YYYY').size()
print(year_size[year_size.values < 365 ])

"""
Output:
YYYY
1907     92
1950    299
1951     31
1952    335
2025    157
dtype: int64
"""

 

Output:에는 1년이 365일이 아닌 연도를 출력한 것으로

관측이 시작한 1907년, 한국전쟁이 있었던 1950~1952년에 결측값이 있습니다.

2025년의 경우 글을 작성하는 연도가 2025년이라 데이터의 수가 365개가 안되는 것이고요.

 

4. 9일 이동평균 및 10년 단위 기간 설정

9일 이동평균을 하되, 최소 10년 이상의 기간을 쓰라고 했으니까 10년 단위로 데이터를 평균합니다.

"""
pandas에서 제공하는 rolling과 mean()을 쓰면 이동평균이 됩니다.
저는 +-4일의 9일 이동평균을 사용할 것이므로 center=True로(기본은 False임),
window=9 (9일 구간 사용)로 설정합니다.

"""
df['t_roll'] = df['평균기온(℃)'].rolling(window=9, center=True).mean()

"""
np.floor는 소수점 숫자를 정수로 올림하는 함수로
np.floor((df['YYYY'] + 5) / 10) 이렇게 설정하면

2015~2024년의 decade_group은 202
2005~2014년의 decade_group은 201
1995~2004년의 decade_group은 200처럼
10년 단위로 decade_group의 숫자가 같아집니다.

"""
import numpy as np
df['decade_group'] = np.floor((df['YYYY'] + 5) / 10).astype(int)

"""
쓰지 않은 decadal_grouop을 제거하고 분석에 필요한 데이터를 data라는 변수에 저장합니다.
decadal_group 제거 목록 
191: 1907년부터 시작이라 1905~1914년 기간은 10년이 안됨
195: 한국전쟁 결측
203: 2025년 제외
groupby 뒤에 .mean()으로 decadal_group 별로 평균
"""
data = df[~df['decade_group'].isin([191, 195, 203])][['decade_group','MM', 'MMDD', 't_roll']]
									.groupby(['decade_group','MMDD']).mean('MMDD')
data.reset_index(inplace=True)

 

 

- 계절 정의에 따른 계절길이 계산

1. 계절 종료일 계산

str_to_datetime() 함수는 MMDD 칼럼을 datetime 타입으로 만들기 위한 함수로 datetime 타입으로 변환하기위해서는 연도값이 필요합니다.

제 코드에서 보면 2000, 2001로 연도를 지정하고 있는데 특별한 의미가 있어서 설정한 숫자는 아닙니다.

 

winter_end, 즉 겨울의 종료일은 2000, 2001로 두 개를 만들어 쓰고 있는데요.

겨울의 길이는 겨울의 종료일와 가을의 종료일의 차이인데 겨울의 종료일은 다음 연도가 되므로 2000년과 1년 뒤인 2001년 두 개를 만듭니다.

 

spring_end와 winter_end를 구할 때는 data['MM'] <= 4과 data['MM'] <=8과 같이 월별 기준을 사용합니다.

winter_end_2000을 구하는 코드 라인을 보면 groupby 이후 last()라는 기능을 써서 마지막 날을 선택하는 것인데 만약 data['MM'] <= 4, 즉 4월 이하라는 조건이 없으면 여름 이후의 5도 이하인 날이 잡힙니다. 여름 전의 5도 이하인 날을 추출하려고 이러한 조건을 사용한 것입니다.

전 서울 관측소 데이터를 확인해보니 5월부터 5도 이하인 날이 없어서 4월이라는 명시적 기준을 설정했지만 다른 지역의 계절길이를 구할 때는 4월이 아니라 다른 월을 써야할 수도 있습니다.

마찬가지로 spring_end를 구할 때 8월 이하 조건도 서울 관측소 데이터를 확인하고 정한겁니다.

 

def str_to_datetime(year, df_series):
    return pd.to_datetime(str(year) + '-' + df_series, format='%Y-%m-%d')
"""
winter_end: 겨울 종료일
spring_end: 봄 종료일
summer_end: 여름 종료일
fall_end: 가을 종료일
"""
winter_end_2000 =str_to_datetime(2000, data[(data['MM']<=4) & (data['t_roll'] < 5)]
                                 .groupby(['decade_group']).last()['MMDD'])
                                 
winter_end_2001 = str_to_datetime(2001, data[(data['MM']<=4) & (data['t_roll'] < 5)]
                                  .groupby(['decade_group']).last()['MMDD'])

spring_end = str_to_datetime(2000, data[(data['MM']<=8) & (data['t_roll'] < 20)]
                             .groupby(['decade_group']).last()['MMDD'])

summer_end = str_to_datetime(2000, data[data['t_roll']>=20]
                             .groupby('decade_group').last()['MMDD'])

fall_end = str_to_datetime(2000, data[(data['MM'] >= 9) & (data['t_roll']>=5)]
                           .groupby('decade_group').last()['MMDD'])

 

 

2. 계절길이 계산

봄의 길이는 '봄의 종료일 - 봄의 시작일 + 하루'니까 '봄의 종료일 - 겨울의 종료일'과 같습니다.

마찬가지 방법으로 여름, 가을, 겨울의 길이를 저장하는 pandas DataFrame을 생성합니다.

df_duration = pd.DataFrame({
    'spring': spring_end - winter_end_2000,
    'summer': summer_end - spring_end,
    'fall': fall_end - summer_end,
    'winter': winter_end_2001 - fall_end
})
df_duration.reset_index(inplace=True)

 

- 계절길이 변화(간단한 시각화)

간단하게 계절길이가 얼마나 변했는지 시계열로 확인해봅시다.

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(10, 3))
ax = fig.add_subplot()
x = df_duration['decade_group']
colors = [
    (244/255, 180/255, 210/255),  # spring
    (203/255, 216/255, 91/255),   # summer
    (255/255, 218/255, 71/255),   # fall
    (167/255, 201/255, 201/255)   # winter
]

seasons = ['spring', 'summer', 'fall', 'winter']

for season, color in zip(seasons, colors):
    ax.plot(x[0:3], df_duration[season].dt.days[0:3], color=color, label=season, lw=3)
    ax.plot(x[3:], df_duration[season].dt.days[3:], color=color,  lw=3)
plt.legend(loc='lower left')
ax.set_xlabel('decadal_group')
ax.set_ylabel('days')

 

위에서 한 번 설명하긴 했지만 decadal_group이 192면 1915년부터 1924년과 같이 decadal*10은 -5년부터 +4년까지의 기간을 의미합니다.

겨울길이는 1900년대 초반에 130일 즈음 되었는데 지금은 100일 정도입니다.

반대로 여름길이는 1900년대 100일 정도 되었는데 이젠 130일이네요.

봄, 가을길이에는 큰 변화가 없습니다.

서울 관측소만 봐도 겨울이 짧아지고 여름이 길어졌습니다.

 

이번 포스트에서는 간단하게 시각화를 했지만 다음 포스트에서는 기후정보포털에 나온 그림을 그려보겠습니다.

'대기과학 > 프로그래밍' 카테고리의 다른 글

[python]서울의 여름은 얼마나 길어졌을까?: 3. 계절 길이 시각화, 누적막대그래프  (0) 2025.06.15
[python]서울의 여름은 얼마나 길어졌을까?: 2. 계절 길이 시각화, 도넛 차트(파이 차트)  (1) 2025.06.12
[python] 스마트서울 도시데이터 센서(S-DoT)로 보는 집중호우 발생시 온도장: 2. 온도장 확인 및 레이더 에코와 비교  (0) 2025.01.08
[python] 스마트서울 도시데이터 센서(S-DoT)로 보는 집중호우 발생시 온도장: 1. 자료 전처리  (0) 2025.01.07
[python, html/css] 안개 속보 화면 만들기: 4. html 파일 만들기  (1) 2024.12.09
'대기과학/프로그래밍' 카테고리의 다른 글
  • [python]서울의 여름은 얼마나 길어졌을까?: 3. 계절 길이 시각화, 누적막대그래프
  • [python]서울의 여름은 얼마나 길어졌을까?: 2. 계절 길이 시각화, 도넛 차트(파이 차트)
  • [python] 스마트서울 도시데이터 센서(S-DoT)로 보는 집중호우 발생시 온도장: 2. 온도장 확인 및 레이더 에코와 비교
  • [python] 스마트서울 도시데이터 센서(S-DoT)로 보는 집중호우 발생시 온도장: 1. 자료 전처리
레까
레까
  • 레까
    데이터 조아
    레까
  • 전체
    오늘
    어제
    • 전체 (102)
      • 일기장 (0)
      • 대기과학 (52)
        • 프로그래밍 (47)
        • 개념 (2)
        • 칼럼 (3)
      • 여러가지 데이터 (5)
        • 프로그래밍 & 분석 (5)
      • 프로그래밍 (19)
        • 파이썬 (8)
        • 시각화 (10)
        • 유용 (1)
      • 프로젝트 (21)
        • 기계학습 기반 서울 기온 예측 (9)
        • 사과게임 매크로 만들기 (4)
        • 버스 한 번으로 특정 지역에 갈 수 있는 지역 찾.. (4)
        • 메이플스토리 챌린저스 월드 시즌 1 분석 (4)
      • 데이터리안 SQL 공부 (4)
      • 주제별 링크 모음 (1)
      • 백업 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
레까
[python]서울의 여름은 얼마나 길어졌을까?: 1. 계절 길이 계산 코드
상단으로

티스토리툴바