기상·기후 데이터의 기초

먼저 읽을 장 15.2 저장 형식과 대용량 데이터

기상·기후 데이터는 겉보기에 평범한 다차원 배열이다. (시간, 고도, 위도, 경도) 축에 숫자가 채워져 있고, NumPy로 평균을 내면 숫자가 하나 나온다. 그러나 그 숫자가 맞으려면 배열 밖의 정보를 알아야 한다. 이 배열이 관측인지 모델 출력인지, 격자 칸 하나가 지구 표면에서 얼마만 한 면적인지, 강수가 한 시간 동안의 양인지 예보 시작부터 쌓인 양인지, 경도가 0–360인지 -180–180인지. 이 정보를 놓치면 코드는 오류 없이 돌아가고 결과는 틀린다.

이 장은 지구·기후 부의 나머지 장(데이터 기반 예보, 다운스케일링, 평가)이 모두 기대는 기초를 다룬다. 저장 형식 자체는 15.2장에서 보았으므로, 여기서는 그 형식 안에 담긴 과학적 의미를 읽는 법, 즉 데이터의 종류, 구면 격자, 변수와 단위, 기후값과 편차에 집중한다. 방법론보다 판단이 중심이다. 어떤 평균이 물리적으로 의미 있는지, 어떤 baseline이 공정한지를 데이터 단계에서 정한다.

학습 목표

  • 관측·재분석·모델 출력 데이터를 구분한다
  • 구면 격자 데이터를 면적 가중과 좌표 관례에 맞게 다룬다
  • 변수의 단위·누적 방식·결측을 확인하고 기후값과 편차를 계산한다

데이터의 종류

왜 필요한가. "기온 데이터"라는 말은 서로 성질이 전혀 다른 세 가지를 가리킬 수 있다. 무엇을 정답으로 삼고 무엇을 입력으로 쓸지는 데이터의 종류에 따라 달라진다. 모델 출력으로 학습하고 같은 모델 출력으로 평가하면, 학습한 것은 대기가 아니라 그 모델이다.

관측(observation). 지상 관측소의 기온·기압·강수, 라디오존데(radiosonde)의 연직 분포, 위성의 복사 휘도(radiance), 레이더 반사도 같은 직접 측정이다. 측정 위치와 시각이 불규칙하고, 관측소는 육지와 인구 밀집 지역에 몰려 있다. 위성은 넓은 영역을 보지만 기온이나 습도를 직접 재는 것이 아니라 여러 고도의 정보가 섞인 복사량을 잰다. 관측에는 기기 교체, 관측소 이전, 관측 시각 변경에 따른 불연속이 있어, 긴 기간의 추세를 볼 때는 균질화(homogenization)된 자료인지 확인한다.

재분석(reanalysis). 과거의 관측을 하나의 고정된 수치 예보 모델과 자료동화(data assimilation)에 넣어, 관측이 없는 곳까지 채운 시공간 격자다. 자료동화는 모델의 짧은 예보(배경장)와 관측을 각각의 오차 크기에 따라 결합해 가장 그럴듯한 대기 상태(분석장)를 만드는 절차다(27.2장). 결과는 빈틈없는 격자이고, 바람·기온·기압이 물리적으로 대체로 일관된다. 대신 관측이 드문 곳(과거의 남반구 해양, 극지방)과 직접 관측되지 않는 변수(강수, 증발, 지표 플럭스)에서는 모델의 성질을 많이 물려받는다. 위성 관측이 들어오기 시작한 시기처럼 관측 체계가 바뀌는 시점에는 재분석에도 불연속이 생길 수 있다.

수치 예보·기후 모델 출력. 수치 예보(numerical weather prediction, NWP) 모델은 현재 상태(분석장)에서 출발해 며칠에서 몇 주 뒤까지의 대기를 예측한다. 기후 모델은 온실가스 농도 같은 외부 강제력(forcing)의 시나리오를 넣고 수십에서 수백 년을 적분한다. 기후 모델 출력의 특정 날짜는 실제 그날의 날씨와 대응하지 않는다. 기후 모델이 맞히려는 것은 날씨의 순서가 아니라 통계(평균, 변동성, 극한의 분포)다. 그래서 기후 모델 출력과 관측을 날짜별로 짝지어 RMSE를 내는 것은 의미가 없고, 분포끼리 비교해야 한다(27.3장, 27.4장).

