Migrating from climdex.pcic

climdex.pcic (Pacific Climate Impacts Consortium) was for many years the standard R implementation of the ETCCDI canonical 27 indices. It was archived from CRAN in 2023. climatekit covers the same ETCCDI 27 plus the ET-SCI heatwave family, on a tidy data-frame interface with no compiled code. This vignette walks through what changes when you migrate.

Function-name crosswalk

Every ETCCDI 27 index in climdex.pcic maps to a climatekit function. The function names mirror the canonical ETCCDI codes where possible.

ETCCDI code climdex.pcic climatekit
FD climdex.fd() ck_frost_days()
ID climdex.id() ck_ice_days()
SU climdex.su() ck_summer_days()
TR climdex.tr() ck_tropical_nights()
TXx climdex.txx() ck_txx()
TNx climdex.tnx() ck_tnx()
TXn climdex.txn() ck_txn()
TNn climdex.tnn() ck_tnn()
TX10p climdex.tx10p() ck_tx10p()
TN10p climdex.tn10p() ck_tn10p()
TX90p climdex.tx90p() ck_tx90p()
TN90p climdex.tn90p() ck_tn90p()
WSDI climdex.wsdi() ck_wsdi()
CSDI climdex.csdi() ck_csdi()
DTR climdex.dtr() ck_diurnal_range()
GSL climdex.gsl() ck_growing_season()
RX1day climdex.rx1day() ck_max_1day_precip()
RX5day climdex.rx5day() ck_max_5day_precip()
SDII climdex.sdii() ck_precip_intensity()
R10mm climdex.r10mm() ck_heavy_precip() (default threshold = 10)
R20mm climdex.r20mm() ck_very_heavy_precip() (default threshold = 20)
Rnnmm climdex.rnnmm(threshold = nn) ck_heavy_precip(threshold = nn)
CDD climdex.cdd() ck_dry_days()
CWD climdex.cwd() ck_wet_days()
R95p climdex.r95p() / climdex.r95ptot() ck_r95p()
R99p climdex.r99p() / climdex.r99ptot() ck_r99p()
PRCPTOT climdex.prcptot() ck_total_precip()

Beyond ETCCDI 27, climatekit also exposes the ET-SCI heatwave family (ck_hwn(), ck_hwf(), ck_hwd(), ck_hwm(), ck_hwa()) and the cold-wave duals (ck_cwn() etc.), agroclimatic indices (Huglin, Winkler, Branas), drought indices (SPI, SPEI), and comfort indices (heat index, humidex, wind chill).

ck_etccdi_27() returns an audit table that maps every ETCCDI code to its climatekit function in one place.

Interface shifts

Inputs. climdex.pcic builds a climdexInput object from your daily temperature and precipitation series and then runs index functions against that object. climatekit takes the daily numeric vectors and a Date vector directly. There is no preliminary input object.

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

# climatekit
fd <- ck_frost_days(tmin, dates)

Outputs. climdex.pcic returns a numeric vector indexed by year (or month). climatekit returns a tidy data frame with period, value, index, and unit columns:

ck_frost_days(tmin, dates)
#> # period       value index        unit
#> # 2020-01-01      72 frost_days   days
#> # 2021-01-01      68 frost_days   days
#> # ...

The tidy shape composes directly with rbind(), ggplot2, and downstream tabulation. No reshaping needed.

Reference period. The percentile-based indices (ck_tx10p(), ck_tn10p(), ck_tx90p(), ck_tn90p(), ck_r95p(), ck_r99p(), ck_wsdi(), ck_csdi(), ck_hwn() and family) use ref_start = 1961L, ref_end = 1990L by default. Pass other values to override:

ck_tx90p(tmax, dates, ref_start = 1981L, ref_end = 2010L)

In-base bootstrap. climdex.pcic implements the Zhang et al. (2005) in-base bootstrap to remove the self-inclusion bias for years inside the reference period. climatekit does not implement the bootstrap in v0.2.0. Years inside the reference period therefore show a small bias (this is documented in each function’s roxygen). For climate-change attribution work, restrict interpretation to years outside the reference window, or apply the bootstrap externally. Future versions may add it.

Calendar-day window. Both packages use a 5-day window centred on each day of year for percentile estimation, pooled across the reference period.

Spell-counting conventions. Both packages use 6 days minimum for WSDI / CSDI. climatekit’s ET-SCI heatwave family uses 3 days minimum (min_spell = 3L), matching ET-SCI / climpact.

Sanity-check workflow

When migrating a real station record, sanity-check the new outputs against your previous climdex.pcic runs at the annual scale:

# Old climdex.pcic outputs
old_fd <- climdex.fd(ci)
old_txx <- climdex.txx(ci)

# New climatekit outputs
new_fd  <- ck_frost_days(tmin, dates)
new_txx <- ck_txx(tmax, dates)

# Compare; expect agreement up to NA-handling and base-period self-inclusion
all.equal(old_fd, new_fd$value)
all.equal(old_txx, new_txx$value)

Most ETCCDI indices are NA-handling-tolerant by construction. Differences typically come from:

  1. Default missing-day handling. climdex.pcic has more elaborate rules (e.g. min number of valid days per month) that climatekit does not enforce in v0.2.0.
  2. In-base bootstrap. As noted above, percentile indices for years 1961-1990 will differ slightly.
  3. Calendar leap-day handling. Both packages handle DOY 60 (29 February) without special-casing in v0.2.0.

For all other ETCCDI 27 indices, climatekit and climdex.pcic should agree to within numerical noise on the same input.

See also