--- title: "Introduction to ClimateAnomaly: Computing Climate Anomaly Indices" author: "Pushkar Bora" date: "`r Sys.Date()`" output: rmarkdown::html_vignette: toc: true toc_depth: 3 vignette: > %\VignetteIndexEntry{Introduction to ClimateAnomaly: Computing Climate Anomaly Indices} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4 ) ``` ## Overview **ClimateAnomaly** is a general-purpose R package for computing standardised rainfall anomaly indices and temperature anomalies from any monthly (or annual) climate dataset. All indices are computed **group-wise** — typically per *location × calendar month* — so that each value reflects a departure from that location's own long-term climatology for the same time of year. ### Indices implemented | Index | Full name | Reference | |-------|-----------|-----------| | **RAI** | Rainfall Anomaly Index | Van Rooy (1965) | | **MRAI** | Modified Rainfall Anomaly Index | Ekpetere (2025) | | **SRAI** | Standardised Rainfall Anomaly Index | Ekpetere (2025) | | **SSPI** | Simplified Standardised Precipitation Index | Raziei (2021) | | **SPI** | Standardised Precipitation Index | McKee et al. (1993) | | **TEMP** | Temperature anomaly (z-score) | — | --- ## 1 Quick start with the built-in dataset ```{r load-pkg} library(ClimateAnomaly) ``` The package ships a sample dataset `india_monthly` — monthly climate records for **six Indian states** (1976–2026) spanning arid, tropical, Himalayan, semi-arid, high-rainfall, and peninsular climate zones. ```{r inspect-data} data(india_monthly) dim(india_monthly) head(india_monthly[, 1:8]) ``` Compute all six indices in one call: ```{r compute-all} result <- calc_all_indices( data = india_monthly, group_cols = c("state", "month_num"), # group = state × calendar month rain_col = "rainfall_mm", tmax_col = "tmax_mean_c", tmin_col = "tmin_mean_c" ) # New columns added setdiff(names(result), names(india_monthly)) ``` Inspect a single state: ```{r show-maharashtra} mh <- subset(result, state == "Maharashtra" & year >= 2015, select = c("year", "month_num", "rainfall_mm", "RAI", "RAI_class", "SPI", "SPI_class", "tmax_anomaly", "tmax_anom_class")) print(head(mh, 24)) ``` --- ## 2 Using your own data file ClimateAnomaly imposes **no fixed column names**. Supply the actual column names from your file as arguments. ### 2.1 Loading a CSV ```r my_data <- read.csv("my_climate_data.csv", stringsAsFactors = FALSE) head(my_data) ``` Assume the file has columns `district`, `yr`, `mon`, `precip_mm`, `tmax_deg`, `tmin_deg`. ```r my_result <- calc_all_indices( data = my_data, group_cols = c("district", "mon"), # group by district AND calendar month rain_col = "precip_mm", tmax_col = "tmax_deg", tmin_col = "tmin_deg" ) ``` ### 2.2 Loading an Excel file ```r # install.packages("readxl") # if not yet installed library(readxl) my_xl <- as.data.frame(read_excel("my_data.xlsx", sheet = "Monthly")) my_result <- calc_all_indices( data = my_xl, group_cols = c("Station", "Month"), rain_col = "Rainfall_mm", tmax_col = "Tmax_C", tmin_col = "Tmin_C" ) ``` ### 2.3 Annual data (no month grouping) For annual totals, group only by location: ```r annual_result <- calc_all_indices( data = my_annual_data, group_cols = "station", # or "state", "grid_id", … rain_col = "annual_rain_mm", tmax_col = NULL, # skip temperature tmin_col = NULL, indices = c("RAI", "MRAI", "SPI") ) ``` ### 2.4 Using the bundled full India dataset ```r src <- system.file("extdata", "state_weather_monthly.csv", package = "ClimateAnomaly") india_full <- read.csv(src, stringsAsFactors = FALSE) full_result <- calc_all_indices(india_full, group_cols = c("state","month_num"), rain_col = "rainfall_mm", tmax_col = "tmax_mean_c", tmin_col = "tmin_mean_c") ``` --- ## 3 Running individual index functions Each index can also be called directly on a **single numeric vector** (one group at a time). ### 3.1 RAI — Rainfall Anomaly Index ```{r rai-direct} # Rainfall series for Kerala July (all available years) kl_jul <- subset(india_monthly, state == "Kerala" & month_num == 7)$rainfall_mm rai_vals <- calc_rai(kl_jul, n_extreme = 10L) summary(rai_vals) classify_rai(rai_vals) ``` ### 3.2 MRAI and SRAI ```{r mrai-srai} mrai_vals <- calc_mrai(kl_jul) srai_vals <- calc_srai(kl_jul) data.frame(year = subset(india_monthly, state=="Kerala" & month_num==7)$year, RAI = round(rai_vals, 2), MRAI = round(mrai_vals, 2), SRAI = round(srai_vals, 2)) ``` ### 3.3 SPI — Standardised Precipitation Index ```{r spi-direct} spi_vals <- calc_spi(kl_jul) summary(spi_vals) ``` ### 3.4 SSPI — Simplified SPI (skewness-based) ```{r sspi-direct} sspi_vals <- calc_sspi(kl_jul) # Compare SPI vs SSPI cor(spi_vals, sspi_vals, use = "complete.obs") ``` ### 3.5 Temperature anomaly ```{r temp-anom} kl_tmax <- subset(india_monthly, state == "Kerala" & month_num == 6)$tmax_mean_c z <- calc_temp_anomaly(kl_tmax) summary(z) classify_temp_anomaly(z) ``` --- ## 4 Selecting a subset of indices Use the `indices` argument to compute only what you need: ```{r subset-indices} rain_only <- calc_all_indices( data = india_monthly, group_cols = c("state", "month_num"), rain_col = "rainfall_mm", tmax_col = NULL, tmin_col = NULL, indices = c("RAI", "SPI"), classify = FALSE # skip classification labels ) names(rain_only) ``` --- ## 5 Drought classification ```{r class-summary} # Frequency table of SPI classes across all states/months tab <- table(result$SPI_class) sort(tab, decreasing = TRUE) ``` ```{r rai-class-by-state} # RAI class distribution per state with(result, tapply(RAI_class, state, function(x) { sort(table(x), decreasing = TRUE)[1:3] })) ``` --- ## 6 Simple time-series plot ```{r plot-spi, fig.cap = "SPI for Kerala June (1976–2026)"} kl_jun <- subset(result, state == "Kerala" & month_num == 6) kl_jun <- kl_jun[order(kl_jun$year), ] plot(kl_jun$year, kl_jun$SPI, type = "h", col = ifelse(kl_jun$SPI >= 0, "steelblue", "firebrick"), lwd = 2, xlab = "Year", ylab = "SPI", main = "Kerala – June SPI (1976–2026)") abline(h = c(-1, 1), lty = 2, col = "grey50") abline(h = 0, lwd = 1) ``` --- ## 7 Minimum data requirements | Index | Min non-NA observations per group | Notes | |-------|------------------------------------|-------| | RAI | 4 | `n_extreme` capped at floor(n/2) | | MRAI | 4 | Uses full-series min/max | | SRAI | 4 | Uses full-series SD; output clamped to [−3, +3] | | SSPI | 4 | Uses 5th/95th percentiles; PZP-branch for dry months | | SPI | 4 | Gamma MLE; falls back to z-score if < 4 non-zero | | TEMP | 4 | z-score; returns 0 if SD = 0 | Groups with fewer than 4 non-missing values return `NA`. --- ## References Van Rooy, M.P. (1965). A rainfall anomaly index independent of time and space. *Notos*, 14, 97–104. Ekpetere, K.O. (2025). Development and evaluation of the modified and standardized rainfall anomaly indices for extreme variability analysis. *Journal of Environmental Management*, 375, 124160. https://doi.org/10.1016/j.jenvman.2025.124160 Raziei, T. (2021). Revisiting the Rainfall Anomaly Index to serve as a Simplified Standardized Precipitation Index. *Journal of Hydrology*, 602, 126761. https://doi.org/10.1016/j.jhydrol.2021.126761 Raziei, T. (2022). Improving the normalization procedure of the simplified standardized precipitation index (SSPI) using Box-Cox transformation. *Stochastic Environmental Research and Risk Assessment*, 37, 925–951. https://doi.org/10.1007/s00477-022-02317-9 McKee, T.B., Doesken, N.J., & Kleist, J. (1993). The relationship of drought frequency and duration to time scales. *Proceedings of the 8th Conference on Applied Climatology*, AMS, pp. 179–184.