종류 공간 정답으로서의 성질 흔한 함정
관측 불규칙한 점·궤도 그 위치에서는 기준 공간 대표성, 관측 체계 변화
재분석 규칙 격자, 빈틈없음 관측으로 제약한 모델 추정치 관측이 드문 곳은 모델 의존
예보 모델 출력 규칙 격자 예측값(정답 아님) 같은 모델의 분석장으로 평가하면 유리
기후 모델 출력 규칙 격자 시나리오 아래의 통계 날짜별 비교는 무의미

연구에서의 의미. 데이터 기반 예보 모델은 대개 재분석으로 학습하고 재분석으로 평가한다. 이 경우 모델이 배운 것은 "재분석이 그리는 대기"이고, 재분석의 체계적 오차는 그대로 상속된다. 관측소 자료로 따로 검증하면 이런 차이가 드러난다. 수치 예보 모델과 비교할 때도 정답을 어느 분석장으로 잡는지에 따라 유불리가 생긴다. 어떤 모델이든 자기 분석장에 가까운 쪽이 짧은 리드 타임에서 유리하다. 평가표에는 정답 자료의 출처를 반드시 적는다.

격자와 좌표

왜 필요한가. 지구는 구이고 배열은 직사각형이다. 위경도 격자는 이 둘을 가장 단순하게 잇지만, 그 대가로 칸마다 면적이 다르다. 이 사실 하나를 놓치면 전구 평균, RMSE, 학습 손실이 모두 극지방 쪽으로 기운다.

정의. 규칙적인 위경도 격자(regular latitude–longitude grid)는 위도 간격 Δφ\Delta\varphi, 경도 간격 Δλ\Delta\lambda가 일정한 격자다. 반지름 RR인 구에서 위도 φ1\varphi_1과 φ2\varphi_2 사이, 경도 폭 Δλ\Delta\lambda인 칸의 면적은

A=R2 Δλ (sin⁡φ2−sin⁡φ1)=2R2 Δλ cos⁡φ sin⁡Δφ2A = R^2\,\Delta\lambda\,(\sin\varphi_2 - \sin\varphi_1) = 2R^2\,\Delta\lambda\,\cos\varphi\,\sin\tfrac{\Delta\varphi}{2}

이다. 둘째 등호의 φ\varphi는 칸 중심의 위도다. 모든 칸에서 Δφ\Delta\varphi와 Δλ\Delta\lambda가 같으므로 칸의 면적은 cos⁡φ\cos\varphi에 정확히 비례한다. 그래서 면적 가중 평균은

xˉ=∑i,jcos⁡φi xij∑i,jcos⁡φi\bar{x} = \frac{\sum_{i,j} \cos\varphi_i\, x_{ij}}{\sum_{i,j} \cos\varphi_i}

로 쓴다. ii는 위도, jj는 경도 색인이다. 격자가 극점 자체를 격자점으로 포함하는 경우(φ=±90°\varphi = \pm 90°)에는 cos⁡φ=0\cos\varphi = 0이 되어 극점의 값이 평균에서 빠진다. 이런 격자에서는 격자점을 둘러싼 칸의 실제 면적으로 가중치를 다시 계산하거나, 자료 제공처가 권하는 가중치를 쓴다.

직관. 적도의 1° × 1° 칸은 한 변이 약 111 km인 정사각형에 가깝다. 위도 60°에서는 경도 방향 폭이 절반으로 줄고, 극에 가까우면 폭이 거의 0인 가는 쐐기가 된다. 위도마다 경도 칸 수는 같으므로, 단순 평균은 이 가는 쐐기들에 적도의 넓은 칸과 같은 표를 준다.

