How to work with Climate Adaptation Digital Twin data on Earth Data Hub. High resolution fields on a single level or surface: climatological analysis of temperature in Germany¶

This notebook will provide you guidance on how to access and use the https://cacheb.dcms.destine.eu/d1-climate-dt/ScenarioMIP-SSP3-7.0-IFS-NEMO-0001-high-sfc-v0.zarr dataset. Access to this dataset is restricted to authorized user only via the the Data Cache Management service.

To access the data you need to instruct your tools (e.g. Xarray, Zarr...) to use the Data Cache Management service. This can be done by running the code snippets below.

First, make sure you have an account on the Destination Earth platform. Then run the following cell, filling in your Destination Earth credentials and password when asked:

In [1]:
%%capture cap
%run ../cacheb/cacheb-authentication.py
In [2]:
from pathlib import Path
with open(Path.home() / ".netrc", "a") as fp:
    fp.write(cap.stdout)

⚠ NOTE: the generated password is valid for a limited period of time and needs to be regenerated and reconfigured periodically by running the cells above.

What you will learn:¶

  • how to access the dataset
  • select and reduce the data
  • plot the results

In this notebook we set two goals:

Our first goal is to plot the mean 2 metre temperature in January 2025 over Central Europe (Germany).

Our second goal is to compute the 2 metre temperature climatology (monthly means and standard deviations) in Berlin for the 2020-2028 reference period.

Working with EDH data¶

Datasets on EDH are typically very large and remotely hosted. Typical use imply a selection of the data followed by one or more reduction steps to be performed in a local or distributed Dask environment.

The structure of a workflow that uses EDH data tipically looks like this:

  • data access
  • data selection
  • (optional) data reduction
  • data download
  • further operations and visualization

Xarray and Dask work together following a lazy principle. This means that when you access and manipulate a Zarr store the data is in not immediately downloaded and loaded in memory. Instead, Dask constructs a task graph that represents the operations to be performed. A smart user will first reduce the amount of data that needs to be downloaded and explicitly call compute() on it. Once the compute() operation is complete the data is loaded into memory and available for subsequent fast processing.

1. Data access¶

To access the data, only the dataset metadata must be downloaded. Xarray does this automatically when you access a Zarr dataset:

In [3]:
import xarray as xr

ds = xr.open_dataset(
    "https://cacheb.dcms.destine.eu/d1-climate-dt/ScenarioMIP-SSP3-7.0-IFS-NEMO-0001-high-sfc-v0.zarr",
    chunks={},
    engine="zarr",
    storage_options={"client_kwargs": {"trust_env": True}},
)
ds
Out[3]:
<xarray.Dataset> Size: 188TB
Dimensions:    (time: 175320, latitude: 4096, longitude: 8193)
Coordinates:
  * latitude   (latitude) float64 33kB -90.0 -89.96 -89.91 ... 89.91 89.96 90.0
  * longitude  (longitude) float64 66kB -180.0 -180.0 -179.9 ... 180.0 180.0
    step       timedelta64[ns] 8B ...
    surface    float64 8B ...
  * time       (time) datetime64[ns] 1MB 2020-01-01 ... 2039-12-31T23:00:00
Data variables:
    d2m        (time, latitude, longitude) float32 24TB dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
    sd         (time, latitude, longitude) float32 24TB dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
    ssr        (time, latitude, longitude) float32 24TB dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
    str        (time, latitude, longitude) float32 24TB dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
    t2m        (time, latitude, longitude) float32 24TB dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
    tprate     (time, latitude, longitude) float32 24TB dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
    u10        (time, latitude, longitude) float32 24TB dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
    v10        (time, latitude, longitude) float32 24TB dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
Attributes:
    Conventions:             CF-1.7
    GRIB_centre:             ecmf
    GRIB_centreDescription:  European Centre for Medium-Range Weather Forecasts
    GRIB_edition:            2
    GRIB_subCentre:          1003
    history:                 2024-06-06T16:50 GRIB to CDM+CF via cfgrib-0.9.1...
    institution:             European Centre for Medium-Range Weather Forecasts
