Event-based climate indicators for public health analysis

Introduction

Climate and health research has traditionally represented environmental exposure using nominal meteorological variables, such as mean temperature or total precipitation, included as continuous covariates in epidemiological models. Although these indicators are widely available and straightforward to implement, they often inadequately represent the persistent, extreme, and threshold-exceeding conditions through which climate variability and change affect human health. Many health impacts are driven by discrete climate events - such as heat waves, cold spells, prolonged rainfall, and dry spells - whose effects depend on duration, intensity, and temporal clustering rather than on average conditions alone.

I propose here a method to compute, for each Brazilian municipality, a monthly or weekly set of event-based climate indicators and climatological normals.

Tip

In a hurry? Jump to the download section ;-)

Methods

A climatological normal can be computed with data from different sources, including remote sensing sensors and “area averages or points in gridded datasets” (WMO 2017). Some gridded climatological datasets are available for the Brazilian territory, including the ERA5-Land from Copernicus (Muñoz-Sabater et al. 2021) and the BR-DWGD dataset (Xavier et al. 2022), offering several climatological indicators for a long time range, and continuously updates.

Some research methods demands that climate data must be aggregated in the same spatial and temporal units of other data to be used in statistical models, being a fairly common procedure in epidemiology and economy studies. In order to approach this issue, spatial gridded data can be aggregated using zonal statistics (Saldanha et al. 2024).

For this dataset, climate zonal statistics from ERA5-Land for the Brazilian municipalities (described here) was used to compute climatological normals and monthly/weekly event indicators.

To compute the event-based indicators and the climatologial normals, an R package named {climindi} was created. The package provides helper functions to compute climatological normals and event-based climate indicators in a tidy way.

Normal indicators

The {climindi} package computes the average, 10th and 90th percentile as climatological normals.

Two climatologial normals were computed: 1961-1990 and 1981-2010.

Event-based indicators

Considering each climatological normal, a set of event-based indicators was computed using the {climindi} package for maximum and minimum temperature, total precipitation, average relative humidity and average wind speed.

For each climate indicator, a set of standard indicators and a set of specific indicators were computed.

Standard indicators

  • count Count of data points
  • mean Mean value
  • median Median
  • sd Standard deviation
  • se Standard error
  • max Maximum value
  • min Minimum value
  • p10 10th percentile
  • p25 25th percentile
  • p75 75th percentile
  • p90 90th percentile

Specific indicators

  • Precipitation
    • rs3 Rain spells: count of rain spells occurrences, with 3 and 5 or more consecutive days with rain above the climatological normal average value
    • p_1 Count of days with precipitation above 1mm
    • p_5 Count of days with precipitation above 5mm
    • p_10 Count of days with precipitation above 10mm
    • p_50 Count of days with precipitation above 50mm
    • p_100 Count of days with precipitation above 100mm
    • d_3 Count of sequences of 3 days or more without precipitation
    • d_5 Count of sequences of 5 days or more without precipitation
    • d_10 Count of sequences of 10 days or more without precipitation
    • d_15 Count of sequences of 15 days or more without precipitation
    • d_20 Count of sequences of 20 days or more without precipitation
    • d_25 Count of sequences of 25 days or more without precipitation
  • Maximum temperature
    • hw3 Count of heat waves occurrences with 3 or more consecutive days with maximum temperature above the climatological normal value plus 5 Celsius degrees
    • hw5 Count of heat waves occurrences with 5 or more consecutive days with maximum temperature above the climatological normal value plus 5 Celsius degrees
    • hot_days Count of hot days, when the maximum temperature is above the normal 90th percentile
    • t_25 Count of days with temperatures above or equal to 25 Celsius degrees
    • t_30 Count of days with temperatures above or equal to 30 Celsius degrees
    • t_35 Count of days with temperatures above or equal to 35 Celsius degrees
    • t_40 Count of days with temperatures above or equal to 40 Celsius degrees
  • Minimum temperature
    • cw3 Count of cold spells occurrences with 3 or more consecutive days with minimum temperature bellow the climatological normal value minus 5 Celsius degrees
    • cw5 Count of cold spells occurrences with 5 or more consecutive days with minimum temperature bellow the climatological normal value minus 5 Celsius degrees
    • cold_days Count of cold days, when the minimum temperature is bellow the normal 10th percentile
    • t_0 Count of days with temperatures bellow or equal to 0 Celsius degrees
    • t_5 Count of days with temperatures bellow or equal to 5 Celsius degrees
    • t_10 Count of days with temperatures bellow or equal to 10 Celsius degrees
    • t_15 Count of days with temperatures bellow or equal to 15 Celsius degrees
    • t_20 Count of days with temperatures bellow or equal to 20 Celsius degrees
  • Relative humidity
    • ds3 Count of dry spells occurrences with 3 or more consecutive days with relative humidity bellow the climatological normal value minus 10 percent
    • ds5 Count of dry spells occurrences with 5 or more consecutive days with relative humidity bellow the climatological normal value minus 10 percent
    • ws3 Count of wet spells occurrences with 3 or more consecutive days with relative humidity above the climatological normal value plus 10 percent
    • ws5 Count of wet spells occurrences with 5 or more consecutive days with relative humidity above the climatological normal value plus 10 percent
    • dry_days Count of dry days, when the relative humidity is bellow the normal 10th percentile
    • wet_days Count of wet days, when the relative humidity is above the normal 90th percentile
    • h_21_30 Count of days with relative humidity between 21% and 30% (Attention level)
    • h_12_20 Count of days with relative humidity between 12% and 20% (Alert level)
    • h_11 Count of days with relative humidity bellow 12% (Emergence level)
  • Wind speed
    • l_eto_3 Count of sequences of 3 or more consecutive days with wind speed bellow the climatological average normal
    • l_eto_5 Count of sequences of 5 or more consecutive days with wind speed bellow the climatological average normal
    • h_eto_3 Count of sequences of 3 or more consecutive days with wind speed above the climatological average normal
    • h_eto_5 Count of sequences of 5 or more consecutive days with wind speed above the climatological average normal
    • b1 Count of days classified as level 1 on Beaufort scale
    • b2 Count of days classified as level 2 on Beaufort scale
    • b3 Count of days classified as level 3 on Beaufort scale
    • b4 Count of days classified as level 4 on Beaufort scale
    • b5 Count of days classified as level 5 on Beaufort scale
    • b6 Count of days classified as level 6 on Beaufort scale
    • b7 Count of days classified as level 7 on Beaufort scale
    • b8 Count of days classified as level 8 on Beaufort scale
    • b9 Count of days classified as level 9 on Beaufort scale
    • b10 Count of days classified as level 10 on Beaufort scale
    • b11 Count of days classified as level 11 on Beaufort scale
    • b12 Count of days classified as level 12 on Beaufort scale