극이 차갑고 적도가 따뜻한 기온장 T(φ)=300−45sin⁡2φT(\varphi) = 300 - 45\sin^2\varphi [K]로 확인한다. 구면 위의 면적 평균은 ⟨sin⁡2φ⟩=1/3\langle\sin^2\varphi\rangle = 1/3이므로 300−15=285300 - 15 = 285 K다.

import numpy as np

lat_deg = np.arange(-87.5, 90, 5.0)                  # (36,) 격자 칸 중심 위도
lon_deg = np.arange(0, 360, 5.0)                     # (72,) 격자 칸 중심 경도
LAT = np.deg2rad(lat_deg)[:, None]                   # (36, 1)
temp_k = np.repeat(300.0 - 45.0 * np.sin(LAT) ** 2, lon_deg.size, axis=1)  # (36, 72) 극이 차가운 기온장

w = np.cos(LAT) * np.ones_like(temp_k)               # (36, 72) 면적에 비례하는 가중치
print(f"단순 평균      {temp_k.mean():.2f} K")
print(f"cos 가중 평균  {(w * temp_k).sum() / w.sum():.2f} K   (해석해 300 - 45/3 = 285 K)")

polar = np.abs(lat_deg) > 60                         # (36,) 위도 60도보다 극 쪽
print(f"극 쪽 칸 비율: 개수 {polar.mean():.3f}  면적 {(w[polar].sum() / w.sum()):.3f}"
      f"  (해석해 1 - sin 60° = {1 - np.sin(np.pi / 3):.3f})")
단순 평균      277.50 K
cos 가중 평균  284.99 K   (해석해 300 - 45/3 = 285 K)
극 쪽 칸 비율: 개수 0.333  면적 0.134  (해석해 1 - sin 60° = 0.134)

단순 평균은 7.5 K 낮다. 위도 60°보다 극 쪽의 칸은 개수로 전체의 1/3이지만 면적으로는 13.4%뿐이다. 가중 평균의 284.99 K와 해석해 285 K의 차이는 5° 격자로 적분을 근사한 이산화 오차다.

좌표 관례. 같은 위경도 격자도 저장 방식은 자료마다 다르다. 실제로 자주 걸리는 것은 다음 세 가지다.

  • 경도 범위. 0–360과 -180–180 두 관례가 공존한다. 영역을 자를 때 조건을 다른 관례로 쓰면 날짜변경선이나 본초 자오선을 가로지르는 영역이 잘린다.
  • 위도 순서. 일부 자료는 위도를 북에서 남으로(90 → -90) 저장한다. 위도가 증가한다고 가정한 슬라이싱(lat[10:20])이나 보간 함수가 조용히 틀린다. 색인이 아니라 좌표값으로 고른다.
  • 결측 표시. 해수면 온도의 육지, 지형 아래로 들어간 기압면의 값은 결측이다. NetCDF의 CF 관례(Climate and Forecast conventions)에서는 _FillValue·missing_value 속성으로 결측을 표시하므로, 읽는 도구가 이를 NaN으로 바꿨는지 확인한다.
lon180 = np.where(lon_deg >= 180, lon_deg - 360, lon_deg)   # (72,) [-180, 180)로 변환
naive = (lon_deg >= -10) & (lon_deg <= 30)          # 0–360 좌표에 -180 기준 조건을 그대로 씀
box = (lon180 >= -10) & (lon180 <= 30)
print("경도 10°W–30°E 칸 수: 변환 없이", naive.sum(), " 변환 후", box.sum())

sst_k = temp_k.copy()                                # (36, 72) 해수면 온도처럼 육지를 결측으로
land = (lat_deg[:, None] > 0) & (lat_deg[:, None] < 70) & (lon180[None, :] > -10) & (lon180[None, :] < 140)
sst_k[land] = np.nan
ok = ~np.isnan(sst_k)
print(f"np.mean            {np.mean(sst_k)}")
print(f"결측 0으로 채움    {(w * np.nan_to_num(sst_k)).sum() / w.sum():.2f} K")
print(f"유효 칸 가중 평균  {(w * sst_k)[ok].sum() / w[ok].sum():.2f} K")
경도 10°W–30°E 칸 수: 변환 없이 7  변환 후 9
np.mean            nan
결측 0으로 채움    230.72 K
유효 칸 가중 평균  284.58 K