xarray.Dataset
    • time: 175320
    • latitude: 4096
    • longitude: 8193
    • latitude
      (latitude)
      float64
      -90.0 -89.96 -89.91 ... 89.96 90.0
      long_name :
      latitude
      standard_name :
      latitude
      units :
      degrees_north
      array([-90.      , -89.956044, -89.912088, ...,  89.912088,  89.956044,
              90.      ])
    • longitude
      (longitude)
      float64
      -180.0 -180.0 ... 180.0 180.0
      long_name :
      longitude
      standard_name :
      longitude
      units :
      degrees_east
      array([-180.      , -179.956055, -179.912109, ...,  179.912109,  179.956055,
              180.      ])
    • step
      ()
      timedelta64[ns]
      ...
      long_name :
      time since forecast_reference_time
      standard_name :
      forecast_period
      [1 values with dtype=timedelta64[ns]]
    • surface
      ()
      float64
      ...
      long_name :
      original GRIB coordinate for key: level(surface)
      units :
      1
      [1 values with dtype=float64]
    • time
      (time)
      datetime64[ns]
      2020-01-01 ... 2039-12-31T23:00:00
      array(['2020-01-01T00:00:00.000000000', '2020-01-01T01:00:00.000000000',
             '2020-01-01T02:00:00.000000000', ..., '2039-12-31T21:00:00.000000000',
             '2039-12-31T22:00:00.000000000', '2039-12-31T23:00:00.000000000'],
            dtype='datetime64[ns]')
    • d2m
      (time, latitude, longitude)
      float32
      dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
      GRIB_NV :
      0
      GRIB_cfName :
      unknown
      GRIB_cfVarName :
      d2m
      GRIB_dataType :
      fc
      GRIB_gridDefinitionDescription :
      150
      GRIB_gridType :
      healpix
      GRIB_missingValue :
      3.4028234663852886e+38
      GRIB_name :
      2 metre dewpoint temperature
      GRIB_numberOfPoints :
      12582912
      GRIB_paramId :
      168
      GRIB_shortName :
      2d
      GRIB_stepType :
      instant
      GRIB_stepUnits :
      1
      GRIB_typeOfLevel :
      heightAboveGround
      GRIB_units :
      K
      last_restart_dim_updated :
      175320
      long_name :
      2 metre dewpoint temperature
      standard_name :
      unknown
      units :
      K
      Array Chunk
      Bytes 21.40 TiB 48.00 MiB
      Shape (175320, 4096, 8193) (48, 512, 512)
      Dask graph 496808 chunks in 2 graph layers
      Data type float32 numpy.ndarray
      8193 4096 175320
    • sd
      (time, latitude, longitude)
      float32
      dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
      GRIB_NV :
      0
      GRIB_cfName :
      lwe_thickness_of_surface_snow_amount
      GRIB_cfVarName :
      sd
      GRIB_dataType :
      fc
      GRIB_gridDefinitionDescription :
      150
      GRIB_gridType :
      healpix
      GRIB_missingValue :
      3.4028234663852886e+38
      GRIB_name :
      Snow depth
      GRIB_numberOfPoints :
      12582912
      GRIB_paramId :
      141
      GRIB_shortName :
      sd
      GRIB_stepType :
      instant
      GRIB_stepUnits :
      1
      GRIB_typeOfLevel :
      surface
      GRIB_units :
      m of water equivalent
      last_restart_dim_updated :
      175320
      long_name :
      Snow depth
      standard_name :
      lwe_thickness_of_surface_snow_amount
      units :
      m of water equivalent
      Array Chunk
      Bytes 21.40 TiB 48.00 MiB
      Shape (175320, 4096, 8193) (48, 512, 512)
      Dask graph 496808 chunks in 2 graph layers
      Data type float32 numpy.ndarray
      8193 4096 175320
    • ssr
      (time, latitude, longitude)
      float32
      dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
      GRIB_NV :
      0
      GRIB_cfName :
      surface_net_downward_shortwave_flux
      GRIB_cfVarName :
      ssr
      GRIB_dataType :
      fc
      GRIB_gridDefinitionDescription :
      150
      GRIB_gridType :
      healpix
      GRIB_missingValue :
      3.4028234663852886e+38
      GRIB_name :
      Surface net short-wave (solar) radiation
      GRIB_numberOfPoints :
      12582912
      GRIB_paramId :
      176
      GRIB_shortName :
      ssr
      GRIB_stepType :
      accum
      GRIB_stepUnits :
      1
      GRIB_typeOfLevel :
      surface
      GRIB_units :
      J m**-2
      last_restart_dim_updated :
      175320
      long_name :
      Surface net short-wave (solar) radiation
      standard_name :
      surface_net_downward_shortwave_flux
      units :
      J m**-2
      Array Chunk
      Bytes 21.40 TiB 48.00 MiB
      Shape (175320, 4096, 8193) (48, 512, 512)
      Dask graph 496808 chunks in 2 graph layers
      Data type float32 numpy.ndarray
      8193 4096 175320
    • str
      (time, latitude, longitude)
      float32
      dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
      GRIB_NV :
      0
      GRIB_cfName :
      surface_net_upward_longwave_flux
      GRIB_cfVarName :
      str
      GRIB_dataType :
      fc
      GRIB_gridDefinitionDescription :
      150
      GRIB_gridType :
      healpix
      GRIB_missingValue :
      3.4028234663852886e+38
      GRIB_name :
      Surface net long-wave (thermal) radiation
      GRIB_numberOfPoints :
      12582912
      GRIB_paramId :
      177
      GRIB_shortName :
      str
      GRIB_stepType :
      accum
      GRIB_stepUnits :
      1
      GRIB_typeOfLevel :
      surface
      GRIB_units :
      J m**-2
      last_restart_dim_updated :
      175320
      long_name :
      Surface net long-wave (thermal) radiation
      standard_name :
      surface_net_upward_longwave_flux
      units :
      J m**-2
      Array Chunk
      Bytes 21.40 TiB 48.00 MiB
      Shape (175320, 4096, 8193) (48, 512, 512)
      Dask graph 496808 chunks in 2 graph layers
      Data type float32 numpy.ndarray
      8193 4096 175320
    • t2m
      (time, latitude, longitude)
      float32
      dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
      GRIB_NV :
      0
      GRIB_cfName :
      air_temperature
      GRIB_cfVarName :
      t2m
      GRIB_dataType :
      fc
      GRIB_gridDefinitionDescription :
      150
      GRIB_gridType :
      healpix
      GRIB_missingValue :
      3.4028234663852886e+38
      GRIB_name :
      2 metre temperature
      GRIB_numberOfPoints :
      12582912
      GRIB_paramId :
      167
      GRIB_shortName :
      2t
      GRIB_stepType :
      instant
      GRIB_stepUnits :
      1
      GRIB_typeOfLevel :
      heightAboveGround
      GRIB_units :
      K
      last_restart_dim_updated :
      175320
      long_name :
      2 metre temperature
      standard_name :
      air_temperature
      units :
      K
      Array Chunk
      Bytes 21.40 TiB 48.00 MiB
      Shape (175320, 4096, 8193) (48, 512, 512)
      Dask graph 496808 chunks in 2 graph layers
      Data type float32 numpy.ndarray
      8193 4096 175320
    • tprate
      (time, latitude, longitude)
      float32
      dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
      GRIB_NV :
      0
      GRIB_cfName :
      unknown
      GRIB_cfVarName :
      tprate
      GRIB_dataType :
      fc
      GRIB_gridDefinitionDescription :
      150
      GRIB_gridType :
      healpix
      GRIB_missingValue :
      3.4028234663852886e+38
      GRIB_name :
      Total precipitation rate
      GRIB_numberOfPoints :
      12582912
      GRIB_paramId :
      260048
      GRIB_shortName :
      tprate
      GRIB_stepType :
      instant
      GRIB_stepUnits :
      1
      GRIB_typeOfLevel :
      surface
      GRIB_units :
      kg m**-2 s**-1
      last_restart_dim_updated :
      175320
      long_name :
      Total precipitation rate
      standard_name :
      unknown
      units :
      kg m**-2 s**-1
      Array Chunk
      Bytes 21.40 TiB 48.00 MiB
      Shape (175320, 4096, 8193) (48, 512, 512)
      Dask graph 496808 chunks in 2 graph layers
      Data type float32 numpy.ndarray
      8193 4096 175320
    • u10
      (time, latitude, longitude)
      float32
      dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
      GRIB_NV :
      0
      GRIB_cfName :
      eastward_wind
      GRIB_cfVarName :
      u10
      GRIB_dataType :
      fc
      GRIB_gridDefinitionDescription :
      150
      GRIB_gridType :
      healpix
      GRIB_missingValue :
      3.4028234663852886e+38
      GRIB_name :
      10 metre U wind component
      GRIB_numberOfPoints :
      12582912
      GRIB_paramId :
      165
      GRIB_shortName :
      10u
      GRIB_stepType :
      instant
      GRIB_stepUnits :
      1
      GRIB_typeOfLevel :
      heightAboveGround
      GRIB_units :
      m s**-1
      last_restart_dim_updated :
      175320
      long_name :
      10 metre U wind component
      standard_name :
      eastward_wind
      units :
      m s**-1
      Array Chunk
      Bytes 21.40 TiB 48.00 MiB
      Shape (175320, 4096, 8193) (48, 512, 512)
      Dask graph 496808 chunks in 2 graph layers
      Data type float32 numpy.ndarray
      8193 4096 175320
    • v10
      (time, latitude, longitude)
      float32
      dask.array<chunksize=(48, 512, 512), meta=np.ndarray>
      GRIB_NV :
      0
      GRIB_cfName :
      northward_wind
      GRIB_cfVarName :
      v10
      GRIB_dataType :
      fc
      GRIB_gridDefinitionDescription :
      150
      GRIB_gridType :
      healpix
      GRIB_missingValue :
      3.4028234663852886e+38
      GRIB_name :
      10 metre V wind component
      GRIB_numberOfPoints :
      12582912
      GRIB_paramId :
      166
      GRIB_shortName :
      10v
      GRIB_stepType :
      instant
      GRIB_stepUnits :
      1
      GRIB_typeOfLevel :
      heightAboveGround
      GRIB_units :
      m s**-1
      last_restart_dim_updated :
      175320
      long_name :
      10 metre V wind component
      standard_name :
      northward_wind
      units :
      m s**-1
      Array Chunk
      Bytes 21.40 TiB 48.00 MiB
      Shape (175320, 4096, 8193) (48, 512, 512)
      Dask graph 496808 chunks in 2 graph layers
      Data type float32 numpy.ndarray
      8193 4096 175320
    • latitude
      PandasIndex
      PandasIndex(Index([             -90.0, -89.95604395604396, -89.91208791208791,
             -89.86813186813187, -89.82417582417582, -89.78021978021978,
             -89.73626373626374,  -89.6923076923077, -89.64835164835165,
              -89.6043956043956,
             ...
              89.60439560439562,  89.64835164835165,  89.69230769230771,
              89.73626373626374,   89.7802197802198,  89.82417582417582,
              89.86813186813188,  89.91208791208791,  89.95604395604397,
                           90.0],
            dtype='float64', name='latitude', length=4096))
    • longitude
      PandasIndex
      PandasIndex(Index([             -180.0,     -179.9560546875, -179.91210937500003,
                 -179.8681640625,       -179.82421875,     -179.7802734375,
                  -179.736328125,     -179.6923828125,        -179.6484375,
                 -179.6044921875,
             ...
                  179.6044921875,         179.6484375,      179.6923828125,
                   179.736328125,      179.7802734375,        179.82421875,
                  179.8681640625,  179.91210937500003,      179.9560546875,
                           180.0],
            dtype='float64', name='longitude', length=8193))
    • time
      PandasIndex
      PandasIndex(DatetimeIndex(['2020-01-01 00:00:00', '2020-01-01 01:00:00',
                     '2020-01-01 02:00:00', '2020-01-01 03:00:00',
                     '2020-01-01 04:00:00', '2020-01-01 05:00:00',
                     '2020-01-01 06:00:00', '2020-01-01 07:00:00',
                     '2020-01-01 08:00:00', '2020-01-01 09:00:00',
                     ...
                     '2039-12-31 14:00:00', '2039-12-31 15:00:00',
                     '2039-12-31 16:00:00', '2039-12-31 17:00:00',
                     '2039-12-31 18:00:00', '2039-12-31 19:00:00',
                     '2039-12-31 20:00:00', '2039-12-31 21:00:00',
                     '2039-12-31 22:00:00', '2039-12-31 23:00:00'],
                    dtype='datetime64[ns]', name='time', length=175320, freq=None))
  • Conventions :
    CF-1.7
    GRIB_centre :
    ecmf
    GRIB_centreDescription :
    European Centre for Medium-Range Weather Forecasts
    GRIB_edition :
    2
    GRIB_subCentre :
    1003
    history :
    2024-06-06T16:50 GRIB to CDM+CF via cfgrib-0.9.12.0/ecCodes-2.35.0 with {"source": ".xarray-ecmwf-cache/10baaccbb0bb6b0157fbe574dc7d566e.grib", "filter_by_keys": {}, "encode_cf": ["parameter", "time", "geography", "vertical"]}
    institution :
    European Centre for Medium-Range Weather Forecasts

