TANGERANG SELATAN WEATHER

Minggu, 10 Mei 2026

Mengunduh dan Memproses ERA5 dengan xarray

Visualisasi grid global ERA5 dengan rentang suhu permukaan dan jaringan observasi

Mengapa Reanalysis Penting untuk Riset Iklim

Jaringan observasi meteorologi tidak pernah benar-benar lengkap. Stasiun cuaca terkonsentrasi di daratan berpenduduk, radiosonde cuma diluncurkan dua kali sehari di lokasi terbatas, dan samudra yang luas hampir sepenuhnya tidak terpantau secara langsung. Akibatnya, setiap analisis iklim yang mengandalkan data observasi mentah selalu menyisakan gap spasial dan temporal yang besar.

Di sinilah reanalysis berperan. Pendekatan ini menggabungkan model NWP dengan observasi historis lewat data assimilation, menghasilkan rekaman state atmosfer global yang lengkap, konsisten secara fisis, dan homogen sepanjang waktu — bukan observasi langsung, melainkan estimasi terbaik kondisi atmosfer dari semua data yang tersedia pada setiap saat.

ERA5 adalah reanalysis generasi kelima dari ECMWF, mencakup Januari 1940 sampai sekarang pada resolusi temporal satu jam dan grid ~31 km global. Buat peneliti data atmosfer Indonesia — pola monsun, extreme weather events, tren suhu jangka panjang — ERA5 hampir wajib jadi titik awal.

Tutorial ini memandu kita dari nol: cara akses ERA5, memahami struktur datanya, dan menguasai operasi xarray yang paling sering dipakai di workflow iklim sehari-hari.

Apa Itu ERA5 dan Bagaimana Cara Kerjanya

ERA5 dibangun di atas Integrated Forecast System (IFS CY41R2) milik ECMWF dengan metode 4D-Var data assimilation. Pada 4D-Var, selisih antara prediksi model dan observasi diminimalkan selama time window 12 jam, sehingga kondisi awal atmosfer yang dihasilkan seoptimal mungkin secara fisis.

Jumlah observasi yang di-assimilate terus naik seiring waktu: dari sekitar 750.000 data per hari pada 1979 jadi sekitar 24 juta data per hari pada 2018. Sumber observasinya mencakup radiosonde, pesawat (AMDAR), satelit cuaca, ocean buoy, sampai GPS radio occultation. Semua jenis observasi ini di-merge jadi satu produk yang koheren.

Beberapa fakta teknis utama ERA5 yang perlu diketahui:

  • Resolusi horizontal: ~31 km (0,25° × 0,25° pada grid interpolasi atmosfer, 0,5° × 0,5° untuk ocean wave) — tersedia di Copernicus CDS.
  • Resolusi vertikal: 137 level dari permukaan sampai ~80 km.
  • Temporal: data per jam, tersedia dari Januari 1940.
  • Lisensi: Creative Commons Attribution 4.0 (CC-BY 4.0) — bebas dipakai untuk riset selama ada atribusi.
  • Format distribusi: GRIB asli, atau NetCDF lewat antarmuka CDS.

Selain ERA5 utama, ada dua varian: ERA5-Land dengan resolusi ~9 km khusus variabel permukaan tanah, dan ERA5T (near-real-time) dengan latensi ~5 hari namun masih sementara. ERA5 menggantikan ERA-Interim yang di-discontinue pada 31 Agustus 2019. Paper referensi utamanya adalah Hersbach et al. (2020).

Pilihan Akses Data ERA5

Ada tiga jalur utama untuk akses ERA5:

1. Portal web Copernicus CDS — kunjungi cds.climate.copernicus.eu, pilih variabel, rentang waktu, dan area, lalu download manual. Cocok untuk eksplorasi awal tanpa nulis kode.

2. CDS API via cdsapi — jalur programatik buat workflow otomatis. Daftar akun CDS, terima lisensi data, simpan API key di ~/.cdsapirc. Detail di How to download ERA5.

3. Cloud mirror dan arsip — beberapa institusi menyediakan ERA5 di platform cloud (misalnya Google Cloud Storage untuk era5 zarr), jadi akses tanpa download file besar dulu.

