SPEI - Standardized Precipitation Evapotranspiration Index

Note: This guide references “drought” in some sections due to SPEI’s historical use in agricultural drought monitoring, but SPEI is a bidirectional index that monitors both dry and wet extremes equally. All analysis functions work for both directions.

Overview

The Standardized Precipitation Evapotranspiration Index (SPEI) extends SPI by incorporating potential evapotranspiration (PET), making it sensitive to temperature changes and climate warming. Like SPI, it monitors both dry and wet conditions.

SPEI Values:

  • Negative values: Indicate dry conditions (drought)
  • Positive values: Indicate wet conditions (flooding/excess)

Key Differences from SPI:

  • Includes temperature effects via PET
  • More sensitive to climate change
  • Better for agricultural drought and water balance
  • Same interpretation as SPI (negative=dry, positive=wet)

Quick Start

import xarray as xr
from indices import spei

# Load data
precip = xr.open_dataset('input/precipitation.nc')['precip']
temp = xr.open_dataset('input/temperature.nc')['temp']
lat = xr.open_dataset('input/temperature.nc')['lat']

# Calculate SPEI-12 (auto-calculates PET from temperature via Thornthwaite)
spei_12 = spei(precip, temperature=temp, latitude=lat, scale=12)

# Negative values = dry conditions (drought)
# Positive values = wet conditions (flooding/excess)

# Save
spei_12.to_netcdf('output/netcdf/spei_12.nc')

PET Methods

Option 1: Thornthwaite (Default)

Use when: Only mean temperature data available

from indices import spei

# Pass temperature and latitude — PET computed automatically using Thornthwaite
spei_12 = spei(precip, temperature=temp, latitude=lat, scale=12)

Pros: Simple, only needs mean temperature and latitude Cons: Less accurate in arid/semi-arid regions

Option 3: Pre-computed PET

Use when: You have PET from other sources (FAO-56, Penman-Monteith, etc.)

# Load your PET data
pet = xr.open_dataset('input/pet.nc')['pet']

# Calculate SPEI directly
spei_12 = spei(precip, pet=pet, scale=12)

Best option: Most accurate if PET quality is good

PET Method Comparison

Method Inputs Required Best For
Thornthwaite T_mean, latitude Humid regions, quick estimates
Hargreaves T_mean, T_min, T_max, latitude Arid/semi-arid regions
Pre-computed External PET dataset Highest accuracy when available

Parameters

Same as SPI, plus:

  • pet (xarray.DataArray): Potential evapotranspiration
    • Same dimensions as precipitation
    • Units: mm/month
    • Required (no default)
  • pe_fitting_params (xarray.Dataset, optional): Pre-fitted parameters for operational use

Examples

Example 1: With Temperature (auto-PET)

import xarray as xr
from indices import spei

# Load data
precip = xr.open_dataset('input/chirps.nc')['precip']
temp = xr.open_dataset('input/temperature.nc')['temp']
lat = xr.open_dataset('input/temperature.nc')['lat']

# Calculate SPEI-12 (PET computed internally from temperature)
spei_12 = spei(precip, temperature=temp, latitude=lat, scale=12,
               calibration_start_year=1991, calibration_end_year=2020)

# Save
spei_12.to_netcdf('output/netcdf/spei_12.nc')

# Preview
print(spei_12)
spei_12.isel(time=-1).plot(cmap='RdBu', vmin=-3, vmax=3)

Example 2: With Hargreaves PET (Better for Arid Regions)

import xarray as xr
from indices import spei

# Load temperature data (mean, min, max)
temp_mean = xr.open_dataset('input/tmean.nc')['tmean']
temp_min = xr.open_dataset('input/tmin.nc')['tmin']
temp_max = xr.open_dataset('input/tmax.nc')['tmax']
precip = xr.open_dataset('input/precip.nc')['precip']
lat = temp_mean.lat

# Calculate SPEI-12 using Hargreaves method
spei_12 = spei(precip, temperature=temp_mean, latitude=lat, scale=12,
               pet_method='hargreaves', temp_min=temp_min, temp_max=temp_max)

Example 3: With Pre-computed PET

# Your high-quality PET from another source
pet = xr.open_dataset('input/pet_penman_monteith.nc')['pet']

# Ensure same dimensions as precipitation
assert precip.dims == pet.dims
assert precip.shape == pet.shape

# Calculate SPEI
spei_6 = spei(precip, pet=pet, scale=6)