⚠ At this point, no data has been downloaded yet, nor loaded in memory.

2. Data selection¶

First, we perform a geographical selection corresponding to the Germany area. This greatly reduces the amount of data that will be downloaded from the DCMS. Also, we convert the temperature to °C. For the moment, this a lazy operation.

In [4]:
xr.set_options(keep_attrs=True)

t2m = ds.t2m.astype("float32") - 273.15
t2m.attrs["units"] = "°C"
t2m_germany = t2m.sel(**{"latitude": slice(47, 55), "longitude": slice(5, 16)})
t2m_germany
Out[4]:
<xarray.DataArray 't2m' (time: 175320, latitude: 182, longitude: 251)> Size: 32GB
dask.array<getitem, shape=(175320, 182, 251), dtype=float32, chunksize=(48, 182, 251), chunktype=numpy.ndarray>
Coordinates:
  * latitude   (latitude) float64 1kB 47.01 47.05 47.1 ... 54.88 54.92 54.97
  * longitude  (longitude) float64 2kB 5.01 5.054 5.098 ... 15.91 15.95 16.0
    step       timedelta64[ns] 8B ...
    surface    float64 8B ...
  * time       (time) datetime64[ns] 1MB 2020-01-01 ... 2039-12-31T23:00:00
Attributes: (12/19)
    GRIB_NV:                         0
    GRIB_cfName:                     air_temperature
    GRIB_cfVarName:                  t2m
    GRIB_dataType:                   fc
    GRIB_gridDefinitionDescription:  150
    GRIB_gridType:                   healpix
    ...                              ...
    GRIB_typeOfLevel:                heightAboveGround
    GRIB_units:                      K
    last_restart_dim_updated:        175320
    long_name:                       2 metre temperature
    standard_name:                   air_temperature
    units:                           °C
