Are downpours getting more intense, even if the yearly total isn't changing?
Draw a rectangle to pick your area of interest, then see what NASA data covers it (live, here in your browser) or download a ready-to-run notebook with your AOI pre-filled. The notebook runs in any Python environment — it needs a free Earthdata Login to fetch the data.
72.6, 18 → 73.6, 19.6 (Mumbai & the Konkan coast)Are downpours getting more intense, even if the yearly total isn’t changing?
What you can answer
- Annual maximum 1-day and 5-day rainfall (Rx1day, Rx5day) per year
- Heavy-day contribution — R95p / R99p totals and their share of annual rain
- Simple daily intensity (SDII: mean rain per wet day)
- Trend and significance via Theil–Sen slope + Mann–Kendall test
- Intensity-vs-total decoupling — whether extremes rise while the annual sum stays flat
What you can NOT answer with these alone
- True absolute extreme magnitudes — IMERG biases the heaviest rates low; gauges or radar are needed for design-value rainfall.
- Sub-pixel cloudburst events — a localized cloudburst can sit inside one ~10 km pixel and be smoothed out; the half-hourly product and gauges resolve more.
- Cause attribution — these indices describe change, not whether it is anthropogenic vs natural variability (that needs detection-attribution methods).
- Short-record significance — ~25 years of IMERG limits power to detect weak trends; treat marginal Mann–Kendall results cautiously.
Code template
import earthaccess
import xarray as xr
import numpy as np
import pandas as pd
# pip install pymannkendall (Theil–Sen slope + Mann–Kendall test)
import pymannkendall as mk
earthaccess.login(strategy="netrc")
# AOI: Mumbai & the Konkan coast
aoi = (72.6, 18.0, 73.6, 19.6)
years = range(2001, 2026)
wet_day = 1.0 # mm/day threshold defining a "wet day"
def annual_indices(daily):
"""daily: 1-D pandas Series of AOI-mean mm/day for one calendar year."""
rx1 = daily.max() # Rx1day
rx5 = daily.rolling(5).sum().max() # Rx5day
wet = daily[daily >= wet_day]
sdii = wet.mean() # simple daily intensity
p95 = wet.quantile(0.95); p99 = wet.quantile(0.99) # baseline percentiles*
r95p = wet[wet > p95].sum() # R95p total
r99p = wet[wet > p99].sum() # R99p total
return dict(Rx1day=rx1, Rx5day=rx5, SDII=sdii, R95p=r95p, R99p=r99p)
# *For strict ETCCDI, fix p95/p99 from a single baseline period, not per-year.
records = {}
for yr in years:
granules = earthaccess.search_data(
short_name="GPM_3IMERGDF",
bounding_box=aoi,
temporal=(f"{yr}-01-01", f"{yr}-12-31"),
)
# files = earthaccess.open(granules)
# ds = xr.open_mfdataset(...) # var: precipitation (mm/day in V07)
# daily = ds.precipitation.sel(...).mean(dim=["lon","lat"]).to_series()
# records[yr] = annual_indices(daily)
df = pd.DataFrame(records).T # rows = years, cols = indices
# Trend test per index
for col in df.columns:
res = mk.original_test(df[col].values)
print(col, "slope/yr:", round(res.slope, 3),
"p:", round(res.p, 3), "->", res.trend)
Expected output
- Per-year index table: Rx1day, Rx5day, SDII, R95p, R99p (with units stated)
- Trend lines: each index vs year with the Theil–Sen slope overlaid
- Trend summary: Mann–Kendall p-value and direction per index
- Decoupling panel: annual total (often flat) plotted against Rx1day / R99p (rising)
- Heavy-day share: % of annual rain from days above the 95th/99th percentile, per year
Caveats
- IMERG underestimates extremes — heaviest rain rates are biased low vs gauges, especially in convective monsoon downpours; cross-check CHIRPS / IMD gauges.
- Define every index — Rx1day, R95p, etc. are only interpretable with their definitions and the percentile baseline stated. Fix the baseline period for strict ETCCDI comparability.
- Western Ghats orographic bias — coastal/orographic rain along the Konkan is systematically under-sensed by satellite precipitation.
- Record length — ~25 years limits trend detectability; extend pre-2000 with TRMM only with care for inter-product inhomogeneity.
- V07 is current; V06 deprecated — reprocess cached pre-2023 data. IMERG Final has ~3.5-month latency; Late/Early trade accuracy for timeliness.
Cross-DAAC composition
GES DISC only for IMERG — single DAAC, single auth. CHIRPS (UCSB/CHC) and gauge data come from outside Earthdata and are optional cross-checks.
Sources
- GPM IMERG: https://gpm.nasa.gov/data/imerg
- IMERG V07 release notes: https://gpm.nasa.gov/sites/default/files/2023-07/IMERG_V07_ReleaseNotes_230713-signed.pdf
- ETCCDI extreme climate indices: https://etccdi.pacificclimate.org/list_27_indices.shtml
- CHIRPS: https://www.chc.ucsb.edu/data/chirps
- Mann–Kendall / Theil–Sen (
pymannkendall): https://pypi.org/project/pymannkendall/
Make it yours → Set your AOI and the years; the notebook computes the standard extreme indices (Rx1day, Rx5day, R95p, R99p, SDII) and runs Theil–Sen + Mann–Kendall on each. Choose the percentile baseline period and swap IMERG for CHIRPS to check whether the trend is robust across products.
The robust trend (Theil–Sen + Mann–Kendall) at the heart of this question — runnable on synthetic data, right here. The full earthaccess code template further down does it on real NASA data (needs an Earthdata login).