qin4·intermediate

Are downpours getting more intense, even if the yearly total isn't changing?

hydrologyprecipitationclimatehazards Datasets: 4 10–25 min

Rainfall intensity is about *how* the rain arrives, not just how much. A place can get the same number of millimetres in a year but have it dumped in fewer, more violent bursts instead of spread over many gentle days. This page measures the biggest single-day and multi-day downpours each year and asks whether they're climbing, even when the yearly total stays flat.

Are downpours getting more intense, even if the yearly total isn’t changing?

Rainfall intensity is about how the rain arrives, not just how much. A place can get the same number of millimetres in a year but have it dumped in fewer, more violent bursts instead of spread over many gentle days. This page measures the biggest single-day and multi-day downpours each year and asks whether they’re climbing, even when the yearly total stays flat.

Which data to use, and how

Use this dataset: GPM IMERG daily (short name GPM_3IMERGDF), a satellite rainfall map that gives you one rainfall value per day for every ~10 km square, from 2000 to today. It’s the easiest to work with because it’s already daily, which is the grain you need for “biggest day of the year.” (The half-hourly version, GPM IMERG half-hourly GPM_3IMERGHH, is finer but heavier. Skip it until you specifically need sub-daily bursts.)

Then do four steps (the runnable code further down does all of this; this is just the idea):

  1. Pick your area and years. A small latitude/longitude box, plus the span of years you care about. Average the daily rainfall inside the box so you get one number per day.
  2. Squeeze each year into a few “intensity” numbers. The biggest 1-day total (Rx1day), the biggest 5-day total (Rx5day), the average rain on rainy days (SDII, simple daily intensity), and how much rain fell on the most extreme days (R95p / R99p, the totals from days above the 95th and 99th percentile of wet days).
  3. Stack the years up. You now have a small table: one row per year, one column per intensity number.
  4. Fit a trend and test it. For each intensity number, draw the long-term line (a Theil–Sen slope, which ignores freak outliers) and check it’s real and not noise (a Mann–Kendall test, which just asks “is this mostly going up or mostly going down?”). Rising intensity numbers while the yearly total stays flat means downpours are getting fiercer.

That’s the whole method. Below, “How a scientist answers this” names the exact tools, and the Run it cell does it live on synthetic data so you can see each step work.

What you can find out

  • The biggest 1-day and 5-day rainfall each year (Rx1day, Rx5day).
  • How much of the year’s rain comes from a few extreme days (R95p / R99p totals and their share of the annual rain).
  • The average intensity of a rainy day (SDII: mean rain per wet day).
  • Whether any of these trends are real. The Theil–Sen slope says how fast, the Mann–Kendall test says whether it’s bigger than the noise.
  • The key decoupling: whether the extremes are rising even though the yearly sum is holding steady. That gap is the whole point of the question.

What it can’t tell you

  • The true size of the worst storms. The satellite underestimates the heaviest rain rates, so the absolute numbers run low. The trend (is it going up?) is usable. The absolute design-value rainfall an engineer needs requires ground gauges or radar.
  • Cloudbursts smaller than a pixel. Each value covers a ~10 km square, so a violent, very local cloudburst gets smeared out into the wider average. The half-hourly product and ground gauges see more of it.
  • Why it’s changing. A rising trend doesn’t say whether it’s human-caused warming or just natural year-to-year swings. Separating those needs a different toolkit (detection-attribution methods).
  • Weak trends with confidence. IMERG is only ~25 years old. That’s a short record, so faint trends are hard to prove. Treat borderline (marginal) Mann–Kendall results with caution.

Gotchas to watch for

  • The satellite lowballs the heaviest rain. Compared to ground gauges, IMERG under-measures the most intense downpours, worst during heavy monsoon storms (convective rain), so your absolute extremes will come out too low. The safe move is to report change over time rather than absolute thresholds, and to cross-check against CHIRPS (a gauge-blended rainfall product) or local India Meteorological Department (IMD) gauges.
  • An index means nothing without its definition. “Rx1day” or “R95p” only make sense if you state exactly how they’re computed and which years set the percentile baseline; leave that out and two people can pull different answers from the same data. Pin the baseline period (the years used to define “extreme”) and never let it drift year to year.
  • Mountains fool the satellite. Along the Western Ghats / Konkan coast, rain forced up by terrain (orographic rain) is systematically under-sensed from space, so coastal and hill totals read low. Cross-check those with gauges and treat absolute coastal numbers skeptically.
  • The record is short. ~25 years limits how confidently you can spot weak trends. You can reach back to 1998 with the older TRMM 3B42 product, but only carefully: stitching two different instruments can create fake jumps (inter-product inhomogeneity).
  • Use the current version (V07). V06 is deprecated, so reprocess any cached pre-2023 data. Keep in mind too that the polished “Final” run lags real time by ~3.5 months, while the faster Late/Early runs trade accuracy for speed.