xarray.DataArray
't2m'
  • time: 175320
  • latitude: 182
  • longitude: 251
  • dask.array<chunksize=(48, 182, 251), meta=np.ndarray>
    Array Chunk
    Bytes 29.84 GiB 8.36 MiB
    Shape (175320, 182, 251) (48, 182, 251)
    Dask graph 3653 chunks in 4 graph layers
    Data type float32 numpy.ndarray
    251 182 175320
    • latitude
      (latitude)
      float64
      47.01 47.05 47.1 ... 54.92 54.97
      long_name :
      latitude
      standard_name :
      latitude
      units :
      degrees_north
      array([47.010989, 47.054945, 47.098901, 47.142857, 47.186813, 47.230769,
             47.274725, 47.318681, 47.362637, 47.406593, 47.450549, 47.494505,
             47.538462, 47.582418, 47.626374, 47.67033 , 47.714286, 47.758242,
             47.802198, 47.846154, 47.89011 , 47.934066, 47.978022, 48.021978,
             48.065934, 48.10989 , 48.153846, 48.197802, 48.241758, 48.285714,
             48.32967 , 48.373626, 48.417582, 48.461538, 48.505495, 48.549451,
             48.593407, 48.637363, 48.681319, 48.725275, 48.769231, 48.813187,
             48.857143, 48.901099, 48.945055, 48.989011, 49.032967, 49.076923,
             49.120879, 49.164835, 49.208791, 49.252747, 49.296703, 49.340659,
             49.384615, 49.428571, 49.472527, 49.516484, 49.56044 , 49.604396,
             49.648352, 49.692308, 49.736264, 49.78022 , 49.824176, 49.868132,
             49.912088, 49.956044, 50.      , 50.043956, 50.087912, 50.131868,
             50.175824, 50.21978 , 50.263736, 50.307692, 50.351648, 50.395604,
             50.43956 , 50.483516, 50.527473, 50.571429, 50.615385, 50.659341,
             50.703297, 50.747253, 50.791209, 50.835165, 50.879121, 50.923077,
             50.967033, 51.010989, 51.054945, 51.098901, 51.142857, 51.186813,
             51.230769, 51.274725, 51.318681, 51.362637, 51.406593, 51.450549,
             51.494505, 51.538462, 51.582418, 51.626374, 51.67033 , 51.714286,
             51.758242, 51.802198, 51.846154, 51.89011 , 51.934066, 51.978022,
             52.021978, 52.065934, 52.10989 , 52.153846, 52.197802, 52.241758,
             52.285714, 52.32967 , 52.373626, 52.417582, 52.461538, 52.505495,
             52.549451, 52.593407, 52.637363, 52.681319, 52.725275, 52.769231,
             52.813187, 52.857143, 52.901099, 52.945055, 52.989011, 53.032967,
             53.076923, 53.120879, 53.164835, 53.208791, 53.252747, 53.296703,
             53.340659, 53.384615, 53.428571, 53.472527, 53.516484, 53.56044 ,
             53.604396, 53.648352, 53.692308, 53.736264, 53.78022 , 53.824176,
             53.868132, 53.912088, 53.956044, 54.      , 54.043956, 54.087912,
             54.131868, 54.175824, 54.21978 , 54.263736, 54.307692, 54.351648,
             54.395604, 54.43956 , 54.483516, 54.527473, 54.571429, 54.615385,
             54.659341, 54.703297, 54.747253, 54.791209, 54.835165, 54.879121,
             54.923077, 54.967033])
    • longitude
      (longitude)
      float64
      5.01 5.054 5.098 ... 15.95 16.0
      long_name :
      longitude
      standard_name :
      longitude
      units :
      degrees_east
      array([ 5.009766,  5.053711,  5.097656, ..., 15.908203, 15.952148, 15.996094])
    • step
      ()
      timedelta64[ns]
      ...
      long_name :
      time since forecast_reference_time
      standard_name :
      forecast_period
      [1 values with dtype=timedelta64[ns]]
    • surface
      ()
      float64
      ...
      long_name :
      original GRIB coordinate for key: level(surface)
      units :
      1
      [1 values with dtype=float64]
    • time
      (time)
      datetime64[ns]
      2020-01-01 ... 2039-12-31T23:00:00
      array(['2020-01-01T00:00:00.000000000', '2020-01-01T01:00:00.000000000',
             '2020-01-01T02:00:00.000000000', ..., '2039-12-31T21:00:00.000000000',
             '2039-12-31T22:00:00.000000000', '2039-12-31T23:00:00.000000000'],
            dtype='datetime64[ns]')
    • latitude
      PandasIndex
      PandasIndex(Index([ 47.01098901098902,  47.05494505494508,  47.09890109890111,
              47.14285714285717,   47.1868131868132, 47.230769230769255,
              47.27472527472529,  47.31868131868134,  47.36263736263737,
              47.40659340659343,
             ...
              54.57142857142859,  54.61538461538461,  54.65934065934067,
               54.7032967032967,  54.74725274725276,  54.79120879120879,
             54.835164835164846, 54.879120879120904, 54.923076923076934,
              54.96703296703299],
            dtype='float64', name='latitude', length=182))
    • longitude
      PandasIndex
      PandasIndex(Index([ 5.009765625000001,       5.0537109375,         5.09765625,
                   5.1416015625,        5.185546875,       5.2294921875,
                      5.2734375,       5.3173828125,        5.361328125,
                   5.4052734375,
             ...
                  15.6005859375, 15.644531250000002,      15.6884765625,
                   15.732421875, 15.776367187499998,         15.8203125,
             15.864257812500002,       15.908203125, 15.952148437500002,
                    15.99609375],
            dtype='float64', name='longitude', length=251))
    • time
      PandasIndex
      PandasIndex(DatetimeIndex(['2020-01-01 00:00:00', '2020-01-01 01:00:00',
                     '2020-01-01 02:00:00', '2020-01-01 03:00:00',
                     '2020-01-01 04:00:00', '2020-01-01 05:00:00',
                     '2020-01-01 06:00:00', '2020-01-01 07:00:00',
                     '2020-01-01 08:00:00', '2020-01-01 09:00:00',
                     ...
                     '2039-12-31 14:00:00', '2039-12-31 15:00:00',
                     '2039-12-31 16:00:00', '2039-12-31 17:00:00',
                     '2039-12-31 18:00:00', '2039-12-31 19:00:00',
                     '2039-12-31 20:00:00', '2039-12-31 21:00:00',
                     '2039-12-31 22:00:00', '2039-12-31 23:00:00'],
                    dtype='datetime64[ns]', name='time', length=175320, freq=None))
  • GRIB_NV :
    0
    GRIB_cfName :
    air_temperature
    GRIB_cfVarName :
    t2m
    GRIB_dataType :
    fc
    GRIB_gridDefinitionDescription :
    150
    GRIB_gridType :
    healpix
    GRIB_missingValue :
    3.4028234663852886e+38
    GRIB_name :
    2 metre temperature
    GRIB_numberOfPoints :
    12582912
    GRIB_paramId :
    167
    GRIB_shortName :
    2t
    GRIB_stepType :
    instant
    GRIB_stepUnits :
    1
    GRIB_typeOfLevel :
    heightAboveGround
    GRIB_units :
    K
    last_restart_dim_updated :
    175320
    long_name :
    2 metre temperature
    standard_name :
    air_temperature
    units :
    °C

