The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

climatekit

CRAN status CRAN downloads Total Downloads Lifecycle: stable License: MIT

A technical working paper for this package can be found here.

An R package for computing climate indices from daily weather observations. Takes vectors of temperature, precipitation, humidity, and wind data and returns tidy data frames: no file wrangling, no class coercion, no API calls.

What are climate indices?

Standardised summary statistics that compress daily weather observations into measures like frost-day counts, growing-season length, drought severity, and heat-index exceedances. The definitions come from international bodies (WMO ETCCDI 27, ET-SCI extensions) and from individual research communities (SPI / SPEI for drought, Huglin / Winkler for viticulture).

Getting started: where to get the data

You bring the data, climatekit does the maths. If you already have a CSV:

library(climatekit)
weather <- read.csv("my_weather_station.csv")
ck_frost_days(weather$tmin, weather$date)

If you don’t have data yet, pair with readnoaa for free NOAA daily observations from 100,000+ stations. No API key:

install.packages("readnoaa")  # or devtools::install_github("charlescoverdale/readnoaa")
library(readnoaa)
library(climatekit)

# Step 1: Find a station near you
noaa_nearby(lat = 51.5, lon = -0.1, radius_km = 25)
#>        station                    name latitude longitude distance_km
#>   UKE00105915     LONDON WEATHER CENTRE   51.517    -0.117        1.4

# Step 2: Download daily data
weather <- noaa_daily("UKE00105915", "2020-01-01", "2024-12-31",
                      datatypes = c("TMAX", "TMIN", "PRCP"))

# Step 3: Compute indices
ck_frost_days(weather$tmin, weather$date, period = "annual")
ck_spi(weather$prcp, weather$date, scale = 3)
ck_heating_degree_days((weather$tmax + weather$tmin) / 2, weather$date)

Common data sources

Region Source Coverage Access
Global NOAA GHCNd 100,000+ stations worldwide Free, no key. Use readnoaa
Global ERA5 reanalysis Gridded, 0.25° resolution, 1940–present Free, requires CDS account
UK Met Office MIDAS ~1,000 UK stations, daily Free via CEDA, requires registration
Europe ECA&D 20,000+ stations across Europe Free download
US ACIS (RCC) All US cooperative & ASOS stations Free, no key
Australia Bureau of Meteorology All BoM stations, daily Free download

Why does this package exist?

R has the methods scattered across packages with incompatible interfaces:

Package Coverage Limitation
ClimInd Multi-family climate indices Returns raw vectors with no metadata, dates, or units
climdex.pcic (archived from CRAN, 2023) 27 ETCCDI core indices Requires a custom climdexInput S4 object
SPEI SPI + SPEI drought indices Single-purpose
heatwaveR Marine + atmospheric heatwaves Single-purpose
weathermetrics Unit conversions + heat index No climate indices

climatekit replaces these with a single interface: vectors in, tidy data frames out, 50+ indices spanning temperature, precipitation, drought, agroclimatic, and comfort categories.

library(climatekit)
ck_frost_days(tmin, dates)                      # data.frame
ck_spi(precip, dates, scale = 3)                # data.frame
ck_growing_degree_days(tavg, dates, base = 10)  # data.frame
ck_huglin(tmin, tmax, dates, lat = 45)          # data.frame

Installation

install.packages("climatekit")

# Or install the development version from GitHub
# install.packages("devtools")
devtools::install_github("charlescoverdale/climatekit")

Functions

ETCCDI canonical 27

The full ETCCDI core set (Alexander et al. 2006; Zhang et al. 2011) is implemented. ck_etccdi_27() returns an audit table mapping every code to its climatekit function.

