Es evidente que estamos en una fase de calentamiento global provocado por causas antropogénicas.
El consenso es muy claro:
“Scientific evidence for warming of the climate system is unequivocal.”
Intergovernmental Panel on Climate Change
El cambio climático es un problema global extremadamente complejo con muchas interacciones entre muchos sistemas distintos y hay veces en las cuales es complicado poder enviar un mensaje claro, informativo e inequívoco.
Pero, gracias a que existen excelentes divulgadores, podemos recurrir a algunas herramientas que nos permitan lanzar ese mensaje claro, informativo e inequívoco.
Lo que vamos a ver hoy es como calcular las warming stripes o bandas del calentamiento usando Matplotlib, Seaborn y Python.
Pero…
¿Qué son las bandas del calentamiento?
Las bandas del calentamiento fueron ideadas por el climatólogo Ed Hawkins, trabajador del MetOffice, para poder explicar de forma visual el calentamiento global al público en general sin tener que entrar en términos técnicos y de forma extremadamente
visual.
Dibujando las warming stripes
Deberéis tener instalado xarray
, netcdf4
, pandas
, seaborn
y todas sus dependencias (numpy
, matplotlib
,…).
Los datos
Las bandas de calentamiento muestran anomalías de temperatura en la superficie del planeta. Los datos de temperaturas en todo el globo con registros que se extienden más allá de un siglo se pueden obtener de varias fuentes y todas ellas muestran similares tendencias. Podríais usar la información generada por, por ejemplo, grandes centros de investigación como:
En este caso voy a usar los de la NOAA. Para descargarlos podemos hacer lo siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
from urllib.request import urlretrieve, urlopen from html.parser import HTMLParser from typing import Union, List, Tuple, Optional, Any URL: str = ('https://www.ncei.noaa.gov/' 'data/noaa-global-surface-temperature/' 'v5/access/gridded/') FILENAME: str = 'NOAA_V5_air_temperature_anomaly.nc' class MyParser(HTMLParser): def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) self.nc_files: List[str] = [] def handle_starttag( self, tag: str, attrs: List[Any] ) -> None: if tag == "a": # Check the list of defined attributes. for name, value in attrs: if name and value and name == "href" and value.endswith('.nc'): self.nc_files.append(value) def download_noaa(url: str, filename: str) -> None: """Get version 5 of NOAA air temperature anomaly. Parameters ---------- url : str Url of the site: https://www.ncei.noaa.gov/data/noaa-global-surface-temperature/v5/access/gridded/. filename : str The filename to be used to name the downloaded file. Returns ------- None. """ html = urlopen(url).read() parser = MyParser() parser.feed(str(html)) _filename = parser.nc_files[-1] urlretrieve(url + _filename, filename) |
El codigo anterior quizá es un poco complejo pero tiene una razón de ser. Si visitais la url definida por URL
veréis que hay un enlace a un fichero NetCDF pero este fichero se va actualizando (cada mes) al igual que el nombre del mismo. Para que el código funcione cuando alguien visite este artículo en el futuro he escrito un código un poco más complejo que parsea la página web para buscar el enlace y devuelve el enlace correcto. La función download_noaa
hace uso de la clase MyParser
para descargar el fichero NetCDF con el nombre contenido en FILENAME
.
Lectura de los datos
Una vez que hemos conseguido unos datos debemos leerlos. Genial, vamos a generar código para hacer lo anterior:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
import xarray as xr import pandas as pd def read_noaa(filename: str) -> xr.Dataset: """Read the netCDF file downloaded using `download_noaa`. Parameters ---------- filename : str The name of the file to read as `xarray.Dataset` Returns ------- `xarray.Dataset` """ return xr.open_dataset(filename) def get_noaa_timeseries( xarr: xr.Dataset, lon: Union[int, float], lat: Union[int, float] ) -> xr.Dataset: """Get the annual temperature anomaly time series from NOAA data. Parameters ---------- xarr : xr.Dataset `xarray.Dataset` containing the monthly anomalies. lon : Union[int, float] Longitude in decimal degrees. It will return the closest timeseries to this location. lat : Union[int, float] Latitude in decimal degrees. It will return the closest timeseries to this location. Returns ------- `xarray.Dataset`. """ data = xarr.sel(lon=lon, lat=lat, z=0, method='nearest') df = data.to_dataframe()['anom'] ts = df * df.index.days_in_month ts = ( ts.groupby(pd.Grouper(freq='Y')).mean() / ts.groupby(pd.Grouper(freq='Y')).count() ) return ts |
La primera función, read_noaa
, es simplemente una abstracción de la lectura del fichero NetCDF leido con ayuda de xarray
. Como nunca he hablado de xarray
en el blog he preferido dejarlo de esa forma.
La segunda función sirve para extraer la serie temporal de anomalías de temperatura más cercana a la posición definida mediante lon
y lat
(logitud y latitud geográficas).
Creando el gráfico
Descargados los datos y sabiendo como obtener una serie temporal en una localización ya lo tenemos todo para crear el gráfico. Vamos a ello:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import matplotlib.pyplot as plt import seaborn as sns import numpy as np def plot_noaa( xarr: xr.Dataset, lon: Union[int, float], lat: Union[int, float] ) -> None: lon = float(lon) lat = float(lat) ts = get_noaa_timeseries(xarr, lon, lat) # warming stripes adapted from https://towardsdatascience.com/climate-heatmaps-made-easy-6ec5be0be6ff fig, ax = plt.subplots(figsize=(12, 5)) sns.heatmap( data=ts.values[np.newaxis,:], ax=ax, cmap='RdBu_r', cbar=False, vmin=ts.min(), vmax=ts.max(), center=0., xticklabels=False, yticklabels=False, ) fig.tight_layout() fig.savefig("warming_stripes.png") |
La nueva función creará el gráfico que deseábamos y lo guardará con el nombre warming_stripes.png.
Resultado final
Voy a obtener el gráfico para la serie temporal más cercana a la posición (longitud, latitud) = (2.5º, 39.5º), cercana a Mallorca. Para ello solo tengo que usar las funciones anteriores de la siguiente forma:
1 2 3 |
download_noaa(URL, FILENAME) xarr = read_noaa(FILENAME) plot_noaa(xarr, 2.5, 39.5) |
Si todo ha funcionado correctamente el resultado final debería ser algo como lo siguiente:

¿Quieres conocer más sobre el cambio climático?
- https://climate.nasa.gov/evidence/
- https://www.ipcc.ch/report/ar5/wg1/
- https://es.wikipedia.org/wiki/Calentamiento_global
- Pregunta en los comentarios e intentaré responderte lo mejor que pueda
- QuantumFracture en YT
- Especial del Podcast “Coffee Break: Señal y Ruido” sobre cambio climático.
- …