Second, we further select January 2025. This is again a lazy operation:

In [5]:
t2m_germany_january_2025 = t2m_germany.sel(time="2025-01")
t2m_germany_january_2025
Out[5]:
<xarray.DataArray 't2m' (time: 744, latitude: 182, longitude: 251)> Size: 136MB
dask.array<getitem, shape=(744, 182, 251), dtype=float32, chunksize=(48, 182, 251), chunktype=numpy.ndarray>
Coordinates:
  * latitude   (latitude) float64 1kB 47.01 47.05 47.1 ... 54.88 54.92 54.97
  * longitude  (longitude) float64 2kB 5.01 5.054 5.098 ... 15.91 15.95 16.0
    step       timedelta64[ns] 8B ...
    surface    float64 8B ...
  * time       (time) datetime64[ns] 6kB 2025-01-01 ... 2025-01-31T23:00:00
Attributes: (12/19)
    GRIB_NV:                         0
    GRIB_cfName:                     air_temperature
    GRIB_cfVarName:                  t2m
    GRIB_dataType:                   fc
    GRIB_gridDefinitionDescription:  150
    GRIB_gridType:                   healpix
    ...                              ...
    GRIB_typeOfLevel:                heightAboveGround
    GRIB_units:                      K
    last_restart_dim_updated:        175320
    long_name:                       2 metre temperature
    standard_name:                   air_temperature
    units:                           °C
xarray.DataArray
't2m'
  • time: 744
  • latitude: 182
  • longitude: 251
  • dask.array<chunksize=(24, 182, 251), meta=np.ndarray>
    Array Chunk
    Bytes 129.65 MiB 8.36 MiB
    Shape (744, 182, 251) (48, 182, 251)
    Dask graph 16 chunks in 5 graph layers
    Data type float32 numpy.ndarray
    251 182 744
    • latitude
      (latitude)
      float64
      47.01 47.05 47.1 ... 54.92 54.97
      long_name :
      latitude
      standard_name :
      latitude
      units :
      degrees_north
      array([47.010989, 47.054945, 47.098901, 47.142857, 47.186813, 47.230769,
             47.274725, 47.318681, 47.362637, 47.406593, 47.450549, 47.494505,
             47.538462, 47.582418, 47.626374, 47.67033 , 47.714286, 47.758242,
             47.802198, 47.846154, 47.89011 , 47.934066, 47.978022, 48.021978,
             48.065934, 48.10989 , 48.153846, 48.197802, 48.241758, 48.285714,
             48.32967 , 48.373626, 48.417582, 48.461538, 48.505495, 48.549451,
             48.593407, 48.637363, 48.681319, 48.725275, 48.769231, 48.813187,
             48.857143, 48.901099, 48.945055, 48.989011, 49.032967, 49.076923,
             49.120879, 49.164835, 49.208791, 49.252747, 49.296703, 49.340659,
             49.384615, 49.428571, 49.472527, 49.516484, 49.56044 , 49.604396,
             49.648352, 49.692308, 49.736264, 49.78022 , 49.824176, 49.868132,
             49.912088, 49.956044, 50.      , 50.043956, 50.087912, 50.131868,
             50.175824, 50.21978 , 50.263736, 50.307692, 50.351648, 50.395604,
             50.43956 , 50.483516, 50.527473, 50.571429, 50.615385, 50.659341,
             50.703297, 50.747253, 50.791209, 50.835165, 50.879121, 50.923077,
             50.967033, 51.010989, 51.054945, 51.098901, 51.142857, 51.186813,
             51.230769, 51.274725, 51.318681, 51.362637, 51.406593, 51.450549,
             51.494505, 51.538462, 51.582418, 51.626374, 51.67033 , 51.714286,
             51.758242, 51.802198, 51.846154, 51.89011 , 51.934066, 51.978022,
             52.021978, 52.065934, 52.10989 , 52.153846, 52.197802, 52.241758,
             52.285714, 52.32967 , 52.373626, 52.417582, 52.461538, 52.505495,
             52.549451, 52.593407, 52.637363, 52.681319, 52.725275, 52.769231,
             52.813187, 52.857143, 52.901099, 52.945055, 52.989011, 53.032967,
             53.076923, 53.120879, 53.164835, 53.208791, 53.252747, 53.296703,
             53.340659, 53.384615, 53.428571, 53.472527, 53.516484, 53.56044 ,
             53.604396, 53.648352, 53.692308, 53.736264, 53.78022 , 53.824176,
             53.868132, 53.912088, 53.956044, 54.      , 54.043956, 54.087912,
             54.131868, 54.175824, 54.21978 , 54.263736, 54.307692, 54.351648,
             54.395604, 54.43956 , 54.483516, 54.527473, 54.571429, 54.615385,
             54.659341, 54.703297, 54.747253, 54.791209, 54.835165, 54.879121,
             54.923077, 54.967033])
    • longitude
      (longitude)
      float64
      5.01 5.054 5.098 ... 15.95 16.0
      long_name :
      longitude
      standard_name :
      longitude
      units :
      degrees_east
      array([ 5.009766,  5.053711,  5.097656, ..., 15.908203, 15.952148, 15.996094])
    • step
      ()
      timedelta64[ns]
      ...
      long_name :
      time since forecast_reference_time
      standard_name :
      forecast_period
      [1 values with dtype=timedelta64[ns]]
    • surface
      ()
      float64
      ...
      long_name :
      original GRIB coordinate for key: level(surface)
      units :
      1
      [1 values with dtype=float64]
    • time
      (time)
      datetime64[ns]
      2025-01-01 ... 2025-01-31T23:00:00
      array(['2025-01-01T00:00:00.000000000', '2025-01-01T01:00:00.000000000',
             '2025-01-01T02:00:00.000000000', ..., '2025-01-31T21:00:00.000000000',
             '2025-01-31T22:00:00.000000000', '2025-01-31T23:00:00.000000000'],
            dtype='datetime64[ns]')
    • latitude
      PandasIndex
      PandasIndex(Index([ 47.01098901098902,  47.05494505494508,  47.09890109890111,
              47.14285714285717,   47.1868131868132, 47.230769230769255,
              47.27472527472529,  47.31868131868134,  47.36263736263737,
              47.40659340659343,
             ...
              54.57142857142859,  54.61538461538461,  54.65934065934067,
               54.7032967032967,  54.74725274725276,  54.79120879120879,
             54.835164835164846, 54.879120879120904, 54.923076923076934,
              54.96703296703299],
            dtype='float64', name='latitude', length=182))
    • longitude
      PandasIndex
      PandasIndex(Index([ 5.009765625000001,       5.0537109375,         5.09765625,
                   5.1416015625,        5.185546875,       5.2294921875,
                      5.2734375,       5.3173828125,        5.361328125,
                   5.4052734375,
             ...
                  15.6005859375, 15.644531250000002,      15.6884765625,
                   15.732421875, 15.776367187499998,         15.8203125,
             15.864257812500002,       15.908203125, 15.952148437500002,
                    15.99609375],
            dtype='float64', name='longitude', length=251))
    • time
      PandasIndex
      PandasIndex(DatetimeIndex(['2025-01-01 00:00:00', '2025-01-01 01:00:00',
                     '2025-01-01 02:00:00', '2025-01-01 03:00:00',
                     '2025-01-01 04:00:00', '2025-01-01 05:00:00',
                     '2025-01-01 06:00:00', '2025-01-01 07:00:00',
                     '2025-01-01 08:00:00', '2025-01-01 09:00:00',
                     ...
                     '2025-01-31 14:00:00', '2025-01-31 15:00:00',
                     '2025-01-31 16:00:00', '2025-01-31 17:00:00',
                     '2025-01-31 18:00:00', '2025-01-31 19:00:00',
                     '2025-01-31 20:00:00', '2025-01-31 21:00:00',
                     '2025-01-31 22:00:00', '2025-01-31 23:00:00'],
                    dtype='datetime64[ns]', name='time', length=744, freq=None))
  • GRIB_NV :
    0
    GRIB_cfName :
    air_temperature
    GRIB_cfVarName :
    t2m
    GRIB_dataType :
    fc
    GRIB_gridDefinitionDescription :
    150
    GRIB_gridType :
    healpix
    GRIB_missingValue :
    3.4028234663852886e+38
    GRIB_name :
    2 metre temperature
    GRIB_numberOfPoints :
    12582912
    GRIB_paramId :
    167
    GRIB_shortName :
    2t
    GRIB_stepType :
    instant
    GRIB_stepUnits :
    1
    GRIB_typeOfLevel :
    heightAboveGround
    GRIB_units :
    K
    last_restart_dim_updated :
    175320
    long_name :
    2 metre temperature
    standard_name :
    air_temperature
    units :
    °C

