Skip to content
← Back to projects

Case study · Data Engineering and Data Science

How much has Argentina warmed?

85 years of daily temperature from 27 cities, from Jujuy to Ushuaia, turned into a dashboard that updates itself. This is how I built it, from the API to the chart.

+0.8 °C

warmer than the 1961–1990 period (average of the last 10 years) (2016–2025)

Hover or tap a year to see its value.

1The problem

I wanted to answer a simple question with data: how much has Argentina warmed, and where?

How much
How much warmer the last 10 years are than the 1961–1990 reference period.
Where
Which areas warmed the most: the north, Cuyo, the Pampas or Patagonia.
When
Whether days or nights warmed more, and which months changed the most.
Extremes
How many hot days (≥ 30 °C) and frosts there are today compared with before.

2The data

The data comes from the ECMWF/Copernicus ERA5 reanalysis, through the free Open-Meteo API: daily temperature since 1940 for the 24 provincial capitals plus three more cities. Every decision aims to keep the series comparable over time.

Pure ERA5
The whole series comes from the same model, not from an automatic mix of sources. Mixing models of different resolution could create an artificial jump in an 85-year trend.
1961–1990 reference
It is the WMO standard period for studying climate change.
Trend since 1980
Before the satellite era ERA5 has fewer observations, so those years are less certain.
Complete years only
A partial year biases the average: it is missing the summer or the winter.

3How I built it

It is an ELT pipeline: extract, load raw, and transform inside the data warehouse. Every stage can be re-run without duplicating or breaking anything.

  1. ERA5 API

    Open-Meteo, ECMWF reanalysis

  2. Extract

    Per city and decade, with retries

  3. DuckDB

    Idempotent load with upsert

  4. dbt

    Staging, intermediate and marts, with tests

  5. Dashboard

    Static site on GitHub Pages

Extract
Requests the API per city and decade, retries errors with growing waits, respects quota limits and can resume where it stopped. Each response is stored as received, with metadata about what was requested and when.
Load
DuckDB unpacks the JSON into one row per day. The load is idempotent: if a day arrives twice, the most recent extraction wins.
Transform
dbt in three layers (staging, intermediate and marts). The fact table is incremental, so it picks up corrections and new cities without recomputing everything.
90-day refresh
The latest months of ERA5 are preliminary and corrected later, so every run requests them again and replaces the values.
Dashboard
A static site. GitHub Actions runs the pipeline on Mondays and Thursdays and publishes it to GitHub Pages. The heat map grid is computed once, because doing it in the browser froze the page for about 2.5 seconds.

4Quality and limits

Bad data shows up late, so every layer has its own checks. I also wrote down what the project cannot claim.

dbt tests
Unique keys, non-null values and relationships between tables; physically possible ranges; minimum ≤ mean ≤ maximum temperature; no missing days between the first and last data point.
Freshness
Warns if the latest data is more than 16 days old and fails at 30.
Python tests
18 tests covering chunk planning, resuming, retries, idempotency and the newest file winning.
Limit: the grid
ERA5 is a roughly 25 km grid, not a weather station: in mountains or on the coast the cell can differ from the local value.
Limit: the average
The national average is a simple mean across 27 cities. Small provinces in the center and the Litoral weigh more than their area, and Patagonia weighs less.

5The result, year by year

Each year is the difference between the country's mean temperature and that of 1961–1990 (the thin line). The thick line is the 10-year moving average: it has risen steadily since 2000.

Hover or tap a year to see its value.

View the dashboardView the repository