Code Function Description
FD ck_frost_days() Days where Tmin < 0 °C
ID ck_ice_days() Days where Tmax < 0 °C
SU ck_summer_days() Days where Tmax > 25 °C
TR ck_tropical_nights() Days where Tmin > 20 °C
TXx ck_txx() Annual / monthly max of Tmax
TNx ck_tnx() Annual / monthly max of Tmin (warmest night)
TXn ck_txn() Annual / monthly min of Tmax (coldest day)
TNn ck_tnn() Annual / monthly min of Tmin (coldest night)
DTR ck_diurnal_range() Mean daily temperature range
GSL ck_growing_season() Growing season length
TX10p ck_tx10p() % cool days (calendar-day base, optional Zhang 2005 bootstrap)
TN10p ck_tn10p() % cool nights (calendar-day base, optional bootstrap)
TX90p ck_tx90p() % warm days (calendar-day base, optional bootstrap)
TN90p ck_tn90p() % warm nights (calendar-day base, optional bootstrap)
WSDI ck_wsdi() Warm spell duration index
CSDI ck_csdi() Cold spell duration index
RX1day ck_max_1day_precip() Max 1-day precipitation
RX5day ck_max_5day_precip() Max 5-day precipitation
SDII ck_precip_intensity() Simple daily intensity index
R10mm ck_heavy_precip() (default 10) Days with precip >= 10 mm
R20mm ck_very_heavy_precip() (default 20) Days with precip >= 20 mm
Rnnmm ck_heavy_precip(threshold = nn) Days with precip >= nn mm
CDD ck_dry_days() Max consecutive dry days
CWD ck_wet_days() Max consecutive wet days
R95p ck_r95p() Total precip on very-wet days
R99p ck_r99p() Total precip on extremely-wet days
PRCPTOT ck_total_precip() Annual wet-day precip total

ET-SCI heatwave family

Period of >= 3 consecutive days with TX above the calendar-day 90th percentile, plus cold-wave duals (TN below 10th percentile).

Code Function Description
HWN ck_hwn() Number of distinct heatwave events
HWF ck_hwf() Total days inside heatwave events
HWD ck_hwd() Longest heatwave duration
HWM ck_hwm(mode = "excess" / "absolute") Mean magnitude across event days
HWA ck_hwa(mode = "excess" / "absolute") Peak magnitude across event days
CWN ck_cwn() Cold-wave number
CWF ck_cwf() Cold-wave frequency
CWD ck_cwd() Cold-wave duration (note: ETCCDI also uses “CWD” for consecutive wet days, which is ck_wet_days)
CWM ck_cwm() Cold-wave magnitude
CWA ck_cwa() Cold-wave amplitude
EHF ck_ehf() Excess Heat Factor (Nairn & Fawcett 2013)

Drought, evapotranspiration

Function Description
ck_spi(distribution = "gamma" / "pearsonIII") Standardized Precipitation Index
ck_spei(distribution = "log-logistic" / "gev") Standardized Precipitation-Evapotranspiration Index
ck_pet() Reference evapotranspiration (Hargreaves)
ck_pet_pm() Reference evapotranspiration (FAO-56 Penman-Monteith)

Agroclimatic, comfort, energy

Function Description
ck_huglin(lat) Huglin heliothermal index (viticulture)
ck_winkler() Winkler index (wine region classification)
ck_branas(lat) Branas hydrothermal index (disease pressure)
ck_first_frost(lat) First autumn frost date (NH or SH)
ck_last_frost(lat) Last spring frost date (NH or SH)
ck_growing_degree_days() Accumulated GDD above base
ck_heating_degree_days() Heating degree days
ck_cooling_degree_days() Cooling degree days
ck_warm_spell() Warm-spell days (series-quantile, simpler variant of WSDI)
ck_wind_chill() Wind chill (Environment Canada / NWS)
ck_heat_index() Heat index (Rothfusz / NWS)
ck_humidex() Canadian humidex
ck_fire_danger() Simplified fire-danger proxy (use cffdrs for full FWI)

Discovery, dispatch, gridded

