Getting Started with rmet

Brazil’s National Institute of Meteorology (INMET) runs a network of more than 600 automatic weather stations that record hourly observations. Historical data are published as one ZIP archive per year, holding one CSV file per station, with formats that changed several times over the years. rmet downloads and caches those archives, reads them into consistent data frames, and summarises the hourly data into daily values.

install.packages("rmet")
library(rmet)

Find a station

inmet_stations() returns the official catalogue of automatic stations. It is downloaded from INMET once and cached; when INMET cannot be reached, a snapshot bundled with the package is used instead.

rs <- inmet_stations(state = "RS", status = "Operante", source = "bundled")
head(rs[, c("station_code", "station_name", "latitude", "longitude", "start_date")])
#>   station_code             station_name  latitude longitude start_date
#> 1         B843                AJURICABA -28.22083 -53.67361 2025-12-02
#> 2         B847       ALEGRETE (MARONNA) -29.82139 -55.88722 2026-02-04
#> 3         A827                     BAGE -31.34778 -54.01333 2007-01-04
#> 4         B827            BAGE - CENTRO -31.30556 -54.01333 2025-08-01
#> 5         A840          BENTO GONCALVES -29.16444 -51.53417 2006-12-01
#> 6         B857 BOM JESUS - SANTO INACIO -28.51389 -50.16778 2026-03-03

(source = "bundled" keeps this vignette reproducible offline. In your own code, leave source at its default to get the current catalogue.)

To find the stations closest to a place, use inmet_nearest():

inmet_nearest(latitude = -30.03, longitude = -51.23, n = 3, source = "bundled")[
  , c("station_code", "station_name", "distance_km", "start_date")
]
#>   station_code                   station_name distance_km start_date
#> 1         A801 PORTO ALEGRE - JARDIM BOTANICO    5.933358 2000-09-22
#> 2         B808                   CACHOEIRINHA   13.427377 2025-02-18
#> 3         B825      PORTO ALEGRE - BELEM NOVO   18.056243 2025-05-01

Station A801 (Porto Alegre - Jardim Botanico) has been operating since September 2000. The catalogue also gives each station’s local time_zone.

With the sf package installed, inmet_as_sf() turns the catalogue (or any result with coordinates) into spatial points:

plot(inmet_as_sf(rs)["elevation"], pch = 19, main = "")
Operating automatic stations in Rio Grande do Sul, by elevation.

Operating automatic stations in Rio Grande do Sul, by elevation.

Get hourly data

inmet_get() downloads any missing archives and reads the stations you ask for:

poa <- inmet_get(
  years = 2023,
  stations = "A801",
  variables = c("temp_dry_c", "temp_max_prev_c", "temp_min_prev_c", "precip_mm")
)

The first call downloads the archive for each year, around 100 MB for recent years; later calls read from the cache. For the current and the previous year, rmet also checks whether INMET has published a newer version of the archive (INMET keeps adding data to them) and downloads it if so.

The package ships the result of inmet_get(2023, stations = "A801") as rmet_example, which the rest of this vignette uses:

dim(rmet_example)
#> [1] 8663   25
head(rmet_example[, c("datetime", "temp_dry_c", "temp_max_prev_c", "precip_mm")])
#>              datetime temp_dry_c temp_max_prev_c precip_mm
#> 1 2023-01-01 00:00:00       26.5            27.7         0
#> 2 2023-01-01 01:00:00       26.8            26.9         0
#> 3 2023-01-01 02:00:00       26.3            26.9         0
#> 4 2023-01-01 03:00:00       25.2            26.6         0
#> 5 2023-01-01 04:00:00       24.1            25.2         0
#> 6 2023-01-01 05:00:00       23.1            24.1         0

Timestamps are in UTC, as in the source files, and each row describes the hour ending at datetime. Pass tz = "America/Sao_Paulo" (or any other Olson name) to inmet_get() or inmet_read() to display them in local time.

Quality control and completeness