Snippet berikut menunjukkan bentuk request cdsapi yang khas — versi multi-hari yang lebih besar dari yang nanti benar-benar kita jalankan di bagian akhir artikel ini. Snippet versi pendek (1 hari, 4 timestep) yang beneran dieksekusi muncul di bagian "Mengunduh Data ERA5 Asli" di bawah:

import cdsapi

c = cdsapi.Client()

c.retrieve(
    "reanalysis-era5-single-levels",
    {
        "product_type": "reanalysis",
        "variable": ["2m_temperature"],
        "year": "2024",
        "month": "01",
        "day": [f"{d:02d}" for d in range(1, 8)],
        "time": ["00:00", "06:00", "12:00", "18:00"],
        "area": [6, 95, -11, 141],   # North, West, South, East — Indonesia
        "format": "netcdf",
    },
    "era5_t2m_indonesia_202401.nc",
)

Dataset identifier utamanya adalah reanalysis-era5-single-levels untuk surface variable dan reanalysis-era5-pressure-levels untuk variabel pada pressure level.

Menyiapkan Lingkungan Python

Sebelum mulai, pastikan xarray, NumPy, dan pandas sudah ter-install. Snippet berikut import ketiganya dan print versinya masing-masing sebagai sanity check:

import xarray as xr
import numpy as np
import pandas as pd

print(f"xarray  : {xr.__version__}")
print(f"NumPy   : {np.__version__}")
print(f"pandas  : {pd.__version__}")
xarray  : 2026.4.0
NumPy   : 2.4.4
pandas  : 3.0.2

Kalau versi xarray yang ter-install masih 0.x, pertimbangkan upgrade ke versi 2.x — API selection dan indexing sudah jauh diperbaiki di sana.

Memuat Dataset Latihan untuk Eksplorasi

Sebelum menyentuh data sebenarnya, kita mulai dulu dari Dataset sintetis yang bentuknya mirip ERA5. Pendekatan ini berguna karena snippet-snippet sintetis di sini reproducible — bisa dijalankan offline, di mesin manapun, tanpa API key. Setelah pola-nya kebayang, kita lanjut ke real download ERA5 di bagian "Mengunduh Data ERA5 Asli dengan cdsapi".

xarray menyediakan dua data structure utama: DataArray (labeled N-dim array, mirip pandas.Series) dan Dataset (container mirip dict yang menyimpan beberapa DataArray dengan dimensi yang aligned, mirip pandas.DataFrame). Data model ini diturunkan langsung dari format NetCDF yang dipakai luas di earth science.

np.random.seed(42)

# Koordinat — menyerupai ERA5 area Indonesia
time = pd.date_range("2024-01-01", periods=24, freq="6h")
latitude = np.linspace(-10.0, 10.0, 11)   # 11 titik, berpusat di ekuator
longitude = np.linspace(95.0, 141.0, 24)  # 24 titik, batas barat-timur Indonesia

# Suhu 2 m: gradien lintang tropis + gangguan acak kecil
lat_grid = latitude[np.newaxis, :, np.newaxis]      # shape (1, 11, 1)
t2m_data = (
    273.15 + 28.0
    - 2.0 * np.abs(lat_grid)                        # lebih hangat di ekuator
    + np.random.normal(0, 0.5, (24, 11, 24))        # time, lat, lon
).astype("float32")

ds = xr.Dataset(
    {
        "t2m": xr.DataArray(
            t2m_data,
            dims=["time", "latitude", "longitude"],
            attrs={
                "long_name": "2 metre temperature",
                "units": "K",
                "source": "synthetic ERA5-like tutorial data",
            },
        )
    },
    coords={
        "time": time,
        "latitude": latitude,
        "longitude": longitude,
    },
    attrs={"title": "Synthetic ERA5-like dataset for tutorial purposes"},
)