The real-data code

The Run it cell above runs this method on synthetic data with no login. Below is the whole recipe against the real archive: search, open each year’s IMERG granules, average to one number per day, squeeze each year into its extreme indices, fit Theil–Sen + Mann–Kendall, and plot it. It needs a free Earthdata Login.

import earthaccess
import xarray as xr
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# pip install pymannkendall  (Theil–Sen slope + Mann–Kendall test)
import pymannkendall as mk

earthaccess.login(strategy="netrc")

# AOI: Mumbai & the Konkan coast, as (W, S, E, N)
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, p95, p99):
    """daily: 1-D pandas Series of AOI-mean mm/day for one calendar year."""
    rx1 = daily.max()                                  # Rx1day: biggest single day
    rx5 = daily.rolling(5).sum().max()                 # Rx5day: biggest 5-day run
    wet = daily[daily >= wet_day]
    sdii = wet.mean()                                  # mean rain per wet day
    r95p = wet[wet > p95].sum()                        # rain on the wettest ~5% of days
    r99p = wet[wet > p99].sum()                        # rain on the wettest ~1% of days
    annual = daily.sum()                               # total rain (the "is it flat?" control)
    return dict(Rx1day=rx1, Rx5day=rx5, SDII=sdii, R95p=r95p, R99p=r99p, Annual=annual)

# 1. One number per day, per year: open each year's IMERG granules, clip to the box,
#    and average the daily precip field over the AOI. (IMERG dims: lon, lat, time.)
def daily_series(yr):
    grans = earthaccess.search_data(
        short_name="GPM_3IMERGDF", bounding_box=aoi,
        temporal=(f"{yr}-01-01", f"{yr}-12-31"))
    ds = xr.open_mfdataset(earthaccess.open(grans), combine="by_coords")
    box = ds["precipitation"].sel(           # V07 units are mm/day
        lon=slice(aoi[0], aoi[2]), lat=slice(aoi[1], aoi[3]))
    return box.mean(dim=["lon", "lat"]).to_series().clip(lower=0)

# 2. Fix the wet-day percentile baseline ONCE (first 10 yrs), so later years compare.
baseline = pd.concat([daily_series(y) for y in list(years)[:10]])
bwet = baseline[baseline >= wet_day]
p95, p99 = bwet.quantile(0.95), bwet.quantile(0.99)

# 3. Squeeze every year into its extreme indices -> one row per year.
records = {yr: annual_indices(daily_series(yr), p95, p99) for yr in years}
df = pd.DataFrame(records).T

# 4. Robust trend + significance per index (both shrug off freak outliers).
yr_arr = np.array(list(years))
for col in df.columns:
    res = mk.original_test(df[col].values)
    tag = "(significant)" if res.h else "(not distinguishable from noise)"
    print(f"{col:7s} slope/yr: {res.slope:+.3f}  p={res.p:.3f}  -> {res.trend} {tag}")

# Verdict: are downpours intensifying while the yearly total stays flat?
rx1, ann = mk.original_test(df.Rx1day.values), mk.original_test(df.Annual.values)
print(f"VERDICT: Rx1day {rx1.trend} ({rx1.slope:+.2f} mm/yr), "
      f"annual total {ann.trend} -> "
      f"{'intensifying bursts' if rx1.h and not ann.h else 'no clean decoupling'}")

# 5. Plot Rx1day with its Theil–Sen line, annual total alongside for contrast.
fig, ax = plt.subplots()
ax.plot(yr_arr, df.Rx1day, "o-", label="Rx1day (mm)")
ax.plot(yr_arr, rx1.intercept + rx1.slope * (yr_arr - yr_arr[0]), "k--",
        label=f"Theil–Sen {rx1.slope:+.2f} mm/yr")
ax2 = ax.twinx(); ax2.plot(yr_arr, df.Annual, "C1.", alpha=.5, label="annual total")
ax.set_xlabel("year"); ax.set_ylabel("Rx1day (mm)"); ax2.set_ylabel("annual (mm)")
ax.legend(loc="upper left"); plt.tight_layout(); plt.show()

# Before you trust it: IMERG lowballs the heaviest rates, so cross-check the trend
# against CHIRPS or an IMD gauge and report relative change, not absolute thresholds.

Where the data comes from

Everything you need comes from NASA through one free login (Earthdata Login). GPM IMERG, both daily and half-hourly, lives in a single archive (GES DISC). The optional cross-checks, CHIRPS (from UCSB/CHC) and any IMD gauge data, come from outside NASA and are separate steps. So the core analysis is a single dataset and a single sign-in.

Sources