Due to the chunked structure of the DataArray, xarray must download every chunk that includes a portion of the selected data.

To estimate the size of the download, we can use the costing.py module. This must be done before we apply any reduction operation.

In [6]:
import costing

costing.estimate_download_size(t2m, t2m_germany_january_2025)
estimated_needed_chunks: 16
estimated_memory_size: 0.805 GB
estimated_download_size: 0.081 GB

3. Data reduction¶

Before dowloading the data, we can perform further lazy operations. Here we compute the 2 metre temperature montly average:

In [7]:
t2m_germany_january_2025_monthly_mean = t2m_germany_january_2025.mean(dim="time")
t2m_germany_january_2025_monthly_mean
Out[7]:
<xarray.DataArray 't2m' (latitude: 182, longitude: 251)> Size: 183kB
dask.array<mean_agg-aggregate, shape=(182, 251), dtype=float32, chunksize=(182, 251), chunktype=numpy.ndarray>
Coordinates:
  * latitude   (latitude) float64 1kB 47.01 47.05 47.1 ... 54.88 54.92 54.97
  * longitude  (longitude) float64 2kB 5.01 5.054 5.098 ... 15.91 15.95 16.0
    step       timedelta64[ns] 8B ...
    surface    float64 8B ...
Attributes: (12/19)
    GRIB_NV:                         0
    GRIB_cfName:                     air_temperature
    GRIB_cfVarName:                  t2m
    GRIB_dataType:                   fc
    GRIB_gridDefinitionDescription:  150
    GRIB_gridType:                   healpix
    ...                              ...
    GRIB_typeOfLevel:                heightAboveGround
    GRIB_units:                      K
    last_restart_dim_updated:        175320
    long_name:                       2 metre temperature
    standard_name:                   air_temperature
    units:                           °C