print(ds)
<xarray.Dataset> Size: 26kB
Dimensions:    (time: 24, latitude: 11, longitude: 24)
Coordinates:
  * time       (time) datetime64[us] 192B 2024-01-01 ... 2024-01-06T18:00:00
  * latitude   (latitude) float64 88B -10.0 -8.0 -6.0 -4.0 ... 4.0 6.0 8.0 10.0
  * longitude  (longitude) float64 192B 95.0 97.0 99.0 ... 137.0 139.0 141.0
Data variables:
    t2m        (time, latitude, longitude) float32 25kB 281.4 281.1 ... 281.2
Attributes:
    title:    Synthetic ERA5-like dataset for tutorial purposes

Dataset yang dihasilkan punya satu variabel t2m (suhu 2 meter dalam Kelvin) dengan tiga dimensi: time (24 timestep × 6 jam = 6 hari), latitude (11 titik), dan longitude (24 titik). Persis bentuk yang akan kita temui pas buka file ERA5 NetCDF aslinya nanti.

Menjelajahi Dimensi, Koordinat, dan Variabel

Sebelum analisis, selalu wise untuk inspect struktur Dataset dulu. Data model xarray membedakan tiga konsep penting: dims (nama dimensi dan ukurannya), coords (nilai koordinat yang menempel pada dimensi), dan data_vars (N-dimensional data variable).

print("=== Dimensi ===")
print(dict(ds.dims))

print("\n=== Koordinat ===")
print("time  :", ds.coords["time"].values[:4], "...")
print("lat   :", ds.coords["latitude"].values)
print("lon   :", ds.coords["longitude"].values[[0, -1]], "(first & last)")

print("\n=== Variabel data ===")
print(list(ds.data_vars))

print("\n=== Atribut variabel t2m ===")
print(ds["t2m"].attrs)

print("\n=== Bentuk array t2m ===")
print("shape:", ds["t2m"].shape, "  dtype:", ds["t2m"].dtype)
=== Dimensi ===
{'time': 24, 'latitude': 11, 'longitude': 24}

=== Koordinat ===
time  : ['2024-01-01T00:00:00.000000' '2024-01-01T06:00:00.000000'
 '2024-01-01T12:00:00.000000' '2024-01-01T18:00:00.000000'] ...
lat   : [-10.  -8.  -6.  -4.  -2.   0.   2.   4.   6.   8.  10.]
lon   : [ 95. 141.] (first & last)

=== Variabel data ===
['t2m']

=== Atribut variabel t2m ===
{'long_name': '2 metre temperature', 'units': 'K', 'source': 'synthetic ERA5-like tutorial data'}

=== Bentuk array t2m ===
shape: (24, 11, 24)   dtype: float32

Perhatikan ds["t2m"] mengembalikan DataArray, bukan array NumPy biasa. Bedanya yang penting: DataArray membawa metadata dimensi dan koordinat, jadi operasi seperti sel() dan resample() bisa bekerja berdasarkan label, bukan integer index.

Operasi Inti — Selection, Aggregation, dan Resampling

Selection dan Spatial Mean

sel() membiarkan kita pilih data langsung pakai nilai koordinat. Untuk pilih area Indonesia bagian barat (Sumatra dan sekitarnya), kita batasi lintang antara -6° sampai 6° dan bujur 95° sampai 108°, lalu hitung mean spasial dan temporalnya:

# Seleksi sub-area Sumatra
sumatera = ds["t2m"].sel(
    latitude=slice(-6.0, 6.0),
    longitude=slice(95.0, 108.0),
)
print("Shape setelah seleksi:", sumatera.shape)

# Rata-rata spasial (mean atas lat dan lon)
spatial_mean = sumatera.mean(dim=["latitude", "longitude"])
print("\nRata-rata spasial per langkah waktu (K):")
print(spatial_mean.values.round(3))

# Rata-rata temporal keseluruhan (skalar)
overall_mean = sumatera.mean().item()
print(f"\nRata-rata keseluruhan: {overall_mean:.3f} K  ({overall_mean - 273.15:.2f} °C)")

# Nilai min dan maks dalam seleksi
print(f"Min: {sumatera.min().item():.3f} K   Max: {sumatera.max().item():.3f} K")
Shape setelah seleksi: (24, 7, 7)