INMET publishes these observations as raw data. inmet_qc() flags values that are physically impossible, internally inconsistent, isolated spikes, or stuck at the same value, and inmet_completeness() shows how many valid hours each month has:

checked <- inmet_qc(rmet_example)
#> Flagged 0 value(s) in 0 variable(s). See attr(x, "qc_summary").
head(inmet_completeness(checked, variables = c("temp_dry_c", "precip_mm")), 3)
#>   station_code  period hours_expected hours_present temp_dry_c precip_mm
#> 1         A801 2023-01            744           734       98.7      98.7
#> 2         A801 2023-02            672           666       99.1      99.1
#> 3         A801 2023-03            744           734       98.7      98.7

Daily summaries

Most analyses need daily values. inmet_daily() assigns each hour to the station’s local day, ignores values flagged by inmet_qc(), and requires a minimum number of valid hours per day (20 by default):

daily <- inmet_daily(checked)
head(daily[, c("date", "n_hours", "temp_max_c", "temp_min_c", "precip_mm")])
#>         date n_hours temp_max_c temp_min_c precip_mm
#> 1 2022-12-31       4         NA         NA        NA
#> 2 2023-01-01      24       37.0       20.8       0.0
#> 3 2023-01-02      24       33.6       23.6      10.2
#> 4 2023-01-03      24       30.0       20.8       0.0
#> 5 2023-01-04      24       32.9       20.0       0.0
#> 6 2023-01-05      24       33.0       19.3       0.0

The first row is 31 December 2022: the records stamped 00:00 to 03:00 UTC on 1 January 2023 describe the last hours of 31 December in Porto Alegre. With only four hours of data that day’s values are NA. See vignette("daily-data", package = "rmet") for the details and why as.Date(datetime) gives the wrong days.

daily <- daily[format(daily$date, "%Y") == "2023", ]
oldpar <- par(mar = c(4, 4.5, 2, 1))
plot(daily$date, daily$temp_max_c, type = "l", col = "#C0392B",
     ylim = range(daily$temp_min_c, daily$temp_max_c, na.rm = TRUE),
     xlab = "", ylab = "Temperature (°C)")
lines(daily$date, daily$temp_min_c, col = "#2471A3")
legend("topright", c("Maximum", "Minimum"), col = c("#C0392B", "#2471A3"),
       lwd = 1, bty = "n")
Daily maximum and minimum temperature at Porto Alegre (A801), 2023.

Daily maximum and minimum temperature at Porto Alegre (A801), 2023.

par(oldpar)
monthly <- tapply(daily$precip_mm, format(daily$date, "%m"), sum, na.rm = TRUE)
oldpar <- par(mar = c(4, 4.5, 2, 1))
barplot(monthly, names.arg = month.abb, col = "#5DADE2", border = NA,
        ylab = "Precipitation (mm)")
Monthly precipitation at Porto Alegre (A801), 2023.

Monthly precipitation at Porto Alegre (A801), 2023.

par(oldpar)

Days with missing precipitation hours are NA in daily$precip_mm, so these monthly totals are lower bounds when data are missing; check n_hours.

Step-by-step workflow

inmet_get() combines three steps that can also be run separately:

inmet_download(2020:2023)        # download (or update) archives
inmet_cache_status()             # what is cached, and how large
weather <- inmet_read(
  years = 2020:2023,
  stations = c("A801", "A832"),
  start_date = "2020-06-01",
  end_date = "2023-08-31",
  variables = c("temp_dry_c", "precip_mm")
)

Reading only the stations and variables you need keeps memory use low: one year of every station is about four million rows. inmet_extract() copies the original CSV files out of the archives if you need them for other software, and inmet_cache_clear() frees disk space.

Data quality

INMET publishes automatic-station data as raw measurements without consistency checks. inmet_qc() catches gross errors, but it cannot judge every value. Apply further checks appropriate to your analysis, such as comparison with neighbouring stations, before drawing conclusions.

The variables returned and their units are listed in ?inmet_read; daily variables are described in ?inmet_daily.