How a scientist answers this
Parameters
Sub-daily and daily precipitation from GPM IMERG (V07, ~10 km, 2000–present) used to compute ETCCDI-style extreme-rainfall indices per year — annual maximum 1-day total (Rx1day) and 5-day total (Rx5day), the 95th- and 99th-percentile wet-day intensities (R95p / R99p, totals from days above those percentiles), simple daily intensity (SDII), and the fraction of annual rain falling on heavy days. Together these capture intensity even when the annual total is flat.
Method
Build a daily precipitation series for your AOI, derive each annual index, then test each index for a monotonic trend with the non-parametric Mann–Kendall test and estimate the slope with the Theil–Sen estimator (robust to outliers, no normality assumption). Percentiles for R95p/R99p are fixed from a wet-day baseline period so later years are comparable. A rising Rx1day or R99p with a flat annual total is the signature of intensifying downpours.
Validation
IMERG underestimates the heaviest rain rates relative to gauges, so absolute extreme magnitudes are biased low — cross-check against CHIRPS and any local gauge or IMD data, and report trends as relative change rather than absolute thresholds. State every index definition and the percentile baseline explicitly; an index is only meaningful with its definition attached.
In plain EnglishEven if the total rain in a year stays the same, it can arrive in fewer, fiercer bursts. Measure the biggest single-day and five-day downpours each year, and how much of the rain comes from the most extreme days — if those are climbing while the yearly total holds steady, the storms are getting more intense.

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.

🧪 Virtual Mock Lab stand-in data · runs in your browser · no login

A safe place to practise the method on GPM_3IMERGDF. The code is the real earthaccess workflow, but we feed it stand-in data shaped like the real product — so you can run it, change the numbers, and break it without an account. When you want the real data, use the recipe under “The real-data code” above, or download the notebook below.

editable · runs in your browser

Datasets used

Computed · real data Worked examples, computed end-to-end on real satellite data — not yet scientist-verified · expand ▾
Real trends, computed on Google Earth Engine (area-weighted Theil–Sen + Mann–Kendall).

We ran the analysis on real data for Indian regions and report exactly what came back — including the honest nulls and refusals. Each card shows the slope, significance, and (for rainfall) a gauge cross-check.

Has annual rainfall in the Marathwada region declined from 1990 to 2020?
rainfall Marathwada 1990–2020
-1.183 mm/year (trend) no significant trend
95% CI [-8.646, 7.167] Mann–Kendall p = 0.6341

The data doesn't support a rainfall change here — an earned null, not a missing answer.

⚠ Ground-truth: gauges disagree IMD gauges ↓ · ERA5 ↑ · CHIRPS ↑ sources DISAGREE on direction — treat the trend as unresolved. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has rainfall over the Western Ghats crest meaningfully changed from 1991 to 2020?
rainfall Western Ghats crest 1991–2020
-0.245 mm/year (trend) no significant trend
95% CI [-16.379, 16.596] Mann–Kendall p = 1

The data doesn't support a rainfall change here — an earned null, not a missing answer.

✓ Ground-truth: gauges agree IMD gauges ↑ · ERA5 ↑ · CHIRPS ↑ all sources agree on direction. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Rainfall trend over Punjab, 1990-2020?
rainfall Punjab 1990–2020
-5.799 mm/year (trend) no significant trend
95% CI [-12.603, 1.898] Mann–Kendall p = 0.1739

The data doesn't support a rainfall change here — an earned null, not a missing answer.

⚠ Ground-truth: gauges disagree IMD gauges ↓ · ERA5 ↓ · CHIRPS ↑* sources DISAGREE on direction — treat the trend as unresolved. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Rainfall trend over Kerala, 1990-2020?
rainfall Kerala 1990–2020
+6.541 mm/year (trend) no significant trend
95% CI [-4.858, 15.146] Mann–Kendall p = 0.1438

The data doesn't support a rainfall change here — an earned null, not a missing answer.

✓ Ground-truth: gauges agree IMD gauges ↑ · ERA5 ↑ · CHIRPS ↑* all sources agree on direction. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
What was the mean annual rainfall over the Krishna basin, 2000-2020?
rainfall Krishna basin 2000–2020
1129.3mm/year
computed · ERA5/GEE · not yet scientist-verified
Has monsoon-season (JJAS) rainfall over central India changed from 1990 to 2020?
rainfall Central India 1990–2020
+1.23 mm/year (trend) no significant trend
95% CI [-9.445, 13.019] Mann–Kendall p = 0.6341

The data doesn't support a rainfall change here — an earned null, not a missing answer.

✓ Ground-truth: gauges agree IMD gauges ↑ · ERA5 ↑ · CHIRPS ↑ all sources agree on direction. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over Tamil Nadu changed from 1990 to 2020?
rainfall Tamil Nadu 1990–2020
+4.088 mm/year (trend) no significant trend
95% CI [-5.939, 12.906] Mann–Kendall p = 0.3412