xarray.DataArray
't2m'
  • latitude: 182
  • longitude: 251
  • dask.array<chunksize=(182, 251), meta=np.ndarray>
    Array Chunk
    Bytes 178.45 kiB 178.45 kiB
    Shape (182, 251) (182, 251)
    Dask graph 1 chunks in 8 graph layers
    Data type float32 numpy.ndarray
    251 182
    • latitude
      (latitude)
      float64
      47.01 47.05 47.1 ... 54.92 54.97
      long_name :
      latitude
      standard_name :
      latitude
      units :
      degrees_north
      array([47.010989, 47.054945, 47.098901, 47.142857, 47.186813, 47.230769,
             47.274725, 47.318681, 47.362637, 47.406593, 47.450549, 47.494505,
             47.538462, 47.582418, 47.626374, 47.67033 , 47.714286, 47.758242,
             47.802198, 47.846154, 47.89011 , 47.934066, 47.978022, 48.021978,
             48.065934, 48.10989 , 48.153846, 48.197802, 48.241758, 48.285714,
             48.32967 , 48.373626, 48.417582, 48.461538, 48.505495, 48.549451,
             48.593407, 48.637363, 48.681319, 48.725275, 48.769231, 48.813187,
             48.857143, 48.901099, 48.945055, 48.989011, 49.032967, 49.076923,
             49.120879, 49.164835, 49.208791, 49.252747, 49.296703, 49.340659,
             49.384615, 49.428571, 49.472527, 49.516484, 49.56044 , 49.604396,
             49.648352, 49.692308, 49.736264, 49.78022 , 49.824176, 49.868132,
             49.912088, 49.956044, 50.      , 50.043956, 50.087912, 50.131868,
             50.175824, 50.21978 , 50.263736, 50.307692, 50.351648, 50.395604,
             50.43956 , 50.483516, 50.527473, 50.571429, 50.615385, 50.659341,
             50.703297, 50.747253, 50.791209, 50.835165, 50.879121, 50.923077,
             50.967033, 51.010989, 51.054945, 51.098901, 51.142857, 51.186813,
             51.230769, 51.274725, 51.318681, 51.362637, 51.406593, 51.450549,
             51.494505, 51.538462, 51.582418, 51.626374, 51.67033 , 51.714286,
             51.758242, 51.802198, 51.846154, 51.89011 , 51.934066, 51.978022,
             52.021978, 52.065934, 52.10989 , 52.153846, 52.197802, 52.241758,
             52.285714, 52.32967 , 52.373626, 52.417582, 52.461538, 52.505495,
             52.549451, 52.593407, 52.637363, 52.681319, 52.725275, 52.769231,
             52.813187, 52.857143, 52.901099, 52.945055, 52.989011, 53.032967,
             53.076923, 53.120879, 53.164835, 53.208791, 53.252747, 53.296703,
             53.340659, 53.384615, 53.428571, 53.472527, 53.516484, 53.56044 ,
             53.604396, 53.648352, 53.692308, 53.736264, 53.78022 , 53.824176,
             53.868132, 53.912088, 53.956044, 54.      , 54.043956, 54.087912,
             54.131868, 54.175824, 54.21978 , 54.263736, 54.307692, 54.351648,
             54.395604, 54.43956 , 54.483516, 54.527473, 54.571429, 54.615385,
             54.659341, 54.703297, 54.747253, 54.791209, 54.835165, 54.879121,
             54.923077, 54.967033])
    • longitude
      (longitude)
      float64
      5.01 5.054 5.098 ... 15.95 16.0
      long_name :
      longitude
      standard_name :
      longitude
      units :
      degrees_east
      array([ 5.009766,  5.053711,  5.097656, ..., 15.908203, 15.952148, 15.996094])
    • step
      ()
      timedelta64[ns]
      ...
      long_name :
      time since forecast_reference_time
      standard_name :
      forecast_period
      [1 values with dtype=timedelta64[ns]]
    • surface
      ()
      float64
      ...
      long_name :
      original GRIB coordinate for key: level(surface)
      units :
      1
      [1 values with dtype=float64]
    • latitude
      PandasIndex
      PandasIndex(Index([ 47.01098901098902,  47.05494505494508,  47.09890109890111,
              47.14285714285717,   47.1868131868132, 47.230769230769255,
              47.27472527472529,  47.31868131868134,  47.36263736263737,
              47.40659340659343,
             ...
              54.57142857142859,  54.61538461538461,  54.65934065934067,
               54.7032967032967,  54.74725274725276,  54.79120879120879,
             54.835164835164846, 54.879120879120904, 54.923076923076934,
              54.96703296703299],
            dtype='float64', name='latitude', length=182))
    • longitude
      PandasIndex
      PandasIndex(Index([ 5.009765625000001,       5.0537109375,         5.09765625,
                   5.1416015625,        5.185546875,       5.2294921875,
                      5.2734375,       5.3173828125,        5.361328125,
                   5.4052734375,
             ...
                  15.6005859375, 15.644531250000002,      15.6884765625,
                   15.732421875, 15.776367187499998,         15.8203125,
             15.864257812500002,       15.908203125, 15.952148437500002,
                    15.99609375],
            dtype='float64', name='longitude', length=251))
  • GRIB_NV :
    0
    GRIB_cfName :
    air_temperature
    GRIB_cfVarName :
    t2m
    GRIB_dataType :
    fc
    GRIB_gridDefinitionDescription :
    150
    GRIB_gridType :
    healpix
    GRIB_missingValue :
    3.4028234663852886e+38
    GRIB_name :
    2 metre temperature
    GRIB_numberOfPoints :
    12582912
    GRIB_paramId :
    167
    GRIB_shortName :
    2t
    GRIB_stepType :
    instant
    GRIB_stepUnits :
    1
    GRIB_typeOfLevel :
    heightAboveGround
    GRIB_units :
    K
    last_restart_dim_updated :
    175320
    long_name :
    2 metre temperature
    standard_name :
    air_temperature
    units :
    °C

3. Data download¶

This is the phase where we explicitly trigger the download of the data. To do so we will call compute() on the previously averaged temperature. The result will be small enought to easily fit into memory. Remember to assign the return of the compute() function to a new variable, so that the data is kept in memory.

We can measure the time it takes:

In [8]:
%%time

t2m_germany_january_2025_monthly_mean_computed = t2m_germany_january_2025_monthly_mean.compute()
CPU times: user 3.55 s, sys: 1.92 s, total: 5.48 s
Wall time: 3.63 s

The data was very small, this didn't take long!

5. Visualization¶

Finally, we can plot the monthly mean of January 2025 on a map:

In [9]:
import display
import matplotlib.pyplot as plt
from cartopy import crs
In [10]:
display.map(
    t2m_germany_january_2025_monthly_mean_computed, 
    projection=crs.Miller(), 
    vmax=None, 
    cmap="YlOrRd", 
    title="Mean Surface Temperature, Jan 2025"
);
No description has been provided for this image

2 metre temperature climatology in Berlin, 2020-2035¶

The power of EDH is better showned when working with timeseries. We will now show how fast it is to compute the 2 metre temperature climatology (montly mean and standard deviation) in Berlin, over the reference period 2020-2035.