0–360 좌표에 "-10 이상"이라는 조건을 쓰면 서쪽 두 칸(350°, 355°)이 빠진다. 결측은 세 가지 결과를 낸다. np.mean은 NaN을 퍼뜨려 실수를 바로 드러내므로 오히려 안전하다. 결측을 0으로 채우면 숫자는 나오지만 육지 면적만큼 0 K 쪽으로 끌려가 55 K 가까이 틀린다. 결측 칸의 가중치를 분자와 분모 양쪽에서 빼야 "바다의 면적 평균"이 된다.

연직 좌표와 해상도. 대기 자료의 연직 축은 기압면(예: 850, 500, 250 hPa), 모델 고유의 혼합 좌표(지면 근처에서는 지형을 따르고 위로 갈수록 기압면에 가까워지는 hybrid sigma–pressure 좌표), 고도 중 하나다. 기압면 자료에서는 높은 산지의 850 hPa처럼 지면 아래에 있는 값이 결측이거나 외삽된 값이다. 수평 해상도는 격자 간격으로 표시하지만, 모델이 실제로 표현하는 가장 작은 구조는 격자 간격의 몇 배다. 격자를 촘촘하게 보간했다고 해상도가 올라가지는 않는다.

위경도 격자 외에도 극 근처의 칸 밀집을 피하는 격자들이 있다. 위도 간격이 Gauss 구적점을 따르는 Gauss 격자, 고위도에서 경도 칸 수를 줄인 축소(reduced) Gauss 격자, 정이십면체를 나눈 격자, 정육면체를 구에 투영한 격자(cubed sphere), 칸 면적이 모두 같은 HEALPix 격자 등이다. 이런 격자의 자료를 쓸 때는 칸 면적을 자료가 함께 제공하는지 확인하고, 없으면 격자 정의에서 계산한다.

연구에서의 의미. 학습 손실과 평가 지표에도 같은 가중치를 쓴다. 위경도 격자에서 가중치 없는 MSE로 학습하면, 모델은 면적이 작은 극지방 칸에 지나치게 많은 용량을 쓴다. 데이터 기반 예보 연구는 대개 위도 가중 RMSE로 평가하고, 학습 손실에도 위도 가중을 넣는다(27.2장, 27.4장). 공간 단위로 분할할 때도(15.4장의 공간 블록) 블록의 면적이 같은지 확인한다.

변수와 단위

왜 필요한가. 기상 변수는 이름이 같아도 정의가 다르다. "2 m 기온"과 "850 hPa 기온"은 다른 변수이고, "강수"는 한 시간 합일 수도, 예보 시작부터의 누적일 수도, 순간 강도일 수도 있다. 단위와 누적 방식을 확인하지 않은 변수는 모델의 입력이 될 수 없다.

자주 쓰는 변수.

변수 흔한 단위 확인할 것
기온 K 2 m 기온인지 기압면 기온인지, °C 변환
기압 Pa, hPa 지표 기압인지 해면 기압(sea-level pressure, 해수면 높이로 환산한 값)인지
바람 m/s 동서 성분 uu·남북 성분 vv, 10 m인지 기압면인지
습도 kg/kg, % 비습(specific humidity)인지 상대습도인지
지오퍼텐셜 m²/s² 지위고도(m)로 바꾸려면 표준 중력가속도로 나눈다
강수 m, mm, kg m⁻² s⁻¹ 누적량인지 강도인지, 누적 기간

지오퍼텐셜과 지위고도. 지오퍼텐셜 Φ\Phi는 단위 질량을 해수면에서 그 높이까지 들어 올리는 데 드는 일 [m²/s²]이다. 이를 표준 중력가속도 g0=9.80665g_0 = 9.80665 m/s²로 나눈 Z=Φ/g0Z = \Phi / g_0가 지위고도(geopotential height) [m]다. 500 hPa 지위고도는 중위도 대기의 흐름을 보는 대표 변수라서 데이터 기반 예보의 평가에도 자주 쓰인다(27.2장).