Rata-rata spasial per langkah waktu (K):
[294.243 294.346 294.346 294.46  294.362 294.363 294.325 294.282 294.274
 294.364 294.393 294.399 294.281 294.294 294.207 294.329 294.041 294.219
 294.247 294.239 294.182 294.246 294.257 294.29 ]

Rata-rata keseluruhan: 294.291 K  (21.14 °C)
Min: 287.848 K   Max: 302.437 K

Resampling dan Monthly Aggregation

Reanalysis ERA5 tersedia per jam; di praktiknya kita sering aggregate ke frekuensi yang lebih kasar. resample() bekerja mirip pandas.resample() tapi di atas dimensi waktu xarray:

# Resample dari 6-jam ke harian (rata-rata)
t2m_daily = ds["t2m"].resample(time="1D").mean()
print("Shape harian:", t2m_daily.shape)
print("Rata-rata harian (rata-rata spasial per hari, K):")
daily_spatial = t2m_daily.mean(dim=["latitude", "longitude"])
for t, v in zip(t2m_daily.time.values, daily_spatial.values):
    print(f"  {str(t)[:10]} : {v:.3f} K")

# Groupby bulan — satu bulan di dataset ini (Januari)
print("\nStatistik groupby bulan:")
monthly = ds["t2m"].groupby("time.month").mean()
for m, v in zip(monthly.month.values, monthly.mean(dim=["latitude", "longitude"]).values):
    print(f"  Bulan {m:02d} : {v:.3f} K")
Shape harian: (6, 11, 24)
Rata-rata harian (rata-rata spasial per hari, K):
  2024-01-01 : 290.257 K
  2024-01-02 : 290.262 K
  2024-01-03 : 290.238 K
  2024-01-04 : 290.232 K
  2024-01-05 : 290.232 K
  2024-01-06 : 290.222 K

Statistik groupby bulan:
  Bulan 01 : 290.240 K

Pola resample().mean() dan groupby().mean() ini persis sama dengan yang nanti kita pakai di file ERA5 asli berukuran gigabyte — xarray meng-handle time indexing otomatis tanpa kode tambahan.

Mengunduh Data ERA5 Asli dengan cdsapi

Sekarang giliran the real thing: request data ERA5 langsung dari CDS, download ke file lokal, lalu apply workflow yang sama yang baru kita pelajari di atas. Pastikan API key sudah ada di ~/.cdsapirc sebelum menjalankan snippet ini — sandbox tutorial ini sudah menganggap credential-nya valid.

Snippet pertama kirim request ke CDS untuk variabel 2m_temperature selama satu hari (1 Januari 2024) dengan empat timestep, dibatasi ke wilayah Indonesia. Request bisa nge-queue di server CDS selama puluhan detik sampai beberapa menit; makanya snippet ini diberi timeout=600 biar cukup waktu buat queue + download.

import os
import cdsapi

OUT = "era5_t2m_indonesia_20240101.nc"

if not os.path.exists(OUT):
    client = cdsapi.Client(quiet=True)
    client.retrieve(
        "reanalysis-era5-single-levels",
        {
            "product_type": "reanalysis",
            "variable": ["2m_temperature"],
            "year": "2024",
            "month": "01",
            "day": "01",
            "time": ["00:00", "06:00", "12:00", "18:00"],
            "area": [6, 95, -11, 141],   # N, W, S, E — kotak Indonesia
            "format": "netcdf",
        },
        OUT,
    )

size_kb = os.path.getsize(OUT) / 1024
print(f"Downloaded : {OUT}")
print(f"Size       : {size_kb:.1f} KB")
Downloaded : era5_t2m_indonesia_20240101.nc
Size       : 105.2 KB

Setelah file tersedia, kita buka pakai xarray dan apply operasi yang sama persis dengan yang kita pakai di Dataset sintetis sebelumnya. Satu hal penting: di file ERA5 modern, dimensi latitude di-order dari positif ke negatif (utara ke selatan), jadi argumen slice() ditulis dari nilai lebih besar ke lebih kecil. Snippet di bawah mendeteksi order-nya otomatis biar sama-sama bekerja di konvensi mana pun.

import xarray as xr