With legacy data distributon systems you would need to download the entire world temperature for the reference time period, extract the Berlin data and do your calculations. Alternatively the data provider might do a crop and merge operation for you, but chances are that this is very slow (internally the data provider still accesses the entire world temperature, you just don't see it!). Thanks to Earth Data Hub this is not needed anymore. You only need to download the relevant chunks.

Here, we select the closest data to Berlin:

In [11]:
t2m_Berlin_2020_2035 = t2m.sel(**{"latitude": 52.5, "longitude": 13.4}, method="nearest").sel(time=slice("2020", "2035"))
t2m_Berlin_2020_2035
Out[11]:
<xarray.DataArray 't2m' (time: 140256)> Size: 561kB
dask.array<getitem, shape=(140256,), dtype=float32, chunksize=(48,), chunktype=numpy.ndarray>
Coordinates:
    latitude   float64 8B 52.51
    longitude  float64 8B 13.4
    step       timedelta64[ns] 8B ...
    surface    float64 8B ...
  * time       (time) datetime64[ns] 1MB 2020-01-01 ... 2035-12-31T23:00:00
Attributes: (12/19)
    GRIB_NV:                         0
    GRIB_cfName:                     air_temperature
    GRIB_cfVarName:                  t2m
    GRIB_dataType:                   fc
    GRIB_gridDefinitionDescription:  150
    GRIB_gridType:                   healpix
    ...                              ...
    GRIB_typeOfLevel:                heightAboveGround
    GRIB_units:                      K
    last_restart_dim_updated:        175320
    long_name:                       2 metre temperature
    standard_name:                   air_temperature
    units:                           °C
xarray.DataArray
't2m'
  • time: 140256
  • dask.array<chunksize=(48,), meta=np.ndarray>
    Array Chunk
    Bytes 547.88 kiB 192 B
    Shape (140256,) (48,)
    Dask graph 2922 chunks in 5 graph layers
    Data type float32 numpy.ndarray
    140256 1
    • latitude
      ()
      float64
      52.51
      long_name :
      latitude
      standard_name :
      latitude
      units :
      degrees_north
      array(52.50549451)
    • longitude
      ()
      float64
      13.4
      long_name :
      longitude
      standard_name :
      longitude
      units :
      degrees_east
      array(13.40332031)
    • step
      ()
      timedelta64[ns]
      ...
      long_name :
      time since forecast_reference_time
      standard_name :
      forecast_period
      [1 values with dtype=timedelta64[ns]]
    • surface
      ()
      float64
      ...
      long_name :
      original GRIB coordinate for key: level(surface)
      units :
      1
      [1 values with dtype=float64]
    • time
      (time)
      datetime64[ns]
      2020-01-01 ... 2035-12-31T23:00:00
      array(['2020-01-01T00:00:00.000000000', '2020-01-01T01:00:00.000000000',
             '2020-01-01T02:00:00.000000000', ..., '2035-12-31T21:00:00.000000000',
             '2035-12-31T22:00:00.000000000', '2035-12-31T23:00:00.000000000'],
            dtype='datetime64[ns]')
    • time
      PandasIndex
      PandasIndex(DatetimeIndex(['2020-01-01 00:00:00', '2020-01-01 01:00:00',
                     '2020-01-01 02:00:00', '2020-01-01 03:00:00',
                     '2020-01-01 04:00:00', '2020-01-01 05:00:00',
                     '2020-01-01 06:00:00', '2020-01-01 07:00:00',
                     '2020-01-01 08:00:00', '2020-01-01 09:00:00',
                     ...
                     '2035-12-31 14:00:00', '2035-12-31 15:00:00',
                     '2035-12-31 16:00:00', '2035-12-31 17:00:00',
                     '2035-12-31 18:00:00', '2035-12-31 19:00:00',
                     '2035-12-31 20:00:00', '2035-12-31 21:00:00',
                     '2035-12-31 22:00:00', '2035-12-31 23:00:00'],
                    dtype='datetime64[ns]', name='time', length=140256, freq=None))
  • GRIB_NV :
    0
    GRIB_cfName :
    air_temperature
    GRIB_cfVarName :
    t2m
    GRIB_dataType :
    fc
    GRIB_gridDefinitionDescription :
    150
    GRIB_gridType :
    healpix
    GRIB_missingValue :
    3.4028234663852886e+38
    GRIB_name :
    2 metre temperature
    GRIB_numberOfPoints :
    12582912
    GRIB_paramId :
    167
    GRIB_shortName :
    2t
    GRIB_stepType :
    instant
    GRIB_stepUnits :
    1
    GRIB_typeOfLevel :
    heightAboveGround
    GRIB_units :
    K
    last_restart_dim_updated :
    175320
    long_name :
    2 metre temperature
    standard_name :
    air_temperature
    units :
    °C

This is already small enought to be computed:

We estimate the cost of the download with de costing.py module.

In [12]:
costing.estimate_download_size(t2m, t2m_Berlin_2020_2035)
estimated_needed_chunks: 2922
estimated_memory_size: 147.069 GB
estimated_download_size: 14.707 GB

We explicitly trigger the download of the data. Remember to assign the return of the compute() function to a new variable, so that the data is kept in memory.

In [13]:
%%time

t2m_Berlin_2020_2035_computed = t2m_Berlin_2020_2035.compute()
CPU times: user 6min 39s, sys: 4min 12s, total: 10min 52s
Wall time: 4min 14s

Now that the data is loaded in memory we can easily compute the climatology:

In [14]:
t2m_Berlin_climatology_mean = t2m_Berlin_2020_2035_computed.groupby("time.month").mean(dim="time")
t2m_Berlin_climatology_std = t2m_Berlin_2020_2035_computed.groupby("time.month").std(dim="time")

We can finally plot the climatology in Berlin for the 2020-2035 refrence period

In [15]:
plt.figure(figsize=(10, 5))
t2m_Berlin_climatology_mean.plot(label="Mean", color="#3498db")
plt.errorbar(
    t2m_Berlin_climatology_mean.month, 
    t2m_Berlin_climatology_mean, 
    yerr=t2m_Berlin_climatology_std, 
    fmt="o", 
    label="Standard Deviation",
    color="#a9a9a9"
)

plt.title("Surface Temperature climatology in Berlin (DE), 2020-2035")
plt.xticks(t2m_Berlin_climatology_mean.month)
plt.xlabel("Month")
plt.ylabel("Surface Temperature [°C]")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
No description has been provided for this image