누적량과 순간량. 강수, 복사, 증발처럼 흐름(flux)의 시간 적분으로 저장되는 변수는 누적량(accumulated)이다. 기온, 바람처럼 한 시각의 상태를 저장하는 변수는 순간량(instantaneous)이다. 누적량은 "어디서부터 누적했는가"가 자료마다 다르다. 예보 자료는 흔히 예보 시작 시각부터 누적하고, 시간별로 배포되는 자료는 직전 한 시간을 누적하기도 한다. 예보 시작부터의 누적값이라면 차분해야 기간별 양이 된다.

acc_m = np.array([0.0, 0.0004, 0.0013, 0.0013, 0.0031, 0.0052])  # (6,) 예보 시작부터 누적한 강수 [m], 1시간 간격
rate_mm_h = np.round(np.diff(acc_m) * 1000, 2)       # (5,) 각 1시간 동안의 강수 [mm/h]
print("시간별 강수 mm/h", rate_mm_h)
print("5시간 합 mm     ", round(acc_m[-1] * 1000, 2))

g0_m_s2 = 9.80665                                    # 표준 중력가속도
phi_m2_s2 = 54_915.0                                 # 500 hPa 지오퍼텐셜 [m^2/s^2]
print(f"지위고도 {phi_m2_s2 / g0_m_s2:.0f} m   기온 {253.15 - 273.15:.1f} °C")
시간별 강수 mm/h [0.4 0.9 0.  1.8 2.1]
5시간 합 mm      5.2
지위고도 5600 m   기온 -20.0 °C

강수를 물의 깊이 [m]로 저장하는 자료가 있으므로 1000을 곱해 mm로 바꿨다. 물 1 mm는 1 m² 위의 물 1 kg과 같아서, 강수 플럭스 kg m⁻² s⁻¹에 3600을 곱하면 mm/h가 된다. 누적값을 그대로 시간별 강수로 착각하면 마지막 시각의 "강수" 5.2 mm/h가 실제 2.1 mm/h의 두 배를 넘는다.

연구에서의 의미. 데이터 기반 모델의 입력 채널은 수십 개의 변수·기압면 조합이다. 채널마다 단위·누적 방식·결측 규칙을 표로 남기지 않으면, 학습과 추론에서 같은 채널이 다른 의미로 들어가는 버그가 생긴다. 15.5장의 데이터 카드에 이 표를 포함한다.

저장 형식과 도구

왜 필요한가. 전 지구 0.25° 격자의 한 변수 한 시각은 약 100만 개 값이다. 여기에 수십 년의 시간별 자료와 수십 개의 기압면이 곱해지면 메모리에 올릴 수 없는 크기가 된다. 형식과 도구는 "필요한 부분만, 좌표와 단위를 잃지 않고" 읽기 위해 있다.

NetCDF와 Zarr. 15.2장에서 본 것처럼 기상·기후 자료의 표준 형식은 NetCDF다. 변수 배열과 함께 차원 이름, 좌표 변수(위도·경도·시간), 단위·결측값 같은 속성을 담는다. CF 관례는 이 속성의 이름과 의미를 정해, 서로 다른 기관의 자료를 같은 도구로 읽게 한다. 클라우드 객체 저장소에 둔 큰 격자에는 chunk를 객체 하나씩 저장하는 Zarr가 많이 쓰인다. 수치 예보 기관의 원자료는 GRIB 형식으로 배포되는 경우가 많다.

xarray. xarray는 이름 붙은 차원과 좌표를 가진 다차원 배열을 다루는 Python 라이브러리다. 축 번호 대신 "lat", "time" 같은 이름으로 연산하고, 좌표값으로 고르고, 단위 속성을 함께 들고 다닌다. 위의 계산은 xarray로 다음과 같이 쓴다(집필 시점 예시. 함수 이름과 인자는 설치한 버전의 문서에서 확인한다).

import numpy as np
import xarray as xr

