CMIP6_Rain‐based seasonal detection methods - Rwema25/AE-project GitHub Wiki
In evaluating rain-based seasonal detection methods, it is essential to consider criteria that reflect both scientific rigor and practical applicability. Key factors include the types of input variables required, the generalizability of the method across diverse climatic and soil conditions, and the extent to which the method has been adopted by major institutions and operational projects globally. It is important to balance methodological sophistication with feasibility, especially in data-limited contexts, while also ensuring that the approach aligns with real-world agricultural decision-making needs. This evaluation focuses on three representative methods that span this spectrum, from simple rainfall thresholds to integrated soil moisture models and advanced satellite vegetation indices, providing a comprehensive overview of their strengths, limitations, and institutional relevance.
- Input Variables: Daily rainfall only.
-
Generalizability:
- Regional: Hard-coded parameters (e.g., "25mm in first dekad, 20mm in next two") limit global applicability.
- Soil Blindness: Ignores soil properties (e.g., drainage in sandy vs. clay soils), reducing accuracy in heterogeneous regions.
-
Institutional Adoption:
- High: FAO’s standard method for agrometeorological bulletins and seasonal maps.
- Approach Type: Heuristic, rule-based method using fixed rainfall thresholds.
- Strengths: Low technical barriers, ideal for resource-limited regions.
- Weaknesses: Fails in climate-variable zones; no evaporation/temperature integration.
- Documentation: FAO Handbook (2019).
- Input Variables: Rainfall, soil type, temperature, evapotranspiration.
-
Generalizability:
- Moderate-High: Accounts for soil water-holding capacity (e.g., sand vs. clay) and climate.
- Adaptability: Adjustable parameters for regional calibration (e.g., using SoilGrids data).
-
Institutional Adoption:
- Moderate: World Bank projects (e.g., East Africa climate-smart agriculture); not yet FAO-adopted.
- Approach Type: Model-based, incorporating soil water balance and climate variables.
- Strengths: Reflects actual planting conditions; superior to rainfall-only methods.
- Weaknesses: Requires soil data and local calibration.
- Documentation:
- Input Variables: Satellite vegetation indices (NDVI/PRI), rainfall, temperature.
-
Generalizability:
- High: Tracks actual plant "green-up" via satellite; applicable globally.
- Strength: Less noise from dry spells; captures phenology (e.g., growing season length).
-
Institutional Adoption:
- Emerging: NASA/ESA projects (e.g., MODIS VI); limited operational use in agriculture.
- Approach Type: Model-based using remote sensing to track vegetation phenology.
- Strengths: Direct biomass monitoring; large-scale suitability.
- Weaknesses: Cloud cover disrupts data; technical capacity required.
- Documentation:
Method | Input Variables | Approach Type | Generalizability | Institutional Adoption | Strengths | Limitations |
---|---|---|---|---|---|---|
Rainfall Threshold | Rainfall only | Heuristic (rule-based) | Regional; parameters often hard-coded for specific climates and soils | High (FAO widely used) | Simple, low data needs, easy to implement | Limited transferability; ignores soil and temp |
Soil Moisture-Integrated | Rainfall + soil + climate | Model-based | Moderate-High; adaptable with soil and climate data | Moderate (World Bank projects) | More accurate; accounts for soil water retention | Requires soil data and calibration; moderate complexity |
Vegetation Index (MVDI) | Satellite vegetation indices + climate | Model-based | High; applicable globally via remote sensing | Emerging (NASA/ESA) | Captures actual plant phenology; large-scale monitoring | Cloud cover issues; technical and data intensive |
-
Tiered Implementation:
- Resource-Limited: FAO rainfall threshold (simple, low-cost).
- Data-Rich Regions: Soil moisture method (balanced accuracy/feasibility).
- Advanced Monitoring: NDVI/MVDI for research/policy (e.g., climate adaptation).
-
Validation Protocol:
- Ground-truth methods using farmer-reported planting dates.
- FAO Rainfall Threshold: FAO Handbook (2019)
- Soil Moisture Guidance: Drought.gov Soil Moisture Data Quality
- NDVI Algorithms: MODIS VI Algorithm (NASA)
- NDVI/PRI Guide: METER Group NDVI/PRI Guide
Threshold-based methods are among the simplest and most widely used approaches to detect the onset and cessation of rainy seasons. These methods define specific rainfall criteria, such as cumulative rainfall over a certain number of days or a minimum daily rainfall threshold, to objectively determine when the rainy season starts and ends.
Term | Definition | References |
---|---|---|
Onset Date (OD) | The first period of five consecutive days accumulating at least 25 mm of rainfall, with the first day and at least two others having rainfall ≥ 1 mm, and no dry spell (rainfall < 1 mm) lasting 7 days or more in the following 30 days. | Adelekan & Adegebo (2014); Mugo et al. (2016); Camberlin et al. (2009); Haleakala et al. (2018); Recha et al. (2012); Rwema et al. (2025); FAO Handbook (2019) |
Cessation Date (CD) | The first day when the accumulated rainfall over ten consecutive days is less than half of the corresponding potential evapotranspiration (PET). | Kijazi & Reason (2012) ; Sebaziga et al. (2024) |
Season Length (SL) | The number of days between the onset and cessation dates, calculated by subtracting OD from CD for each year. | Gebremichael et al. (2014); Haleakala et al. (2018) |
The following R code demonstrates how to fetch Climate Data from the Open-Meteo API and detect the onset dates of the long and short rainy seasons for the year 2024 using our defined rainfall and dry spell criteria.
# Load Required Libraries
# Install if not already installed
if (!require(httr)) install.packages("httr")
if (!require(jsonlite)) install.packages("jsonlite")
if (!require(dplyr)) install.packages("dplyr")
if (!require(zoo)) install.packages("zoo")
library(httr)
library(jsonlite)
library(dplyr)
library(zoo)
# Fetch Climate Data from Open-Meteo API
url <- "https://climate-api.open-meteo.com/v1/climate"
get_climate_data <- function(start_date, end_date) {
params <- list(
latitude = -1.2921, # Nairobi latitude
longitude = 36.8219, # Nairobi longitude
start_date = start_date,
end_date = end_date,
models = "MRI_AGCM3_2_S", # This model can be changed to try others
daily = "temperature_2m_max,precipitation_sum",
temperature_unit = "celsius",
precipitation_unit = "mm",
timeformat = "iso8601"
)
response <- GET(url, query = params)
if (status_code(response) == 200) {
data <- content(response, as = "parsed", type = "application/json")
dates <- unlist(data$daily$time)
temp_max <- unlist(data$daily$temperature_2m_max)
precip <- unlist(data$daily$precipitation_sum)
df <- data.frame(
Date = as.Date(dates),
Temperature_Max_C = temp_max,
Rainfall = precip
)
return(df)
} else {
warning(paste("Request failed with status:", status_code(response), "for", start_date, "to", end_date))
return(NULL)
}
}
# Fetch data month by month for 2024
start_dates <- seq(as.Date("2024-01-01"), as.Date("2024-12-01"), by = "month")
end_dates <- seq(as.Date("2024-01-31"), as.Date("2024-12-31"), by = "month")
if(length(end_dates) < length(start_dates)) {
end_dates <- c(end_dates, as.Date("2024-12-31"))
}
full_year_data <- data.frame()
for (i in seq_along(start_dates)) {
cat("Fetching data from", as.character(start_dates[i]), "to", as.character(end_dates[i]), "\n")
time_taken <- system.time({
monthly_data <- tryCatch(
get_climate_data(as.character(start_dates[i]), as.character(end_dates[i])),
error = function(e) {
cat("Error for", as.character(start_dates[i]), "to", as.character(end_dates[i]), "\n")
return(NULL)
}
)
})
cat("Time taken:", time_taken["elapsed"], "seconds\n\n")
if(!is.null(monthly_data)) {
full_year_data <- bind_rows(full_year_data, monthly_data)
}
}
full_year_data <- distinct(full_year_data)
# Print the first few rows of the data
print(head(full_year_data))
# Onset Detection Logic: Definition
# --- Onset detection function ---
detect_onset_precise <- function(rainfall,
onset_cum_threshold = 25,
onset_window = 5,
min_rainy_days = 3, # including first day
min_first_day_rain = 1,
dry_threshold = 1,
max_dry_spell_length = 6, # less than 7 days allowed
dry_spell_check_days = 30) {
n <- length(rainfall)
rolling_sum <- zoo::rollapply(rainfall, width = onset_window, FUN = sum, align = "left", fill = NA)
for (i in 1:(n - onset_window - dry_spell_check_days + 1)) {
if (!is.na(rolling_sum[i]) && rolling_sum[i] >= onset_cum_threshold) {
window_rain <- rainfall[i:(i + onset_window - 1)]
if (window_rain[1] < min_first_day_rain) next
rainy_days_other <- sum(window_rain[-1] >= min_first_day_rain)
if (rainy_days_other < (min_rainy_days - 1)) next
dry_period <- rainfall[(i + onset_window):(i + onset_window + dry_spell_check_days - 1)]
rle_dry <- rle(dry_period < dry_threshold)
if (any(rle_dry$values == TRUE & rle_dry$lengths >= 7)) next
return(i)
}
}
return(NA)
}
detect_onset_period <- function(data, start_date, end_date) {
period_data <- data %>% filter(Date >= start_date & Date <= end_date)
onset_idx <- detect_onset_precise(period_data$Rainfall)
if (!is.na(onset_idx)) {
return(period_data$Date[onset_idx])
} else {
return(NA)
}
}
# Define Rainy Seasons and Calculate Onset
year_of_interest <- 2024
long_rains_start <- as.Date(paste0(year_of_interest, "-03-01"))
long_rains_end <- as.Date(paste0(year_of_interest, "-06-30"))
short_rains_start <- as.Date(paste0(year_of_interest, "-09-01"))
short_rains_end <- as.Date(paste0(year_of_interest, "-12-31"))
long_rains_onset <- detect_onset_period(full_year_data, long_rains_start, long_rains_end)
short_rains_onset <- detect_onset_period(full_year_data, short_rains_start, short_rains_end)
cat("Year:", year_of_interest, "\n")
cat("Long rains onset:", ifelse(is.na(long_rains_onset), "No onset detected", as.character(long_rains_onset)), "\n")
cat("Short rains onset:", ifelse(is.na(short_rains_onset), "No onset detected", as.character(short_rains_onset)), "\n")
The output below shows the first rows of the climate data printed by the scripts
print(head(full_year_data))
Date Temperature_Max_C Rainfall
1 2024-01-01 29.3 0.00
2 2024-01-02 28.5 1.46
3 2024-01-03 26.8 2.19
4 2024-01-04 26.7 0.00
5 2024-01-05 27.1 0.00
6 2024-01-06 29.8 0.88
The output below shows the detected onset dates for the long and short rainy seasons in 2024 based on the applied criteria.
cat("Year:", year_of_interest, "\n")
Year: 2024
> cat("Long rains onset:", ifelse(is.na(long_rains_onset), "No onset detected", as.character(long_rains_onset)), "\n")
Long rains onset: 2024-03-18
> cat("Short rains onset:", ifelse(is.na(short_rains_onset), "No onset detected", as.character(short_rains_onset)), "\n")
Short rains onset: 2024-11-14
Below is the complete R script to fetch Climate Data from the Open-Meteo API and detect the onset dates of the long and short rainy seasons for each year from 1981 to 2024, using a precise rainfall and dry spell definition. The code fetches climate data from the Open-Meteo API and processes the daily rainfall data, applies the onset detection criteria, and outputs a clean summary of onset dates for both seasons.
# Load Required Libraries
if (!require(httr)) install.packages("httr")
if (!require(jsonlite)) install.packages("jsonlite")
if (!require(dplyr)) install.packages("dplyr")
if (!require(zoo)) install.packages("zoo")
if (!require(tibble)) install.packages("tibble")
library(httr)
library(jsonlite)
library(dplyr)
library(zoo)
library(tibble) # For better data frame handling
# Function to fetch climate data from API for a given date range
get_climate_data <- function(start_date, end_date) {
params <- list(
latitude = -1.2921, # Dakar latitude
longitude = 36.8219, # Dakar longitude
start_date = start_date,
end_date = end_date,
models = "MRI_AGCM3_2_S", # Change model if needed
daily = "temperature_2m_max,precipitation_sum",
temperature_unit = "celsius",
precipitation_unit = "mm",
timeformat = "iso8601"
)
response <- GET("https://climate-api.open-meteo.com/v1/climate", query = params)
if (status_code(response) == 200) {
data <- content(response, as = "parsed", type = "application/json")
dates <- unlist(data$daily$time)
temp_max <- unlist(data$daily$temperature_2m_max)
precip <- unlist(data$daily$precipitation_sum)
df <- data.frame(
Date = as.Date(dates),
Temperature_Max_C = temp_max,
Rainfall = precip
)
return(df)
} else {
warning(paste("Request failed with status:", status_code(response), "for", start_date, "to", end_date))
return(NULL)
}
}
# Onset detection function (unchanged)
detect_onset_precise <- function(rainfall,
onset_cum_threshold = 25,
onset_window = 5,
min_rainy_days = 3,
min_first_day_rain = 1,
dry_threshold = 1,
max_dry_spell_length = 6,
dry_spell_check_days = 30) {
n <- length(rainfall)
rolling_sum <- zoo::rollapply(rainfall, width = onset_window, FUN = sum, align = "left", fill = NA)
for (i in 1:(n - onset_window - dry_spell_check_days + 1)) {
if (!is.na(rolling_sum[i]) && rolling_sum[i] >= onset_cum_threshold) {
window_rain <- rainfall[i:(i + onset_window - 1)]
if (window_rain[1] < min_first_day_rain) next
rainy_days_other <- sum(window_rain[-1] >= min_first_day_rain)
if (rainy_days_other < (min_rainy_days - 1)) next
dry_period <- rainfall[(i + onset_window):(i + onset_window + dry_spell_check_days - 1)]
rle_dry <- rle(dry_period < dry_threshold)
if (any(rle_dry$values == TRUE & rle_dry$lengths >= 7)) next
return(i)
}
}
return(NA)
}
detect_onset_period <- function(data, start_date, end_date) {
period_data <- data %>%
filter(Date >= start_date & Date <= end_date) %>%
arrange(Date)
# Ensure Date column is Date class
period_data$Date <- as.Date(period_data$Date)
onset_idx <- detect_onset_precise(period_data$Rainfall)
if (!is.na(onset_idx)) {
onset_date <- period_data$Date[onset_idx]
return(onset_date)
} else {
return(NA)
}
}
# Prepare to fetch data for each year from 1981 to 2024
years <- 1981:2024
all_data <- data.frame()
for (year in years) {
cat("Fetching data for year:", year, "\n")
# Generate monthly intervals for the year
start_dates <- seq(as.Date(paste0(year, "-01-01")), as.Date(paste0(year, "-12-01")), by = "month")
end_dates <- seq(as.Date(paste0(year, "-01-31")), as.Date(paste0(year, "-12-31")), by = "month")
if (length(end_dates) < length(start_dates)) {
end_dates <- c(end_dates, as.Date(paste0(year, "-12-31")))
}
yearly_data <- data.frame()
for (i in seq_along(start_dates)) {
cat(" Fetching from", as.character(start_dates[i]), "to", as.character(end_dates[i]), "\n")
time_taken <- system.time({
monthly_data <- tryCatch(
get_climate_data(as.character(start_dates[i]), as.character(end_dates[i])),
error = function(e) {
cat(" Error fetching data for", as.character(start_dates[i]), "to", as.character(end_dates[i]), "\n")
return(NULL)
}
)
})
cat(" Time taken:", time_taken["elapsed"], "seconds\n")
if (!is.null(monthly_data)) {
yearly_data <- bind_rows(yearly_data, monthly_data)
}
}
yearly_data <- distinct(yearly_data)
all_data <- bind_rows(all_data, yearly_data)
}
all_data <- distinct(all_data)
cat("Data fetching complete.\n")
cat("Total records fetched:", nrow(all_data), "\n")
# Print the first and last few rows of the combined climate data
cat("First few rows of the data:\n")
print(head(all_data))
cat("\nLast few rows of the data:\n")
print(tail(all_data))
# Initialize results tibble with proper Date columns
results <- tibble(
Year = integer(),
Long_Rains_Onset = as.Date(character()),
Short_Rains_Onset = as.Date(character())
)
for (year in years) {
long_rains_start <- as.Date(paste0(year, "-03-01"))
long_rains_end <- as.Date(paste0(year, "-06-30"))
short_rains_start <- as.Date(paste0(year, "-09-01"))
short_rains_end <- as.Date(paste0(year, "-12-31"))
# Filter data for this year
year_data <- all_data %>% filter(format(Date, "%Y") == as.character(year))
if (nrow(year_data) == 0) {
cat("No data for year", year, "\n")
results <- bind_rows(results, tibble(Year = year, Long_Rains_Onset = NA, Short_Rains_Onset = NA))
next
}
long_onset <- detect_onset_period(year_data, long_rains_start, long_rains_end)
short_onset <- detect_onset_period(year_data, short_rains_start, short_rains_end)
cat("Year:", year, "\n")
cat(" Long rains onset:", ifelse(is.na(long_onset), "No onset detected", as.character(long_onset)), "\n")
cat(" Short rains onset:", ifelse(is.na(short_onset), "No onset detected", as.character(short_onset)), "\n\n")
results <- bind_rows(results, tibble(Year = year, Long_Rains_Onset = long_onset, Short_Rains_Onset = short_onset))
}
# View or save results
options(tibble.print_max = Inf)
print(results)
# write.csv(results, "rainfall_onset_1981_2024_dakar.csv", row.names = FALSE)
#############################################################
# To get climatograph for a selected year (2024)
library(lubridate)
monthly_data <- all_data %>%
mutate(Year = year(Date), Month = month(Date, label = TRUE, abbr = TRUE)) %>%
group_by(Year, Month) %>%
summarise(
Monthly_Rainfall = sum(Rainfall, na.rm = TRUE),
Avg_Temperature = mean(Temperature_Max_C, na.rm = TRUE)
) %>%
ungroup()
# Plot climatograph for a selected year
selected_year <- 2024
monthly_year <- monthly_data %>% filter(Year == selected_year)
ggplot(monthly_year, aes(x = Month)) +
geom_col(aes(y = Monthly_Rainfall), fill = "skyblue") +
geom_line(aes(y = Avg_Temperature * 10, group = 1), color = "red", size = 1) +
scale_y_continuous(
name = "Monthly Rainfall (mm)",
sec.axis = sec_axis(~./10, name = "Avg Max Temperature (°C)")
) +
labs(title = paste("Monthly Climatograph for", selected_year)) +
theme_minimal()
#####################################################################
########To print monthly climatology by considering all period 1981-2024
library(ggplot2)
library(dplyr)
library(lubridate)
library(tidyr)
# Aggregate daily data into monthly total rainfall for 1981-2024
monthly_rainfall <- all_data %>%
filter(year(Date) >= 1981 & year(Date) <= 2024) %>%
mutate(Year = year(Date),
Month = month(Date, label = TRUE, abbr = TRUE)) %>%
group_by(Year, Month) %>%
summarise(Monthly_Rainfall = sum(Rainfall, na.rm = TRUE), .groups = "drop")
# Calculate long-term average monthly rainfall across all years
monthly_climatology <- monthly_rainfall %>%
group_by(Month) %>%
summarise(Avg_Monthly_Rainfall = mean(Monthly_Rainfall, na.rm = TRUE), .groups = "drop")
# Ensure all months (Jan-Dec) are present and in correct order
monthly_climatology <- monthly_climatology %>%
complete(Month = factor(month.abb, levels = month.abb), fill = list(Avg_Monthly_Rainfall = 0))
# Plot the climatograph for rainfall only
ggplot(monthly_climatology, aes(x = Month, y = Avg_Monthly_Rainfall)) +
geom_col(fill = "skyblue", width = 0.9) +
labs(
title = "Long-Term Average Monthly Rainfall (1981-2024) - Dakar",
x = "Month",
y = "Average Monthly Rainfall (mm)"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
The output below shows the first and last rows of the climate data printed by the scripts
cat("First few rows of the data:\n")
First few rows of the data:
> print(head(all_data))
Date Temperature_Max_C Rainfall
1 1981-01-01 25.3 0
2 1981-01-02 25.7 0
3 1981-01-03 26.6 0
4 1981-01-04 26.9 0
5 1981-01-05 26.8 0
6 1981-01-06 27.6 0
> cat("\nLast few rows of the data:\n")
Last few rows of the data:
> print(tail(all_data))
Date Temperature_Max_C Rainfall
16066 2024-12-26 25.7 2.45
16067 2024-12-27 24.3 1.88
16068 2024-12-28 25.6 2.17
16069 2024-12-29 25.8 1.16
16070 2024-12-30 25.8 1.74
16071 2024-12-31 26.6 2.77
The figure below illustrates the monthly climatology of precipitation for the selected location, providing an overview of the typical rainfall distribution throughout the year. Accompanying this, the table summarizes the detected onset dates for both the long and short rainy seasons over the period from 1981 to 2024. These onset dates have been identified based on the specific criteria applied in the analysis, enabling a consistent comparison across different years and locations.
print(results)
Year Long_Rains_Onset Short_Rains_Onset
1 1981 1981-03-30 1981-10-14
2 1982 1982-03-30 1982-10-28
3 1983 1983-03-28 1983-10-08
4 1984 1984-04-09 <NA>
5 1985 1985-04-12 <NA>
6 1986 1986-04-25 <NA>
7 1987 1987-03-31 1987-10-24
8 1988 1988-03-01 <NA>
9 1989 <NA> <NA>
10 1990 1990-03-06 <NA>
11 1991 <NA> 1991-10-13
12 1992 1992-03-28 <NA>
13 1993 1993-03-01 <NA>
14 1994 1994-03-20 <NA>
15 1995 1995-04-01 <NA>
16 1996 1996-04-01 <NA>
17 1997 1997-04-08 1997-10-27
18 1998 1998-03-20 <NA>
19 1999 1999-03-18 <NA>
20 2000 <NA> <NA>
21 2001 2001-03-17 2001-11-07
22 2002 2002-05-21 2002-11-02
23 2003 2003-04-08 <NA>
24 2004 2004-04-23 <NA>
25 2005 2005-03-20 <NA>
26 2006 2006-03-02 2006-10-27
27 2007 2007-03-16 <NA>
28 2008 2008-03-30 <NA>
29 2009 2009-04-16 2009-11-02
30 2010 2010-03-20 2010-10-14
31 2011 2011-04-17 2011-10-25
32 2012 2012-04-01 2012-11-19
33 2013 <NA> <NA>
34 2014 2014-03-27 <NA>
35 2015 <NA> 2015-10-14
36 2016 2016-04-05 <NA>
37 2017 <NA> 2017-11-26
38 2018 2018-03-15 2018-10-22
39 2019 <NA> <NA>
40 2020 <NA> 2020-10-31
41 2021 2021-04-12 2021-11-04
42 2022 <NA> 2022-10-25
43 2023 2023-04-11 2023-11-03
44 2024 2024-03-18 2024-11-14
print(results)
Year Long_Rains_Onset Short_Rains_Onset
1 1981 NA NA
2 1982 NA NA
3 1983 NA NA
4 1984 NA NA
5 1985 NA 1985-09-01
6 1986 NA NA
7 1987 NA NA
8 1988 NA NA
9 1989 NA NA
10 1990 NA NA
11 1991 NA NA
12 1992 NA NA
13 1993 NA NA
14 1994 NA NA
15 1995 NA NA
16 1996 NA NA
17 1997 NA NA
18 1998 NA NA
19 1999 NA NA
20 2000 NA NA
21 2001 NA NA
22 2002 NA NA
23 2003 NA NA
24 2004 NA NA
25 2005 NA NA
26 2006 NA NA
27 2007 NA NA
28 2008 NA 2008-09-03
29 2009 NA NA
30 2010 NA NA
31 2011 NA 2011-09-08
32 2012 NA 2012-09-12
33 2013 NA NA
34 2014 NA NA
35 2015 NA 2015-09-01
36 2016 NA NA
37 2017 NA NA
38 2018 NA NA
39 2019 NA NA
40 2020 NA NA
41 2021 NA 2021-09-02
42 2022 NA 2022-09-16
43 2023 NA NA
44 2024 NA NA
print(results)
Year Long_Rains_Onset Short_Rains_Onset
1 1981 1981-03-01 1981-09-01
2 1982 1982-03-04 1982-09-07
3 1983 1983-03-08 1983-09-07
4 1984 1984-03-04 1984-09-06
5 1985 1985-03-01 1985-09-04
6 1986 1986-03-01 1986-09-01
7 1987 1987-03-01 1987-09-06
8 1988 1988-03-16 1988-09-05
9 1989 1989-03-01 1989-09-01
10 1990 1990-03-01 1990-09-01
11 1991 1991-03-01 1991-09-01
12 1992 1992-03-08 1992-09-01
13 1993 1993-03-05 1993-09-02
14 1994 1994-03-01 1994-09-07
15 1995 1995-03-13 1995-09-01
16 1996 1996-03-09 1996-09-01
17 1997 1997-03-19 1997-09-06
18 1998 1998-03-01 1998-09-11
19 1999 1999-03-01 1999-09-04
20 2000 2000-03-03 2000-09-01
21 2001 2001-03-02 2001-09-04
22 2002 2002-03-01 2002-09-09
23 2003 2003-03-01 2003-09-01
24 2004 2004-03-01 2004-09-01
25 2005 2005-03-01 2005-09-07
26 2006 2006-03-01 2006-09-01
27 2007 2007-03-01 2007-09-07
28 2008 2008-03-01 2008-09-01
29 2009 2009-03-02 2009-09-01
30 2010 2010-03-01 2010-09-01
31 2011 2011-03-01 2011-09-05
32 2012 2012-03-01 2012-09-01
33 2013 2013-03-09 2013-09-07
34 2014 2014-03-06 2014-09-03
35 2015 2015-03-01 2015-09-01
36 2016 2016-03-02 2016-09-03
37 2017 2017-03-01 2017-09-01
38 2018 2018-03-01 2018-09-05
39 2019 2019-03-01 2019-09-02
40 2020 2020-03-01 2020-09-01
41 2021 2021-03-01 2021-09-01
42 2022 2022-03-01 2022-09-01
43 2023 2023-03-04 2023-09-01
44 2024 2024-03-04 2024-09-01
print(results)
Year Long_Rains_Onset Short_Rains_Onset
1 1981 NA NA
2 1982 NA NA
3 1983 NA NA
4 1984 NA NA
5 1985 NA NA
6 1986 NA NA
7 1987 NA NA
8 1988 NA NA
9 1989 NA NA
10 1990 NA NA
11 1991 NA NA
12 1992 NA NA
13 1993 NA NA
14 1994 NA NA
15 1995 NA NA
16 1996 NA NA
17 1997 NA NA
18 1998 NA NA
19 1999 NA NA
20 2000 NA NA
21 2001 NA NA
22 2002 NA NA
23 2003 NA NA
24 2004 NA NA
25 2005 NA NA
26 2006 NA NA
27 2007 NA NA
28 2008 NA NA
29 2009 NA NA
30 2010 NA NA
31 2011 NA NA
32 2012 NA NA
33 2013 NA NA
34 2014 NA NA
35 2015 NA NA
36 2016 NA NA
37 2017 NA NA
38 2018 NA NA
39 2019 NA NA
40 2020 NA NA
41 2021 NA NA
42 2022 NA NA
43 2023 NA NA
44 2024 NA NA
Region Type | Location | Latitude | Longitude | Notes |
---|---|---|---|---|
Bimodal Region | Nairobi, Kenya | -1.2921 | 36.8219 | Bimodal rainfall with long rains (Mar–May) and short rains (Oct–Dec) driven by ITCZ. |
Kisumu, Kenya | -0.0900 | 34.7600 | Characteristic bimodal rainfall with long and short rainy seasons. | |
Dar es Salaam, Tanzania | -6.7900 | 39.2100 | Coastal bimodal rainfall pattern. | |
Unimodal Region | Addis Ababa, Ethiopia | 9.0300 | 38.7400 | Exhibits a single rainy season, typically June to September. |
Dakar, Senegal | 14.6900 | -17.4400 | West African unimodal rainfall pattern. | |
Very Wet Region | Libreville, Gabon | 0.3900 | 9.4500 | One of Africa’s wettest capitals, heavy equatorial rainfall. |
Kisangani, DRC | 0.5200 | 25.2000 | Located in the Congo Basin rainforest, very high rainfall. | |
Very Dry Region | Timbuktu, Mali | 16.7700 | -3.0000 | Located in the Sahara Desert region, extremely low rainfall, ideal for dry climate test. |
Aswan, Egypt | 24.0900 | 32.9000 | Hot desert climate with minimal precipitation. |
- Bimodal Regions: Equatorial East African locations such as Nairobi, Kisumu, and Dar es Salaam have two distinct rainy seasons due to the seasonal migration of the Inter-Tropical Convergence Zone (ITCZ).
- Unimodal Regions: Areas in the Sahel and parts of East and West Africa experience a single rainy season annually.
- Very Wet Regions: Equatorial rainforest zones like Gabon and Congo Basin receive heavy rainfall year-round.
- Very Dry Regions: Sahara Desert and parts of northern Africa (e.g., Timbuktu, Aswan) have negligible rainfall, ideal to test dry onset criteria.