Function Description
ck_etccdi_27() Canonical ETCCDI 27 audit table
ck_catalogue() Full implementation catalogue
ck_browse(sector, standard, search) Filter the catalogue
ck_compute(data, index, ...) Dispatch any index by name
ck_available(), ck_metadata() Lightweight index lookups (all 54 indices)
ck_convert_temp() Celsius / Fahrenheit / Kelvin
ck_apply_grid(x, fun, dates, ...) Apply any function over a terra::SpatRaster
ck_from_netcdf(path, var) Thin reader for netCDF input

Examples

How many frost days does a location get?

library(climatekit)

# Daily minimum temperatures for a year
dates <- as.Date("2024-01-01") + 0:364
set.seed(42)
tmin <- sin(seq(0, 2 * pi, length.out = 365)) * 15 + 2

# Annual frost days
ck_frost_days(tmin, dates)
#>       period value      index unit
#>   2024-01-01   132 frost_days days

# Monthly breakdown
ck_frost_days(tmin, dates, period = "monthly")
#>       period value      index unit
#>   2024-01-01    25 frost_days days
#>   2024-02-01    17 frost_days days
#>   2024-03-01     4 frost_days days
#>   ...

How much heating energy does a building need?

# Heating degree days: cumulative warmth deficit below the base (default 18C).
tavg <- sin(seq(0, 2 * pi, length.out = 365)) * 12 + 10
ck_heating_degree_days(tavg, dates, period = "monthly")
#>       period  value                index        unit
#>   2024-01-01 481.10 heating_degree_days degree-days
#>   2024-02-01 378.49 heating_degree_days degree-days
#>   2024-03-01 244.53 heating_degree_days degree-days
#>   ...

# Cooling degree days for air conditioning demand
ck_cooling_degree_days(tavg, dates, base = 22)

Is a region in drought?

# SPI standardises rolling-window precipitation totals to N(0, 1).
# Values below -1 = moderate, -1.5 = severe, -2 = extreme drought.
dates_long <- seq(as.Date("2015-01-01"), as.Date("2024-12-31"), by = "day")
set.seed(42)
precip <- rgamma(length(dates_long), shape = 2, rate = 0.5)

spi <- ck_spi(precip, dates_long, scale = 3)
head(spi)
#>       period      value index        unit
#>   2015-03-01 -0.2891577   spi dimensionless
#>   2015-04-01  0.4458927   spi dimensionless
#>   ...

# SPEI adds evapotranspiration to capture temperature-driven drought
pet <- ck_pet(tmin, tmax, lat = 51.5, dates = dates)

What wine regions does a climate support?

# The Huglin heliothermal index classifies grape-growing potential:
# < 1500: too cool for viticulture
# 1500-1800: cool climate (Champagne, Mosel)
# 1800-2100: temperate (Burgundy, Oregon)
# 2100-2400: warm (Bordeaux, Napa)
# > 2400: hot (Barossa, Southern Spain)

dates_gs <- seq(as.Date("2024-04-01"), as.Date("2024-09-30"), by = "day")
set.seed(42)
tmin_gs <- rnorm(length(dates_gs), mean = 12, sd = 3)
tmax_gs <- tmin_gs + runif(length(dates_gs), 8, 15)

ck_huglin(tmin_gs, tmax_gs, dates_gs, lat = 45)
#>       period    value  index        unit
#>   2024-01-01 2129.284 huglin degree-days

# Winkler index (wine region classification)
tavg_gs <- (tmin_gs + tmax_gs) / 2
ck_winkler(tavg_gs, dates_gs)

When did frost season start and end?

dates_year <- as.Date("2024-01-01") + 0:364
set.seed(42)
tmin_year <- -10 + seq_along(dates_year) * 0.08 + rnorm(365, sd = 4)

ck_last_frost(tmin_year, dates_year)
#>       period value       date      index       unit
#>   2024-01-01   120 2024-04-29 last_frost day of year

ck_first_frost(tmin_year, dates_year)

How dangerous is a heatwave?