ds = xr.open_dataset("t2m.nc")                      # Zarr면 xr.open_zarr(...)
t2m = ds["t2m"]                                     # (time, latitude, longitude)
print(t2m.attrs.get("units"), t2m.latitude.values[:3])   # 단위와 위도 순서 확인

w = np.cos(np.deg2rad(t2m.latitude))
gmean = t2m.weighted(w).mean(("latitude", "longitude"))  # (time,) 결측은 가중치에서 빠진다
clim = t2m.sel(time=slice("1991", "2020")).groupby("time.month").mean("time")
anom = t2m.groupby("time.month") - clim             # 월별 편차

대용량 접근. 메모리보다 큰 자료는 chunk 단위로 지연 계산(lazy evaluation)한다. xarray는 Dask와 함께 쓰면 위 코드를 그대로 두고 chunk별로 병렬 계산한다. chunk 모양은 읽는 방식에 맞춘다. 한 시각의 전 지구 지도를 주로 읽으면 (시간 1, 위도 전체, 경도 전체), 한 지점의 긴 시계열을 주로 읽으면 (시간 전체, 위도 소수, 경도 소수)가 유리하다(15.2장). 학습용 data loader가 무작위 시각을 뽑아 전 지구 지도를 읽는다면 앞의 모양이 맞다.

기후값과 편차

왜 필요한가. 기온의 변동 대부분은 계절과 하루의 주기다. 7월이 1월보다 덥다는 것을 맞히는 모델은 아무것도 예측하지 않은 것이다. 예측과 진단의 관심은 "평년보다 얼마나 다른가"에 있다.

정의. 기후값(climatology)은 기준 기간에서 같은 달(또는 같은 날짜, 같은 시각)끼리 모은 평균이다. 세계기상기구가 쓰는 30년 기준 기간(예: 1991–2020)이 흔하다. 편차(anomaly)는 각 값에서 그 시기의 기후값을 뺀 것이다. 월 mm의 기후값을 cmc_m이라 하면

cm=1∣Bm∣∑t∈Bmxt,xt′=xt−cm(t)c_m = \frac{1}{|B_m|}\sum_{t \in B_m} x_t, \qquad x'_t = x_t - c_{m(t)}

이다. BmB_m은 기준 기간 중 달이 mm인 시점의 집합, m(t)m(t)는 시점 tt의 달이다. 기준 기간 안에서 편차의 평균은 정의상 0이다.

rng = np.random.default_rng(0)
years = np.arange(1981, 2021)                        # (40,)
month = np.tile(np.arange(12), years.size)           # (480,) 0 = 1월
year = np.repeat(years, 12)                          # (480,)
t2m_c = (12 - 10 * np.cos(2 * np.pi * (month + 0.5) / 12)   # 계절 주기
         + 0.03 * (year - 1981)                      # 추세 0.03 °C/년
         + rng.normal(0, 0.8, year.size))            # 날씨 잡음

base = (year >= 1991) & (year <= 2020)               # 기준 기간 1991–2020
clim = np.array([t2m_c[base & (month == m)].mean() for m in range(12)])  # (12,)
anom = t2m_c - clim[month]                           # (480,)
print("1월·7월 기후값", np.round(clim[[0, 6]], 2))
print(f"표준편차: 원자료 {t2m_c.std():.2f}  편차 {anom.std():.2f}")
print("기준 기간 편차 평균 = 0:", abs(anom[base].mean()) < 1e-8)
print(f"1981–1990 편차 평균 {anom[~base].mean():+.3f} °C")
1월·7월 기후값 [ 3.37 22.21]
표준편차: 원자료 7.15  편차 0.86
기준 기간 편차 평균 = 0: True
1981–1990 편차 평균 -0.488 °C

편차의 표준편차(0.86)는 원자료(7.15)의 8분의 1 수준이다. 원자료의 변동 대부분이 계절 주기였다는 뜻이다. 월별 기후값은 달마다 상수 하나라서 계절 주기만 빼고 장기 추세는 남긴다. 기준 기간 이전의 1980년대 편차가 평균 -0.49 °C인 것이 그 흔적이다.