ds_era5 = xr.open_dataset(OUT)
print(ds_era5)

# Pilih nama variabel suhu (file ERA5 baru kadang menamainya "t2m",
# kadang prefix lain — kita ambil variabel data pertama jika "t2m" tidak ada).
var_name = "t2m" if "t2m" in ds_era5 else next(iter(ds_era5.data_vars))
t2m = ds_era5[var_name]

# Deteksi urutan latitude
lat = ds_era5["latitude"].values
lat_slice = slice(6.0, -6.0) if lat[0] > lat[-1] else slice(-6.0, 6.0)

sumatera = t2m.sel(latitude=lat_slice, longitude=slice(95.0, 108.0))

mean_K = float(sumatera.mean())
print(f"\nShape setelah seleksi : {sumatera.shape}")
print(f"Rata-rata Sumatra (K) : {mean_K:.3f}")
print(f"Rata-rata Sumatra (°C): {mean_K - 273.15:.2f}")
print(f"Min  : {float(sumatera.min()):.3f} K   Max : {float(sumatera.max()):.3f} K")
<xarray.Dataset> Size: 206kB
Dimensions:     (valid_time: 4, latitude: 69, longitude: 185)
Coordinates:
  * valid_time  (valid_time) datetime64[ns] 32B 2024-01-01 ... 2024-01-01T18:...
    expver      (valid_time) <U4 64B ...
  * latitude    (latitude) float64 552B 6.0 5.75 5.5 5.25 ... -10.5 -10.75 -11.0
  * longitude   (longitude) float64 1kB 95.0 95.25 95.5 ... 140.5 140.8 141.0
    number      int64 8B ...
Data variables:
    t2m         (valid_time, latitude, longitude) float32 204kB ...
Attributes:
    GRIB_centre:             ecmf
    GRIB_centreDescription:  European Centre for Medium-Range Weather Forecasts
    GRIB_subCentre:          0
    Conventions:             CF-1.7
    institution:             European Centre for Medium-Range Weather Forecasts
    history:                 2026-05-09T17:41 GRIB to CDM+CF via cfgrib-0.9.1...

Shape setelah seleksi : (4, 49, 53)
Rata-rata Sumatra (K) : 300.024
Rata-rata Sumatra (°C): 26.87
Min  : 289.247 K   Max : 305.995 K

Inilah indahnya xarray: kode untuk file ERA5 asli hampir identik dengan kode untuk Dataset sintetis. Cuma satu check kecil soal order dimensi latitude yang bedain keduanya — sisanya pola yang sama (sel, mean, resample, groupby) yang sudah kita pelajari.

Untuk dataset ERA5 berskala besar (bulanan atau tahunan), pertimbangkan xr.open_mfdataset() yang membuka banyak file NetCDF sekaligus dan meng-concatenate-nya secara lazy di sepanjang dimensi waktu.

Langkah Lanjut dan Sumber Belajar

Tutorial ini sudah mencakup workflow dasar yang menutupi hampir semua kebutuhan analisis ERA5 sehari-hari: memahami struktur Dataset, lakukan spatial dan temporal selection, dan aggregate data ke frekuensi yang diinginkan.

Langkah alami berikutnya:

  1. Download data ERA5 pertama kita — daftar di cds.climate.copernicus.eu, pasang API key, jalankan snippet cdsapi di bagian akses di atas.
  2. Pelajari xr.open_mfdataset() — untuk analisis multi-tahun, jauh lebih efisien daripada buka file satu-satu.
  3. Eksplorasi API xarray lebih dalaminterp() untuk interpolasi, where() untuk conditional masking, apply_ufunc() untuk custom function NumPy.
  4. Analisis climate anomaly — hitung temperature anomaly dengan groupby("time.month") - climatology.
  5. Baca dokumentasi resmixarray Data Structures dan xarray.tutorial.open_dataset.

Eksplorasi artikel meteorologi lainnya di meteo.my.id — ada panduan ERA5, GFS, analisis BMKG, dan teknik visualisasi atmosfer di https://meteo.my.id.

Referensi

Tidak ada komentar:

Posting Komentar