The data doesn't support a rainfall change here — an earned null, not a missing answer.

✓ Ground-truth: gauges agree IMD gauges ↑ · ERA5 ↑ · CHIRPS ↑* all sources agree on direction. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over Karnataka changed from 1990 to 2020?
rainfall Karnataka 1990–2020
+1.402 mm/year (trend) no significant trend
95% CI [-4.89, 9.295] Mann–Kendall p = 0.6341

The data doesn't support a rainfall change here — an earned null, not a missing answer.

✓ Ground-truth: gauges agree IMD gauges ↑ · ERA5 ↑ · CHIRPS ↑ all sources agree on direction. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over Gujarat changed from 1990 to 2020?
rainfall Gujarat 1990–2020
+8.447 mm/year (trend) no significant trend
95% CI [-4.367, 17.172] Mann–Kendall p = 0.1347

The data doesn't support a rainfall change here — an earned null, not a missing answer.

✓ Ground-truth: gauges agree IMD gauges ↑ · ERA5 ↑* · CHIRPS ↑* all sources agree on direction. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over Rajasthan changed from 1990 to 2020?
rainfall Rajasthan 1990–2020
+1.414 mm/year (trend) no significant trend
95% CI [-4.819, 7.13] Mann–Kendall p = 0.5184

The data doesn't support a rainfall change here — an earned null, not a missing answer.

✓ Ground-truth: gauges agree IMD gauges ↑ · ERA5 ↑ · CHIRPS ↑* all sources agree on direction. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over West Bengal changed from 1990 to 2020?
rainfall West Bengal 1990–2020
-6.59 mm/year (trend) no significant trend
95% CI [-16.478, 4.77] Mann–Kendall p = 0.1534

The data doesn't support a rainfall change here — an earned null, not a missing answer.

⚠ Ground-truth: gauges disagree IMD gauges ↓* · ERA5 ↓ · CHIRPS ↑ sources DISAGREE on direction — treat the trend as unresolved. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over Madhya Pradesh changed from 1990 to 2020?
rainfall Madhya Pradesh 1990–2020
+0.524 mm/year (trend) no significant trend
95% CI [-6.436, 11.043] Mann–Kendall p = 0.8918

The data doesn't support a rainfall change here — an earned null, not a missing answer.

⚠ Ground-truth: gauges disagree IMD gauges ↓ · ERA5 ↑ · CHIRPS ↑ sources DISAGREE on direction — treat the trend as unresolved. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over Odisha changed from 1990 to 2020?
rainfall Odisha 1990–2020
+3.624 mm/year (trend) no significant trend
95% CI [-15.228, 13.977] Mann–Kendall p = 0.5634

The data doesn't support a rainfall change here — an earned null, not a missing answer.

⚠ Ground-truth: gauges disagree IMD gauges ↓ · ERA5 ↑ · CHIRPS ↑ sources DISAGREE on direction — treat the trend as unresolved. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over Assam changed from 1990 to 2020?
rainfall Assam 1990–2020
-20.4 mm/year (trend) significant trend
95% CI [-36.556, -0.498] Mann–Kendall p = 0.0249
✓ Ground-truth: gauges agree IMD gauges ↓ · ERA5 ↓ · CHIRPS ↓ all sources agree on direction. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over the Delhi NCR changed from 1990 to 2020?
rainfall Delhi NCR 1990–2020
+0.094 mm/year (trend) no significant trend
95% CI [-7.46, 7.186] Mann–Kendall p = 1

The data doesn't support a rainfall change here — an earned null, not a missing answer.

⚠ Ground-truth: gauges disagree IMD gauges ↓* · ERA5 ↑ · CHIRPS ↑ sources DISAGREE on direction — treat the trend as unresolved. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified
Has annual rainfall over Mumbai & the Konkan coast changed from 1990 to 2020?
rainfall Mumbai & Konkan 1990–2020
+10.047 mm/year (trend) no significant trend
95% CI [-13.514, 28.655] Mann–Kendall p = 0.3412

The data doesn't support a rainfall change here — an earned null, not a missing answer.

✓ Ground-truth: gauges agree IMD gauges ↑ · ERA5 ↑ · CHIRPS ↑ all sources agree on direction. IMD = India Meteorological Dept. gauge grid (the ground-truth); * = significant.
computed · ERA5/GEE · not yet scientist-verified

…and what it refuses to answer

What is the rainfall trend over Maharashtra from 2018 to 2021?
rainfall Central India 2018–2021
⊘ Refused

trend span < 10y — the validator refuses ill-posed questions rather than guessing. That discipline is the point.

validator-enforced refusal

See all verified answers →