baseline으로서의 기후값. 기후값은 가장 기본적인 예측 baseline이기도 하다. "내일 기온은 그 날짜의 평년값"이라는 예측이다. 27.2장에서 데이터 기반 예보를 이 baseline과 지속성(persistence) baseline에 비교하고, 27.4장의 anomaly correlation coefficient(ACC)는 편차끼리의 상관이다. 이때 기후값은 학습 기간에서만 계산한다. 시험 기간을 포함한 기후값은 시험 기간의 평균을 미리 알려 주는 누수다(15.4장).

연구에서의 의미. 편차를 예측 대상으로 쓰면 모델이 계절 주기를 외우는 데 용량을 쓰지 않는다. 반대로 편차로 학습한 모델의 출력을 원래 값으로 돌릴 때는 같은 기후값을 더해야 하고, 그 기후값은 학습 기간의 것이어야 한다. 기후값을 일 단위로 만들 때는 같은 날짜의 표본이 30개뿐이라 잡음이 크므로, 이웃 날짜를 함께 평균하거나 몇 개의 조화 함수로 평활한다.

실습

재분석 자료의 한 변수(예: 2 m 기온의 월평균)를 내려받아 면적 가중 전구 평균과 기후값을 계산한다. 재분석 자료는 배포처에 따라 회원 가입과 사용 약관 동의가 필요하므로, 절차는 배포처의 안내를 따른다.

  1. 데이터 카드를 먼저 쓴다. 출처(데이터셋 이름·버전·배포처), 기간, 격자(해상도, 위도 순서, 경도 범위), 변수 이름과 단위, 누적 방식, 결측 표시를 표로 적는다. ds.attrs와 변수의 attrs에서 읽은 값을 그대로 옮긴다.
  2. 위도 가중 전구 평균 시계열을 계산하고, 같은 자료의 단순 평균과 차이를 기록한다.
  3. 기준 기간(예: 1991–2020)의 월별 기후값과 월별 편차를 계산한다. 편차의 연평균 시계열과 그 선형 추세를 기록한다(추세의 불확실성은 27.4장에서 다룬다).
  4. 육지·바다 마스크를 적용해 바다만의 평균을 계산하고, 결측 처리 방법에 따라 값이 어떻게 달라지는지 기록한다.

먼저 두 연습문제로 핵심 계산을 구현한다. 테스트가 바로 채점한다.

실패 사례

  • 위경도 격자에서 면적 가중 없이 평균을 내는 것. 극지방이 과대 대표된다. 기온처럼 위도에 따라 크게 변하는 변수에서는 수 K의 편향이 생기고, RMSE나 학습 손실에서도 같은 일이 일어난다.
  • 결측을 0으로 채우고 평균하는 것. NaN을 피하려다 물리적으로 의미 없는 값을 넣는다. 결측 칸은 가중치에서 뺀다.
  • 경도 관례·위도 순서를 확인하지 않고 영역을 자르는 것. 오류 없이 잘못된 영역이 선택된다. 좌표값으로 고르고, 자른 뒤 좌표 범위를 출력해 확인한다.
  • 누적량을 순간량으로 다루는 것. 예보 시작부터 누적한 강수를 시간별 강수로 착각하면 리드 타임에 따라 강수가 커지는 가짜 추세가 생긴다.
  • 재분석을 관측으로 부르는 것. 관측이 드문 지역에서 재분석과 모델이 잘 맞는 것은 둘 다 모델이기 때문일 수 있다.

통과 기준

  • 사용한 기상 데이터셋의 출처·격자·변수·단위·누적 방식·결측 표시를 표로 문서화한다.
  • 위도 가중 전구 평균과 단순 평균의 차이, 기준 기간 기후값과 월별 편차를 계산하고 기준 기간 편차 평균이 0임을 확인한다.
  • 연습문제 두 개(lat-weighted-mean, monthly-anomaly)의 테스트를 통과한다.

참고 문헌

  • Hersbach 외 (2020). The ERA5 global reanalysis. Quarterly Journal of the Royal Meteorological Society, 146(730), 1999–2049. — ERA5 재분석의 구성과 기본 평가