# Heat index = apparent temperature from T + RH. Above ~40C is dangerous.
ck_heat_index(tavg = c(30, 33, 36, 39), humidity = c(60, 65, 70, 75))
#>      value      index unit
#>   32.94844 heat_index   °C
#>   38.67052 heat_index   °C
#>   47.57163 heat_index   °C
#>   60.56858 heat_index   °C

# Wind chill for cold conditions
ck_wind_chill(tavg = c(-5, -10, -15), wind_speed = c(20, 30, 40))

# Fire weather risk
ck_fire_danger(tavg = 35, humidity = 15, wind_speed = 30, precip = 0)

Removing the in-base bias with the Zhang (2005) bootstrap

# Inside the reference period, each year biases its own threshold.
# bootstrap = TRUE applies Zhang (2005) leave-one-out resampling.
ck_tx10p(tmax, dates, ref_start = 1961L, ref_end = 1990L, bootstrap = TRUE)

Operational heatwave intensity (Excess Heat Factor)

# EHF: 3-day mean anomaly above the 95th percentile + acclimatisation.
# Australian BoM operational metric; positive EHF = heatwave conditions.
ck_ehf(tmax, tmin, dates, ref_start = 1961L, ref_end = 1990L, stat = "max")
ck_ehf(tmax, tmin, dates, stat = "n_positive")    # heatwave-condition day count
ck_ehf(tmax, tmin, dates, stat = "sum_positive")  # severity-weighted total

FAO-56 Penman-Monteith reference evapotranspiration

# ck_pet() is Hargreaves (Tmin / Tmax / lat). ck_pet_pm() is FAO-56
# Penman-Monteith with optional humidity, wind, Rs, and elevation.
ck_pet_pm(tmin, tmax, lat = 45, dates = dates,
          elev = 200, wind = 2.5,
          rh_min = rh_min, rh_max = rh_max)

Computing indices programmatically

# ck_compute() dispatches on a string index name. Useful in loops or apps.
weather <- data.frame(
  dates = as.Date("2024-01-01") + 0:364,
  tmin = sin(seq(0, 2 * pi, length.out = 365)) * 15 + 2,
  tmax = sin(seq(0, 2 * pi, length.out = 365)) * 15 + 12,
  precip = rgamma(365, shape = 0.5, rate = 0.2)
)

# Compute any index by name
ck_compute(weather, "frost_days")
ck_compute(weather, "total_precip", period = "monthly")

# See all available indices
ck_available()
#>                 index      category          unit
#>            frost_days   temperature          days
#>              ice_days   temperature          days
#>          summer_days    temperature          days
#>    tropical_nights     temperature          days
#>    ...

Package Description
readnoaa NOAA weather and climate data (pairs with climatekit for data acquisition)
carbondata Carbon market data (EU/UK ETS, voluntary registries)
cer Clean Energy Regulator data (Australia)
aemo Australian Energy Market Operator data

Migrating from climdex.pcic

climdex.pcic was the standard R implementation of the canonical ETCCDI 27 until it was archived from CRAN in 2023. climatekit covers the same set with a simpler interface:

# climdex.pcic
ci <- climdexInput.raw(tmax, tmin, prec, ..., base.range = c(1961, 1990))
fd <- climdex.fd(ci)        # named numeric vector

# climatekit
fd <- ck_frost_days(tmin, dates)   # tidy data frame

See vignette("climdex-migration", package = "climatekit") for the full function-name crosswalk and interface-shift notes.

Citation

citation("climatekit")

For academic use, also cite Alexander et al. (2006) and Zhang et al. (2011) (canonical ETCCDI), and Zhang et al. (2005) if you use the in-base bootstrap. inst/CITATION and CITATION.cff carry the bibentries.

Issues

Please report bugs or requests at https://github.com/charlescoverdale/climatekit/issues.

r, r-package, climate-indices, etccdi, climate-extremes, temperature-extremes, precipitation-extremes, heatwave, cold-wave, drought, spi, spei, evapotranspiration, penman-monteith, frost-days, growing-degree-days, climate-change, climatology

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.