Important

The package functions needs data in specific units. Please certify that your data is in the required unit before running the functions.

The code used to compute this dataset is available here.

Dataset download

The climatological normals and event-based indicators be downloaded from Zenodo on CSV and parquet formats. Click the link bellow to access and download the data.

You can also download the dataset directly from R, using the {zendown} package.

Results preview

Let’s check some results using the monthly datasets and the 1981-2010 climatological normal.

Code
library(dplyr)
library(lubridate)
library(arrow)
library(zendown) # https://rfsaldanha.github.io/zendown/

Maximum temperature, Rio de Janeiro, RJ, 2024

Observed and normal

Code
# 2024 data
tmax_data <- zen_file(15748125, "2m_temperature_max.parquet") |>
  open_dataset() |>
  filter(name == "2m_temperature_max_mean") |>
  filter(code_muni == 3304557) |>
  select(-name) |>
  mutate(value = round(value - 273.15, digits = 2)) |>
  collect()

#  normals
tmax_normal <- zen_file(18612340, "tmax_monthly_normal_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 3304557) |>
  collect()
Code
library(ggplot2)
library(tidyr)

# Prepare data for graph
tmax_normal_exp <- tmax_normal |>
  mutate(date = as_date(paste0("2024-", month, "-01"))) |>
  group_by(month) %>%
  expand(
    date = seq.Date(
      floor_date(date, unit = "month"),
      ceiling_date(date, unit = "month") - days(1),
      by = "day"
    ),
    normal_mean,
    normal_p10,
    normal_p90
  ) |>
  pivot_longer(cols = starts_with("normal_")) |>
  mutate(name = substr(name, 8, 100))

# Plot
ggplot() +
  geom_line(data = tmax_data, aes(x = date, y = value)) +
  geom_line(data = tmax_normal_exp, aes(x = date, y = value, color = name)) +
  theme_bw() +
  labs(
    title = "Maximum temperatures and monthly climatological normals",
    subtitle = "Rio de Janeiro, RJ",
    color = "Normal (1981-2010)",
    x = "Date",
    y = "Celsius degrees"
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal")

Code
#  normals
tmax_normal <- zen_file(18612340, "tmax_weekly_normal_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 3304557) |>
  collect()

tmax_normal_exp <- tmax_normal |>
  filter_out(week == 53) |>
  mutate(date = as.Date(paste(2024, week, 1, sep = "-"), "%Y-%U-%u")) |>
  group_by(week) %>%
  expand(
    date = seq.Date(
      floor_date(date, unit = "week"),
      ceiling_date(date, unit = "week") - days(1),
      by = "day"
    ),
    normal_mean,
    normal_p10,
    normal_p90
  ) |>
  pivot_longer(cols = starts_with("normal_")) |>
  mutate(name = substr(name, 8, 100))

# Plot
ggplot() +
  geom_line(data = tmax_data, aes(x = date, y = value)) +
  geom_line(data = tmax_normal_exp, aes(x = date, y = value, color = name)) +
  theme_bw() +
  labs(
    title = "Maximum temperatures and weekly climatological normals, 2024",
    subtitle = "Rio de Janeiro, RJ",
    color = "Normal (1981-2010)",
    x = "Date",
    y = "Celsius degrees"
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal")

Indicators

Code
library(gt)

# Load event-based indicators and filter for 2024
zen_file(18612340, "tmax_monthly_indi_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 3304557) |>
  filter(year == 2024) |>
  select(-code_muni, -year) |>
  collect() |>
  gt() |>
  fmt_number(
    columns = where(is.double),
    decimals = 2,
    use_seps = FALSE
  )
month count normal_mean normal_p10 normal_p90 mean median sd se max min p10 p25 p75 p90 hw3 hw5 hot_days t_25 t_30 t_35 t_40
1.00 31 29.74 25.35 33.87 29.84 28.73 3.69 0.66 36.31 24.13 25.73 26.62 32.60 34.64 0 0 7 29 15 3 0
2.00 29 30.30 26.30 33.70 30.46 30.61 2.01 0.37 34.95 26.50 27.84 29.26 31.68 32.67 0 0 2 29 17 0 0
3.00 31 29.00 25.24 32.84 29.31 28.65 3.68 0.66 35.99 23.32 24.23 26.72 32.42 33.84 0 0 7 27 13 1 0
4.00 30 27.48 23.58 31.40 28.50 28.95 2.22 0.41 33.21 23.90 26.29 26.79 29.58 30.79 0 0 3 28 7 0 0
5.00 31 25.39 21.54 29.40 28.68 30.18 4.41 0.79 33.82 18.28 21.87 25.87 32.15 32.96 12 9 16 23 16 0 0
6.00 30 24.60 20.54 28.42 27.47 28.22 2.59 0.47 31.07 21.18 23.74 26.32 29.30 29.78 0 0 14 24 3 0 0
7.00 31 24.21 19.47 28.59 24.68 24.34 2.89 0.52 30.55 18.34 21.29 22.62 26.86 28.20 0 0 2 14 1 0 0
8.00 31 25.24 20.08 30.19 26.43 27.53 4.34 0.78 33.85 17.08 20.87 22.94 29.88 31.34 3 0 7 20 7 0 0
9.00 30 25.62 20.28 31.77 30.09 31.61 4.37 0.80 35.72 20.85 23.49 26.48 33.50 34.61 16 16 15 24 19 1 0
10.00 31 26.73 21.54 32.20 27.53 27.08 4.33 0.78 37.56 21.09 22.19 24.32 30.69 33.32 3 0 5 21 10 1 0
11.00 30 27.66 22.85 32.95 27.72 28.18 3.52 0.64 35.57 22.65 23.55 24.03 29.57 31.72 0 0 3 21 7 1 0
12.00 31 28.50 24.27 32.70 29.04 28.78 3.13 0.56 35.31 23.49 24.94 27.07 31.20 34.07 0 0 4 27 11 1 0

Minimum temperature, Urupema, SC, 2025

Observed and normal

Code
# 2025 data
tmin_data <- zen_file(18257037, "2m_temperature_min.parquet") |>
  open_dataset() |>
  filter(name == "2m_temperature_min_mean") |>
  filter(code_muni == 4218954) |>
  select(-name) |>
  mutate(value = round(value - 273.15, digits = 2)) |>
  collect()

# normals
tmin_normal <- zen_file(18612340, "tmin_monthly_normal_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 4218954) |>
  collect()
Code
library(ggplot2)
library(tidyr)

# Prepare data for graph
tmin_normal_exp <- tmin_normal |>
  mutate(date = as_date(paste0("2025-", month, "-01"))) |>
  group_by(month) %>%
  expand(
    date = seq.Date(
      floor_date(date, unit = "month"),
      ceiling_date(date, unit = "month") - days(1),
      by = "day"
    ),
    normal_mean,
    normal_p10,
    normal_p90
  ) |>
  pivot_longer(cols = starts_with("normal_")) |>
  mutate(name = substr(name, 8, 100))

# Plot
ggplot() +
  geom_line(data = tmin_data, aes(x = date, y = value)) +
  geom_line(data = tmin_normal_exp, aes(x = date, y = value, color = name)) +
  theme_bw() +
  labs(
    title = "Minimum temperatures and monthly climatological normals, 2025",
    subtitle = "Urupema, SC",
    color = "Normal (1981-2010)",
    x = "Date",
    y = "Celsius degrees"
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal")

Indicators

Code
library(gt)

# Load event-based indicators and filter for 2024
zen_file(18612340, "tmin_monthly_indi_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 4218954) |>
  filter(year == 2025) |>
  select(-code_muni, -year) |>
  collect() |>
  gt() |>
  fmt_number(
    columns = where(is.double),
    decimals = 2,
    use_seps = FALSE
  )
month count normal_mean normal_p10 normal_p90 mean median sd se max min p10 p25 p75 p90 cw3 cw5 cold_days t_0 t_5 t_10 t_15 t_20
1.00 31 15.14 12.28 17.54 15.33 15.66 1.88 0.34 18.32 11.61 12.63 13.97 16.89 17.43 0 0 2 0 0 0 14 31
2.00 28 15.28 12.32 17.60 17.20 17.24 1.06 0.20 19.05 14.64 15.67 16.69 17.83 18.56 0 0 0 0 0 0 1 28
3.00 31 14.41 11.66 16.83 15.02 15.16 1.96 0.35 18.33 10.25 12.52 14.06 16.34 17.16 0 0 2 0 0 0 14 31
4.00 30 12.00 7.94 15.42 11.80 11.94 2.54 0.46 17.04 4.81 8.87 10.38 13.31 14.50 0 0 2 0 1 5 27 30
5.00 31 8.68 3.73 13.00 9.65 10.30 2.85 0.51 13.15 1.84 6.40 8.66 11.44 12.66 0 0 2 0 2 13 31 31
6.00 30 7.21 1.81 11.74 7.06 7.74 3.74 0.68 11.59 −3.16 1.67 5.50 9.87 10.89 0 0 4 2 7 25 30 30
7.00 31 6.56 0.90 11.22 5.17 5.52 3.35 0.60 10.39 −2.30 0.03 2.83 7.95 8.77 3 0 4 3 12 30 31 31
8.00 31 7.69 2.54 12.31 7.52 7.81 3.04 0.55 12.17 0.38 3.04 5.44 9.82 11.12 0 0 2 0 7 24 31 31
9.00 30 8.63 4.13 12.91 8.60 8.45 3.13 0.57 13.89 3.32 5.15 6.06 10.88 12.47 0 0 2 0 3 18 30 30
10.00 31 10.75 6.74 14.51 10.38 10.20 2.65 0.48 14.60 4.17 6.60 8.74 12.73 13.30 0 0 4 0 1 14 31 31
11.00 30 12.30 8.86 15.87 11.80 12.20 2.47 0.45 18.30 7.40 9.00 9.66 13.66 14.09 0 0 3 0 0 10 29 30
12.00 31 13.86 10.52 16.73 15.01 15.00 2.74 0.49 18.92 8.10 11.12 13.36 17.62 18.15 0 0 1 0 0 1 16 31

Relative humidity, Aquidauana, MS, 2025

Observed and normal

Code
# 2025 data
rh_data <- zen_file(18392587, "rh_mean_mean_2025.parquet") |>
  open_dataset() |>
  filter(name == "rh_mean_mean") |>
  filter(code_muni == 5001102) |>
  select(-name) |>
  collect()

# normals
rh_normal <- zen_file(18612340, "rh_normal_monthly_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 5001102) |>
  collect()
Code
library(ggplot2)
library(tidyr)

# Prepare data for graph
rh_normal_exp <- rh_normal |>
  mutate(date = as_date(paste0("2025-", month, "-01"))) |>
  group_by(month) %>%
  expand(
    date = seq.Date(
      floor_date(date, unit = "month"),
      ceiling_date(date, unit = "month") - days(1),
      by = "day"
    ),
    normal_mean,
    normal_p10,
    normal_p90
  ) |>
  pivot_longer(cols = starts_with("normal_")) |>
  mutate(name = substr(name, 8, 100))

# Plot
ggplot() +
  geom_line(data = rh_data, aes(x = date, y = value)) +
  geom_line(data = rh_normal_exp, aes(x = date, y = value, color = name)) +
  theme_bw() +
  labs(
    title = "Relative humidity and monthly climatological normals, 2025",
    subtitle = "Aquidauana, MS",
    color = "Normal (1981-2010)",
    x = "Date",
    y = "%"
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal")

Indicators

Code
library(gt)

# Load event-based indicators and filter for 2024
zen_file(18612340, "rh_indi_monthly_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 5001102) |>
  filter(year == 2025) |>
  select(-code_muni, -year) |>
  collect() |>
  gt() |>
  fmt_number(
    columns = where(is.double),
    decimals = 2,
    use_seps = FALSE
  )
month count normal_mean normal_p10 normal_p90 mean median sd se max min p10 p25 p75 p90 ds3 ds5 ws3 ws5 dry_days wet_days h_21_30 h_12_20 h_11
1.00 31 94.68 91.25 97.34 91.45 91.21 2.17 0.39 95.33 87.38 88.97 89.84 92.85 94.07 0 0 31 31 16 0 0 0 0
2.00 28 94.67 91.91 96.94 90.79 90.98 2.45 0.46 95.57 86.42 87.98 89.07 92.00 94.48 0 0 28 28 20 0 0 0 0
3.00 31 94.56 91.05 97.15 90.96 91.42 2.82 0.51 96.12 85.12 86.95 89.34 92.72 94.63 0 0 31 31 13 0 0 0 0
4.00 30 92.75 88.18 96.66 94.16 94.48 2.23 0.41 97.20 88.90 91.43 92.64 96.09 96.76 0 0 30 30 0 4 0 0 0
5.00 31 91.80 87.10 96.39 89.74 89.85 2.40 0.43 96.11 85.46 86.56 87.96 91.22 92.33 0 0 31 31 4 0 0 0 0
6.00 30 91.07 86.18 95.61 91.15 91.31 2.67 0.49 94.76 85.37 87.23 89.86 93.40 94.03 0 0 30 30 1 0 0 0 0
7.00 31 88.53 82.60 94.04 85.44 85.26 3.06 0.55 90.98 80.61 81.60 83.28 87.24 90.24 0 0 31 31 6 0 0 0 0
8.00 31 86.03 78.81 93.23 82.89 81.60 5.53 0.99 95.47 74.91 76.23 78.52 86.29 90.47 0 0 31 31 9 2 0 0 0
9.00 30 87.23 79.36 94.28 80.01 79.12 4.54 0.83 87.78 71.26 74.75 76.57 83.64 86.14 5 5 30 30 16 0 0 0 0
10.00 31 90.65 84.38 95.84 85.64 84.62 5.23 0.94 95.15 76.96 79.13 82.03 89.62 92.37 3 0 31 31 13 0 0 0 0
11.00 30 92.34 87.10 96.51 88.29 87.95 3.74 0.68 97.73 80.45 84.79 85.75 90.22 92.39 0 0 30 30 12 1 0 0 0
12.00 31 93.95 90.05 97.09 92.69 92.77 2.12 0.38 97.33 88.03 90.16 91.42 93.66 95.40 0 0 31 31 3 1 0 0 0

Precipitation, Calçoene, AP, 2025

Observed and normal

Code
# 2025 data
prec_data <- zen_file(18257037, "total_precipitation_sum.parquet") |>
  open_dataset() |>
  filter(name == "total_precipitation_sum_mean") |>
  filter(code_muni == 1600204) |>
  mutate(value = round(value * 1000, digits = 2)) |>
  select(-name) |>
  collect()

# normals
prec_normal <- zen_file(18612340, "prec_monthly_normal_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 1600204) |>
  collect()
Code
library(ggplot2)
library(tidyr)

# Prepare data for graph
prec_normal_exp <- prec_normal |>
  mutate(date = as_date(paste0("2025-", month, "-01"))) |>
  group_by(month) %>%
  expand(
    date = seq.Date(
      floor_date(date, unit = "month"),
      ceiling_date(date, unit = "month") - days(1),
      by = "day"
    ),
    normal_mean,
    normal_p10,
    normal_p90
  ) |>
  pivot_longer(cols = starts_with("normal_")) |>
  mutate(name = substr(name, 8, 100))

# Plot
ggplot() +
  geom_line(data = prec_data, aes(x = date, y = value)) +
  geom_line(data = prec_normal_exp, aes(x = date, y = value, color = name)) +
  theme_bw() +
  labs(
    title = "Precipitation and monthly climatological normals, 2025",
    subtitle = "Calçoene, AP",
    color = "Normal (1981-2010)",
    x = "Date",
    y = "mm"
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal")

Indicators

Code
library(gt)

# Load event-based indicators and filter for 2024
zen_file(18612340, "prec_monthly_indi_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 5001102) |>
  filter(year == 2025) |>
  select(-code_muni, -year) |>
  collect() |>
  gt() |>
  fmt_number(
    columns = where(is.double),
    decimals = 2,
    use_seps = FALSE
  )
month count normal_mean normal_p10 normal_p90 mean median sd se max min p10 p25 p75 p90 rs3 rs5 p_1 p_5 p_10 p_50 p_100 d_3 d_5 d_10 d_15 d_20 d_25
1.00 30 8.68 0.47 19.71 3.15 1.77 3.63 0.66 14.70 0.13 0.34 0.79 4.00 7.55 0 0 21 7 2 0 0 0 0 0 0 0 0
2.00 27 6.64 0.43 15.06 3.17 2.28 3.97 0.76 19.15 0.07 0.29 0.75 3.85 6.93 3 0 19 5 1 0 0 0 0 0 0 0 0
3.00 30 5.71 0.25 13.44 2.10 1.17 2.58 0.47 11.25 0.02 0.07 0.37 3.06 4.48 0 0 17 2 1 0 0 0 0 0 0 0 0
4.00 29 3.19 0.00 10.02 5.30 2.90 5.95 1.10 18.28 0.06 0.22 0.60 8.44 15.37 5 5 19 11 7 0 0 0 0 0 0 0 0
5.00 30 2.85 0.00 9.89 1.55 0.00 5.93 1.08 29.97 0.00 0.00 0.00 0.15 0.53 0 0 2 2 2 0 0 3 1 0 0 0 0
6.00 29 1.53 0.00 3.84 1.04 0.04 3.12 0.58 13.98 0.00 0.00 0.00 0.56 0.96 0 0 3 2 2 0 0 2 0 0 0 0 0
7.00 30 1.02 0.00 1.98 0.10 0.00 0.51 0.09 2.80 0.00 0.00 0.00 0.00 0.00 0 0 1 0 0 0 0 1 1 1 1 1 1
8.00 30 1.19 0.00 2.20 0.92 0.00 4.56 0.83 25.04 0.00 0.00 0.00 0.01 0.47 0 0 2 1 1 0 0 2 1 1 0 0 0
9.00 29 2.81 0.00 9.09 0.27 0.00 0.75 0.14 3.29 0.00 0.00 0.00 0.03 0.76 0 0 3 0 0 0 0 4 1 0 0 0 0
10.00 30 4.73 0.00 14.63 2.15 0.31 3.98 0.73 18.32 0.00 0.01 0.03 2.08 7.41 0 0 10 5 1 0 0 0 0 0 0 0 0
11.00 29 6.01 0.02 16.21 4.04 0.14 9.36 1.74 39.31 0.00 0.00 0.01 2.06 14.92 0 0 10 4 4 0 0 1 0 0 0 0 0
12.00 30 7.76 0.23 17.82 7.08 2.46 11.00 2.01 41.39 0.16 0.41 0.93 5.87 29.58 4 0 22 11 5 0 0 0 0 0 0 0 0

Wind speed, Caetité, BA, 2025

Observed and normal

Code
# 2025 data
ws_data <- zen_file(18390794, "wind_speed_mean_mean_2025.parquet") |>
  open_dataset() |>
  filter(name == "wind_speed_mean_mean") |>
  filter(code_muni == 1600204) |>
  select(-name) |>
  collect()

# normals
ws_normal <- zen_file(18612340, "ws_monthly_normal_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 1600204) |>
  collect()
Code
library(ggplot2)
library(tidyr)

# Prepare data for graph
ws_normal_exp <- ws_normal |>
  mutate(date = as_date(paste0("2025-", month, "-01"))) |>
  group_by(month) %>%
  expand(
    date = seq.Date(
      floor_date(date, unit = "month"),
      ceiling_date(date, unit = "month") - days(1),
      by = "day"
    ),
    normal_mean,
    normal_p10,
    normal_p90
  ) |>
  pivot_longer(cols = starts_with("normal_")) |>
  mutate(name = substr(name, 8, 100))

# Plot
ggplot() +
  geom_line(data = ws_data, aes(x = date, y = value)) +
  geom_line(data = ws_normal_exp, aes(x = date, y = value, color = name)) +
  theme_bw() +
  labs(
    title = "Wind speed and monthly climatological normals, 2025",
    subtitle = "Caetité, BA",
    color = "Normal (1981-2010)",
    x = "Date",
    y = "m/s"
  ) +
  theme(legend.position = "bottom", legend.direction = "horizontal")

Indicators

Code
library(gt)

# Load event-based indicators and filter for 2024
zen_file(18612340, "ws_monthly_indi_n1981_2010.parquet") |>
  open_dataset() |>
  filter(code_muni == 5001102) |>
  filter(year == 2025) |>
  select(-code_muni, -year) |>
  collect() |>
  gt() |>
  fmt_number(
    columns = where(is.double),
    decimals = 2,
    use_seps = FALSE
  )
month count normal_mean normal_p10 normal_p90 mean median sd se max min p10 p25 p75 p90 l_u2_3 l_u2_5 h_u2_3 h_u2_5 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 b10 b11 b12
1.00 31 5.15 2.29 8.50 4.59 4.11 2.08 0.37 9.96 1.26 2.36 2.87 6.12 6.65 16 5 8 5 0.00 1.00 10.00 7.00 11.00 2.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
2.00 28 4.64 1.91 7.87 5.65 5.76 2.34 0.44 10.43 1.35 1.69 4.49 7.14 8.42 7 7 20 20 0.00 3.00 1.00 5.00 12.00 5.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
3.00 31 4.11 1.67 6.86 4.10 4.33 1.50 0.27 6.23 0.95 1.81 3.41 5.12 6.09 3 0 13 13 0.00 2.00 5.00 16.00 6.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
4.00 30 5.14 2.29 8.07 4.24 4.20 2.12 0.39 10.54 0.82 1.85 2.76 5.12 6.84 22 21 5 5 0.00 3.00 7.00 13.00 5.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
5.00 31 6.02 2.86 9.12 6.67 6.35 1.93 0.35 11.60 4.28 4.57 5.02 7.98 9.07 10 6 15 12 0.00 0.00 0.00 11.00 11.00 7.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00
6.00 30 6.53 3.30 9.75 6.13 6.37 2.94 0.54 12.50 1.29 2.64 3.48 8.31 9.40 10 0 7 0 0.00 2.00 5.00 3.00 11.00 7.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00
7.00 31 7.32 3.62 10.92 6.84 6.49 1.88 0.34 11.19 4.22 4.91 5.38 8.18 9.30 18 18 8 0 0.00 0.00 0.00 8.00 12.00 7.00 2.00 0.00 0.00 0.00 0.00 0.00 0.00
8.00 31 6.86 3.03 10.67 7.84 7.18 2.96 0.53 13.25 2.86 3.59 6.06 10.09 11.41 7 0 14 7 0.00 0.00 2.00 3.00 11.00 7.00 6.00 0.00 0.00 0.00 0.00 0.00 0.00
9.00 30 6.96 3.23 10.65 7.06 6.89 3.36 0.61 17.21 2.30 3.39 4.58 9.03 10.46 10 10 11 11 0.00 0.00 3.00 9.00 7.00 8.00 2.00 0.00 1.00 0.00 0.00 0.00 0.00
10.00 31 5.77 2.19 9.64 7.90 8.30 3.30 0.59 13.65 1.29 3.01 6.04 10.07 11.99 3 0 22 16 0.00 1.00 4.00 1.00 7.00 10.00 6.00 0.00 0.00 0.00 0.00 0.00 0.00
11.00 30 5.38 1.95 9.27 5.15 5.34 2.19 0.40 11.79 1.20 2.66 3.38 6.38 7.27 9 5 6 6 0.00 1.00 7.00 8.00 11.00 1.00 1.00 0.00 0.00 0.00 0.00 0.00 0.00
12.00 31 5.32 2.10 8.82 6.17 6.48 2.77 0.50 11.82 0.98 2.86 3.91 7.75 9.74 13 5 17 17 0.00 1.00 3.00 7.00 11.00 5.00 2.00 0.00 0.00 0.00 0.00 0.00 0.00

Session info

sessioninfo::session_info()
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.5.2 (2025-10-31)
 os       macOS Tahoe 26.2
 system   aarch64, darwin20
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       America/Sao_Paulo
 date     2026-02-12
 pandoc   3.6.3 @ /Applications/Positron.app/Contents/Resources/app/quarto/bin/tools/aarch64/ (via rmarkdown)
 quarto   1.8.27 @ /Applications/Positron.app/Contents/Resources/app/quarto/bin/quarto

─ Packages ───────────────────────────────────────────────────────────────────
 package      * version  date (UTC) lib source
 arrow        * 22.0.0.1 2025-12-23 [1] CRAN (R 4.5.2)
 assertthat     0.2.1    2019-03-21 [1] CRAN (R 4.5.0)
 backports      1.5.0    2024-05-23 [1] CRAN (R 4.5.0)
 bit            4.6.0    2025-03-06 [1] CRAN (R 4.5.0)
 bit64          4.6.0-1  2025-01-16 [1] CRAN (R 4.5.0)
 checkmate      2.3.4    2026-02-03 [1] CRAN (R 4.5.2)
 cli            3.6.5    2025-04-23 [1] CRAN (R 4.5.0)
 digest         0.6.39   2025-11-19 [1] CRAN (R 4.5.2)
 dplyr        * 1.2.0    2026-02-03 [1] CRAN (R 4.5.2)
 evaluate       1.0.5    2025-08-27 [1] CRAN (R 4.5.0)
 farver         2.1.2    2024-05-13 [1] CRAN (R 4.5.0)
 fastmap        1.2.0    2024-05-15 [1] CRAN (R 4.5.0)
 fs             1.6.6    2025-04-12 [1] CRAN (R 4.5.0)
 generics       0.1.4    2025-05-09 [1] CRAN (R 4.5.0)
 ggplot2      * 4.0.2    2026-02-03 [1] CRAN (R 4.5.2)
 glue           1.8.0    2024-09-30 [1] CRAN (R 4.5.0)
 gt           * 1.3.0    2026-01-22 [1] CRAN (R 4.5.2)
 gtable         0.3.6    2024-10-25 [1] CRAN (R 4.5.0)
 htmltools      0.5.9    2025-12-04 [1] CRAN (R 4.5.2)
 htmlwidgets    1.6.4    2023-12-06 [1] CRAN (R 4.5.0)
 jsonlite       2.0.0    2025-03-27 [1] CRAN (R 4.5.0)
 knitr          1.51     2025-12-20 [1] CRAN (R 4.5.2)
 labeling       0.4.3    2023-08-29 [1] CRAN (R 4.5.0)
 lifecycle      1.0.5    2026-01-08 [1] CRAN (R 4.5.2)
 lubridate    * 1.9.5    2026-02-04 [1] CRAN (R 4.5.2)
 magrittr       2.0.4    2025-09-12 [1] CRAN (R 4.5.0)
 otel           0.2.0    2025-08-29 [1] CRAN (R 4.5.0)
 pillar         1.11.1   2025-09-17 [1] CRAN (R 4.5.0)
 pkgconfig      2.0.3    2019-09-22 [1] CRAN (R 4.5.0)
 purrr          1.2.1    2026-01-09 [1] CRAN (R 4.5.2)
 R6             2.6.1    2025-02-15 [1] CRAN (R 4.5.0)
 RColorBrewer   1.1-3    2022-04-03 [1] CRAN (R 4.5.0)
 rlang          1.1.7    2026-01-09 [1] CRAN (R 4.5.2)
 rmarkdown      2.30     2025-09-28 [1] CRAN (R 4.5.0)
 S7             0.2.1    2025-11-14 [1] CRAN (R 4.5.2)
 sass           0.4.10   2025-04-11 [1] CRAN (R 4.5.0)
 scales         1.4.0    2025-04-24 [1] CRAN (R 4.5.0)
 sessioninfo    1.2.3    2025-02-05 [1] CRAN (R 4.5.0)
 tibble         3.3.1    2026-01-11 [1] CRAN (R 4.5.2)
 tidyr        * 1.3.2    2025-12-19 [1] CRAN (R 4.5.2)
 tidyselect     1.2.1    2024-03-11 [1] CRAN (R 4.5.0)
 timechange     0.4.0    2026-01-29 [1] CRAN (R 4.5.2)
 vctrs          0.7.1    2026-01-23 [1] CRAN (R 4.5.2)
 withr          3.0.2    2024-10-28 [1] CRAN (R 4.5.0)
 xfun           0.56     2026-01-18 [1] CRAN (R 4.5.2)
 xml2           1.5.2    2026-01-17 [1] CRAN (R 4.5.2)
 yaml           2.3.12   2025-12-10 [1] CRAN (R 4.5.2)
 zendown      * 0.1.0    2024-05-21 [1] CRAN (R 4.5.0)

 [1] /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library
 * ── Packages attached to the search path.

──────────────────────────────────────────────────────────────────────────────
Back to top

References

Muñoz-Sabater, Joaquín, Emanuel Dutra, Anna Agustí-Panareda, Clément Albergel, Gabriele Arduini, Gianpaolo Balsamo, Souhail Boussetta, et al. 2021. “ERA5-Land: A State-of-the-Art Global Reanalysis Dataset for Land Applications.” Earth System Science Data 13 (9): 4349–83. https://doi.org/10.5194/essd-13-4349-2021.
Saldanha, Raphael, Reza Akbarinia, Marcel Pedroso, Victor Ribeiro, Carlos Cardoso, Eduardo H. M. Pena, Patrick Valduriez, and Fabio Porto. 2024. “Zonal Statistics Datasets of Climate Indicators for Brazilian Municipalities.” Environmental Data Science 3: e2. https://doi.org/10.1017/eds.2024.3.
WMO. 2017. WMO Guidelines on the Calculation of Climate Normals. Geneva: WMO.
Xavier, Alexandre C., Bridget R. Scanlon, Carey W. King, and Aline I. Alves. 2022. “New Improved Brazilian Daily Weather Gridded Data (1961).” International Journal of Climatology, December. https://doi.org/10.1002/joc.7731.