Example 4: Multiple Scales

from indices import spei_multi_scale

# Calculate multiple scales (with temperature-based PET)
scales = [3, 6, 12]
spei_multi = spei_multi_scale(precip, temperature=temp, latitude=lat, scales=scales)

# Access different scales
spei_3 = spei_multi['spei_gamma_3_month']
spei_12 = spei_multi['spei_gamma_12_month']

SPEI vs SPI

Aspect SPI SPEI
Input Precipitation only Precipitation + PET
Temperature Not considered Included via PET
Climate change Less sensitive More sensitive
Best for Meteorological dry/wet monitoring Agricultural dry/wet monitoring
Data needs Minimal More data needed
Computation Faster Slower (PET calculation)

When to use SPEI:

  • Agricultural dry/wet monitoring
  • Climate change analysis
  • Temperature effects important
  • Have temperature data

When to use SPI:

  • Purely meteorological dry/wet monitoring
  • Only precipitation available
  • Simple, fast analysis
  • Standardized comparisons

Common Issues

Issue 1: PET Units Mismatch

Problem: SPEI values unrealistic
Cause: PET in different units than precipitation

# Check units
print(f"Precip: {precip.mean().values:.1f} mm/month")
print(f"PET: {pet.mean().values:.1f} mm/month")

# PET should be 30-200 mm/month typically
# If PET is 1-6, likely mm/day - convert
if pet.mean() < 10:
    pet = pet * 30  # Convert mm/day to mm/month (approx)

Issue 2: Negative P-PET Values

Problem: Many negative P-PET values in arid regions
Solution: This is expected! SPEI handles negative water balance

# Check water balance
water_balance = precip - pet
print(f"Mean P-PET: {water_balance.mean().values:.1f} mm/month")
# Negative is fine for arid regions

Issue 3: PET > Precipitation Always

Problem: In deserts, PET always exceeds precipitation Solution: SPEI is designed for this — shows persistent dry conditions

Issue 4: NaN in Arid Regions (Distribution Fitting Failure)

Problem: Hyper-arid grid cells return NaN even with valid data Cause: Pearson Type III requires sufficient non-zero values for L-moments computation. When >95% of water balance values cluster near zero or are identical, fitting fails.

Diagnosis:

from distributions import diagnose_data

# Check the water balance distribution
water_balance = (precip - pet).isel(lat=50, lon=100).values
diag = diagnose_data(water_balance)
print(f"Zero proportion: {diag.zero_proportion:.1%}")
print(f"Suitable for Pearson III: {diag.is_suitable_pearson3}")

Solutions:

  • Use Gamma distribution instead of Pearson III (more robust for extreme cases)
  • Use longer time scales (SPEI-12, SPEI-24)
  • Mask hyper-arid pixels where SPEI is not meaningful

See Data Model — Arid Regions for detailed guidance.

Visualization

from visualization import plot_index

# Single location
location_spei = spei_12.isel(lat=50, lon=100)
plot_index(location_spei, threshold=-1.2,
           title='SPEI-12 Time Series')

Best Practices

  1. Match grids: PET and precipitation on the same spatial and temporal resolution
  2. Quality PET: Use the best available method for your region
  3. Units check: Verify both precipitation and PET are in mm/month

For full input requirements (dimensions, calibration period, missing data handling), see Data Model & Outputs.

Performance

PET calculation is the bottleneck:

# Auto-PET from temperature (Thornthwaite)
spei_12 = spei(precip, temperature=temp, latitude=lat, scale=12)  # ~30 sec

# Pre-computed PET (faster — PET already available)
spei_12 = spei(precip, pet=pet, scale=12)  # ~10 sec

# Use Dask for large datasets
precip_chunked = xr.open_dataset('precip.nc', chunks={'time': 100})['precip']
spei_12 = spei(precip_chunked, pet=pet, scale=12)  # Parallel

References

  • Vicente-Serrano, S.M., Beguería, S., López-Moreno, J.I. (2010). A Multiscalar Drought Index Sensitive to Global Warming: The Standardized Precipitation Evapotranspiration Index. Journal of Climate, 23(7), 1696-1718.

  • Beguería, S., Vicente-Serrano, S.M., Reig, F., Latorre, B. (2014). Standardized precipitation evapotranspiration index (SPEI) revisited: parameter fitting, evapotranspiration models, tools, datasets and drought monitoring. International Journal of Climatology, 34(10), 3001-3023.

See Also

Back to top