Download this notebook from github.

Basic Usage

Climate indicator computations

xclim is a library of climate indicators that operate on xarray DataArray objects. Indicators perform health checks on input data, converts units as needed, assign nans when input data is missing, and format outputs according to the Climate and Forecast (CF) convention. As the list of indicators has grown quite large, indicators are accessed through their realm (xclim.atmos, xclim.land and xclim.seaIce) to help browsing indicators by the domain they apply to.

Indicators should not be confused with indices, which define the algorithmic layer of each indicator. Those indices perform no checks beyond units compliance, and should be considered as low-level functions. See the respective documentation on indicators and indices for more information.

To use xclim in a project, import both xclim and xarray.

[1]:
from __future__ import annotations

import xarray as xr

import xclim.indices
from xclim import testing

Index calculations are performed by opening a NetCDF-like file, accessing the variable of interest, and calling the index function, which returns a new xarray.DataArray.

For this example, we’ll first open a demonstration dataset storing surface air temperature and compute the number of growing degree days (the sum of degrees above a certain threshold) at the yearly frequency. The time frequency parameter, here YS, is specified using pandas offset aliases. Note that some offsets might not be supported for non-standard calendars (e.g. 360_day), see the xarray.cftime_range documentation for details.

[2]:
# Normally, we would use xarray to open a dataset, e.g.:
# ds = xr.open_dataset("your_file.nc")

# For this example, let's use a test dataset from xclim:
ds = testing.open_dataset("ERA5/daily_surface_cancities_1990-1993.nc")
ds.tas
[2]:
<xarray.DataArray 'tas' (location: 5, time: 1461)> Size: 29kB
[7305 values with dtype=float32]
Coordinates:
    lon       (location) float32 20B ...
  * location  (location) <U9 180B 'Halifax' 'Montréal' ... 'Victoria'
    lat       (location) float32 20B ...
  * time      (time) datetime64[ns] 12kB 1990-01-01 1990-01-02 ... 1993-12-31
Attributes:
    cell_methods:       time: mean within days
    long_name:          Mean daily surface temperature
    original_variable:  t2m
    standard_name:      air_temperature
    units:              K
[3]:
gdd = xclim.atmos.growing_degree_days(tas=ds.tas, thresh="10.0 degC", freq="YS")
gdd
[3]:
<xarray.DataArray 'growing_degree_days' (location: 5, time: 4)> Size: 80B
array([[7.7692175e+02, 7.0389856e+02, 6.3124426e+02, 6.5188275e+02],
       [1.2268644e+03, 1.3581746e+03, 1.1266934e+03, 1.2207186e+03],
       [6.9931946e+00, 2.3919769e+01, 6.5368652e-01, 1.8717316e+01],
       [9.2984143e+02, 1.0083430e+03, 7.1768420e+02, 6.2909644e+02],
       [5.8704321e+02, 5.0202234e+02, 5.9931348e+02, 5.6781555e+02]],
      dtype=float32)
Coordinates:
    lon       (location) float32 20B -63.4 -73.4 -68.4 -106.7 -123.2
  * location  (location) <U9 180B 'Halifax' 'Montréal' ... 'Victoria'
    lat       (location) float32 20B 44.5 45.5 63.75 52.0 48.5
  * time      (time) datetime64[ns] 32B 1990-01-01 1991-01-01 ... 1993-01-01
Attributes:
    units:          K days
    cell_methods:   time: mean within days time: sum over days
    history:        [2024-02-27 17:46:20] growing_degree_days: GROWING_DEGREE...
    standard_name:  integral_of_air_temperature_excess_wrt_time
    long_name:      Cumulative sum of temperature degrees for mean daily temp...
    description:    Annual growing degree days (mean temperature above 10.0 d...

This computation was made using the growing_degree_days indicator. The same computation could be made through the index. You can see how the metadata is a lot poorer here.

[4]:
gdd = xclim.indices.growing_degree_days(tas=ds.tas, thresh="10.0 degC", freq="YS")
gdd
[4]:
<xarray.DataArray 'tas' (location: 5, time: 4)> Size: 80B
array([[7.7692175e+02, 7.0389856e+02, 6.3124426e+02, 6.5188275e+02],
       [1.2268644e+03, 1.3581746e+03, 1.1266934e+03, 1.2207186e+03],
       [6.9931946e+00, 2.3919769e+01, 6.5368652e-01, 1.8717316e+01],
       [9.2984143e+02, 1.0083430e+03, 7.1768420e+02, 6.2909644e+02],
       [5.8704321e+02, 5.0202234e+02, 5.9931348e+02, 5.6781555e+02]],
      dtype=float32)
Coordinates:
    lon       (location) float32 20B -63.4 -73.4 -68.4 -106.7 -123.2
  * location  (location) <U9 180B 'Halifax' 'Montréal' ... 'Victoria'
    lat       (location) float32 20B 44.5 45.5 63.75 52.0 48.5
  * time      (time) datetime64[ns] 32B 1990-01-01 1991-01-01 ... 1993-01-01
Attributes:
    units:    K d

The call to xclim.indices.growing_degree_days first checked that the input variable units were units of temperature, ran the computation, then set the output’s units to the appropriate unit (here "K d" or Kelvin days). As you can see, the Indicator returned the same output, but with more metadata, it also performed more checks as explained below.

growing_degree_days makes most sense with daily input, but could theoretically accept other source frequencies. The computational layer (``Index``) assumes that users have checked that the input data has the expected temporal frequency and has no missing values. However, no checks are performed, so the output data could be wrong (which is why it’s always safer to use ``Indicator`` objects from the CF layer, as demonstrated in the following section).

Finally, as almost all indices, the function takes a freq argument to specify over what time period it is computed. These are called “Offset Aliases” and are the same as the resampling string arguments. Valid arguments are detailed in pandas docs (note that aliases involving “business” notions are not supported by xarray and thus could raise issues in xclim).

Units handling paradigm

Indices are written in order to be flexible as to the sampling frequency and units of the data. You can use growing_degree_days on, for example, the 6-hourly data, but the output will then be in degree-hour units ("K h"). Moreover, all units, even when untouched by the calculation, will be reformatted into a CF-compliant symbol format. This behaviour was chosen to ensure consistency between all indices.

Very few indices will convert their output to specific units; Rather, it is the dimensionality that will be consistent on output. The Units Handling page goes more into detail on how unit conversion can easily be done.

This doesn’t apply to ``Indicators``. Those will always output data in a specific unit, the one listed in the Indicators.cf_attrs metadata dictionary.

Conventions

As you may have noticed, the growing_degree_days function above was not told along which dimension to operate. In xclim, the temporal dimension is always assumed to be named "time". All functions which reduce or compute over that dimension will expect that name. If you ever have another name in your data, you can simply rename it like:

ds = ds.rename(T="time")

For other names and attributes, xclim tries to follow different sets of conventions. In particular, input data should follow the CF conventions whenever possible for variable attributes. Variable names are usually the ones used in CMIP6, when they exist.

Indicators

Indices with Health Checks and Metadata Attributes

Indicator instances from the CF layer are found in modules bearing the name of the computational realm in which its input variables are typically found: xclim.atmos, xclim.land and xclim.seaIce. These objects run sanity checks on the input variables and set output’s metadata according to CF-conventions when applicable. Some checks involve:

  • Identifying periods where missing data significantly impacts the calculation and omits calculations for those periods. Those are called “missing methods” and are detailed in section Health checks.

  • Appending process history and maintaining the historical provenance of file metadata.

  • Writing Climate and Forecast Convention compliant metadata based on the variables and indices calculated.

Those modules are best used for producing NetCDF files that will be shared with users. See Climate Indicators for a list of available indicators.

If we run the growing_degree_days indicator over a non-daily dataset, we’ll be warned that the input data is not daily. That is, running xclim.atmos.growing_degree_days(ds.air, thresh='10.0 degC', freq='MS') will fail with a ValidationError:

[5]:
# Show that data is not at a daily time frequency

ds6h = xr.tutorial.open_dataset("air_temperature")
xr.infer_freq(ds6h.time)
[5]:
'6h'
[6]:
gdd = xclim.atmos.growing_degree_days(tas=ds6h.air, thresh="10.0 degC", freq="MS")
gdd
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
ValueError: Frequency of time series not strictly in ['D']. To mute this, set xclim's option data_validation='log'.

The above exception was the direct cause of the following exception:

ValidationError                           Traceback (most recent call last)
Cell In[6], line 1
----> 1 gdd = xclim.atmos.growing_degree_days(tas=ds6h.air, thresh="10.0 degC", freq="MS")
      2 gdd

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/indicator.py:828, in Indicator.__call__(self, *args, **kwds)
    825     out_attrs = {}
    826 out_attrs = [out_attrs.copy() for i in range(self.n_outs)]
--> 828 das, params = self._preprocess_and_checks(das, params)
    830 # Get correct variable names for the compute function.
    831 inv_var_map = dict(map(reversed, self._variable_mapping.items()))

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/indicator.py:1518, in ResamplingIndicator._preprocess_and_checks(self, das, params)
   1516 def _preprocess_and_checks(self, das, params):
   1517     """Perform parent's checks and also check if freq is allowed."""
-> 1518     das, params = super()._preprocess_and_checks(das, params)
   1520     # Check if the period is allowed:
   1521     if self.allowed_periods is not None:

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/indicator.py:1553, in IndexingIndicator._preprocess_and_checks(self, das, params)
   1551 def _preprocess_and_checks(self, das: dict[str, DataArray], params: dict[str, Any]):
   1552     """Perform parent's checks and also check if freq is allowed."""
-> 1553     das, params = super()._preprocess_and_checks(das, params)
   1555     indxr = params.get("indexer")
   1556     if indxr:

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/indicator.py:963, in Indicator._preprocess_and_checks(self, das, params)
    961 """Actions to be done after parsing the arguments and before computing."""
    962 # Pre-computation validation checks on DataArray arguments
--> 963 self._bind_call(self.datacheck, **das)
    964 self._bind_call(self.cfcheck, **das)
    965 return das, params

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/indicator.py:1000, in Indicator._bind_call(self, func, **das)
    997     return func(*das.values())
    998 else:
    999     # Call the func using bound arguments
-> 1000     return func(*ba.args, **ba.kwargs)

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/indicator.py:1316, in Indicator.datacheck(self, **das)
   1314 for key, da in das.items():
   1315     if "time" in da.coords and da.time.ndim == 1 and len(da.time) > 3:
-> 1316         datachecks.check_freq(da, self.src_freq, strict=True)
   1318 datachecks.check_common_time(
   1319     [
   1320         da
   (...)
   1323     ]
   1324 )

File <boltons.funcutils.FunctionBuilder-197>:2, in check_freq(var, freq, strict)

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/options.py:126, in datacheck.<locals>.run_check(*args, **kwargs)
    124 @wraps(func)
    125 def run_check(*args, **kwargs):
--> 126     return _run_check(func, DATA_VALIDATION, *args, **kwargs)

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/options.py:118, in _run_check(func, option, *args, **kwargs)
    116     func(*args, **kwargs)
    117 except ValidationError as err:
--> 118     raise_warn_or_log(err, OPTIONS[option], stacklevel=4)

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/utils.py:564, in raise_warn_or_log(err, mode, msg, err_type, stacklevel)
    562     warnings.warn(msg, stacklevel=stacklevel + 1)
    563 else:  # mode == "raise"
--> 564     raise err from err_type(msg)

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/options.py:116, in _run_check(func, option, *args, **kwargs)
    114 """Run function and customize exception handling based on option."""
    115 try:
--> 116     func(*args, **kwargs)
    117 except ValidationError as err:
    118     raise_warn_or_log(err, OPTIONS[option], stacklevel=4)

File ~/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/datachecks.py:54, in check_freq(var, freq, strict)
     50 v_base = parse_offset(v_freq)[1]
     51 if v_base not in exp_base or (
     52     strict and all(compare_offsets(v_freq, "!=", frq) for frq in freq)
     53 ):
---> 54     raise ValidationError(
     55         f"Frequency of time series not {'strictly' if strict else ''} in {freq}. "
     56         "To mute this, set xclim's option data_validation='log'."
     57     )

ValidationError: Frequency of time series not strictly in ['D']. To mute this, set xclim's option data_validation='log'.

Resampling to a daily frequency and running the same indicator succeeds, but we will still get warnings from the CF metadata checks.

[7]:
daily_ds = ds6h.resample(time="D").mean(keep_attrs=True)
gdd = xclim.atmos.growing_degree_days(daily_ds.air, thresh="10.0 degC", freq="YS")
gdd
/home/docs/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/cfchecks.py:42: UserWarning: Variable does not have a `cell_methods` attribute.
  _check_cell_methods(
/home/docs/checkouts/readthedocs.org/user_builds/xclim/conda/stable/lib/python3.12/site-packages/xclim/core/cfchecks.py:46: UserWarning: Variable does not have a `standard_name` attribute.
  check_valid(vardata, "standard_name", data["standard_name"])
[7]:
<xarray.DataArray 'growing_degree_days' (time: 2, lat: 25, lon: 53)> Size: 11kB
array([[[0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        [0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        [3.3139984e+01, 5.0820068e+01, 6.6547577e+01, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        ...,
        [5.2316343e+03, 4.9528970e+03, 4.8025269e+03, ...,
         5.0538716e+03, 4.8727114e+03, 4.7088438e+03],
        [5.3719463e+03, 5.2851040e+03, 5.1083613e+03, ...,
         5.0971768e+03, 5.0670288e+03, 4.9550889e+03],
        [5.4538442e+03, 5.4520718e+03, 5.3567695e+03, ...,
         5.2816997e+03, 5.2564165e+03, 5.2638311e+03]],

       [[0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        [0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        [1.0225220e+00, 5.5400085e+00, 1.0475037e+01, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        ...,
        [5.3532690e+03, 5.1235386e+03, 5.0373765e+03, ...,
         4.9186064e+03, 4.7180039e+03, 4.5376719e+03],
        [5.5628735e+03, 5.5094141e+03, 5.3455288e+03, ...,
         4.9107969e+03, 4.8807036e+03, 4.7494136e+03],
        [5.6539487e+03, 5.6700210e+03, 5.5670591e+03, ...,
         5.0756938e+03, 5.0595415e+03, 5.0693970e+03]]], dtype=float32)
Coordinates:
  * lat      (lat) float32 100B 75.0 72.5 70.0 67.5 65.0 ... 22.5 20.0 17.5 15.0
  * lon      (lon) float32 212B 200.0 202.5 205.0 207.5 ... 325.0 327.5 330.0
  * time     (time) datetime64[ns] 16B 2013-01-01 2014-01-01
Attributes:
    units:          K days
    cell_methods:    time: sum over days
    history:        [2024-02-27 17:46:21] growing_degree_days: GROWING_DEGREE...
    standard_name:  integral_of_air_temperature_excess_wrt_time
    long_name:      Cumulative sum of temperature degrees for mean daily temp...
    description:    Annual growing degree days (mean temperature above 10.0 d...

To suppress the CF validation warnings, we can set xclim to send these warnings to the log instead of raising a warning or an error. We also could set data_validation='warn' to be able to run the indicator on non-daily data. These options can be set globally or within a context manager with set_options.

The missing method which determines if a period should be considered missing or not can be controlled through the check_missing option, globally or contextually. The main missing methods also have options that can be modified.

[8]:
with xclim.set_options(
    check_missing="pct",
    missing_options={"pct": dict(tolerance=0.1)},
    cf_compliance="log",
):
    # Change the missing method to "percent", instead of the default "any"
    # Set the tolerance to 10%, periods with more than 10% of missing data
    #     in the input will be masked in the output.
    gdd = xclim.atmos.growing_degree_days(daily_ds.air, thresh="10.0 degC", freq="MS")
gdd
[8]:
<xarray.DataArray 'growing_degree_days' (time: 24, lat: 25, lon: 53)> Size: 127kB
array([[[  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        ...,
        [393.33267, 372.0302 , 364.96762, ..., 378.77014, 363.77008,
         344.67023],
        [412.90503, 408.17755, 393.6452 , ..., 385.5701 , 377.91254,
         363.1826 ],
        [431.00766, 430.65256, 421.1451 , ..., 407.7927 , 401.37015,
         399.56268]],

       [[  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
...
        [446.97253, 426.00262, 422.16013, ..., 445.85498, 435.3076 ,
         419.8702 ],
        [474.45016, 469.85767, 453.64764, ..., 448.62262, 452.1326 ,
         442.06757],
        [480.70255, 482.03006, 472.37274, ..., 466.99008, 467.75507,
         468.90762]],

       [[  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        ...,
        [412.275  , 388.78497, 390.86002, ..., 414.72757, 405.0926 ,
         392.55267],
        [443.17264, 442.87   , 434.98505, ..., 427.88004, 431.1575 ,
         420.95013],
        [461.8176 , 467.36002, 462.20013, ..., 450.97516, 451.77017,
         451.37265]]], dtype=float32)
Coordinates:
  * lat      (lat) float32 100B 75.0 72.5 70.0 67.5 65.0 ... 22.5 20.0 17.5 15.0
  * lon      (lon) float32 212B 200.0 202.5 205.0 207.5 ... 325.0 327.5 330.0
  * time     (time) datetime64[ns] 192B 2013-01-01 2013-02-01 ... 2014-12-01
Attributes:
    units:          K days
    cell_methods:    time: sum over days
    history:        [2024-02-27 17:46:21] growing_degree_days: GROWING_DEGREE...
    standard_name:  integral_of_air_temperature_excess_wrt_time
    long_name:      Cumulative sum of temperature degrees for mean daily temp...
    description:    Monthly growing degree days (mean temperature above 10.0 ...

Some indicators also expose time-selection arguments as **indexer keywords. This allows to run the index on a subset of the time coordinates, for example only on a specific season, month, or between two dates in every year. It relies on the select_time function. Some indicators will simply select the time period and run the calculations, while others will smartly perform the selection at the right time, when the order of operation makes a difference. All will pass the indexer kwargs to the missing value handling, ensuring that the missing values outside the valid time period are not considered.

The next example computes the annual sum of growing degree days over 10 °C, but only considering days from the 1st of April to the 30th of September.

[9]:
with xclim.set_options(cf_compliance="log"):
    gdd = xclim.atmos.growing_degree_days(
        tas=daily_ds.air, thresh="10 degC", freq="YS", date_bounds=("04-01", "09-30")
    )
gdd
[9]:
<xarray.DataArray 'growing_degree_days' (time: 2, lat: 25, lon: 53)> Size: 11kB
array([[[0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        [0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        [3.3139984e+01, 5.0820068e+01, 6.6547577e+01, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        ...,
        [2.7736931e+03, 2.6248135e+03, 2.5183257e+03, ...,
         2.6201807e+03, 2.5202236e+03, 2.4362007e+03],
        [2.8073428e+03, 2.7539409e+03, 2.6544858e+03, ...,
         2.6141133e+03, 2.6077134e+03, 2.5585962e+03],
        [2.8185562e+03, 2.8164482e+03, 2.7658508e+03, ...,
         2.6862109e+03, 2.6818706e+03, 2.6931655e+03]],

       [[0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        [0.0000000e+00, 0.0000000e+00, 0.0000000e+00, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        [1.0225220e+00, 5.5400085e+00, 1.0475037e+01, ...,
         0.0000000e+00, 0.0000000e+00, 0.0000000e+00],
        ...,
        [2.8183232e+03, 2.6905305e+03, 2.6107832e+03, ...,
         2.5506506e+03, 2.4474631e+03, 2.3652036e+03],
        [2.8695332e+03, 2.8242585e+03, 2.7269106e+03, ...,
         2.5259932e+03, 2.5199480e+03, 2.4677583e+03],
        [2.8881082e+03, 2.8856885e+03, 2.8283706e+03, ...,
         2.5869858e+03, 2.5948555e+03, 2.6111184e+03]]], dtype=float32)
Coordinates:
  * lat      (lat) float32 100B 75.0 72.5 70.0 67.5 65.0 ... 22.5 20.0 17.5 15.0
  * lon      (lon) float32 212B 200.0 202.5 205.0 207.5 ... 325.0 327.5 330.0
  * time     (time) datetime64[ns] 16B 2013-01-01 2014-01-01
Attributes:
    units:          K days
    cell_methods:    time: sum over days
    history:        [2024-02-27 17:46:21] growing_degree_days: GROWING_DEGREE...
    standard_name:  integral_of_air_temperature_excess_wrt_time
    long_name:      Cumulative sum of temperature degrees for mean daily temp...
    description:    Annual growing degree days (mean temperature above 10 degc).

xclim also allows us to call indicators using datasets and variable names.

[10]:
with xclim.set_options(cf_compliance="log"):
    gdd = xclim.atmos.growing_degree_days(
        tas="air", thresh="10.0 degC", freq="MS", ds=daily_ds
    )

    # variable names default to xclim names, so we can even do this:
    renamed_daily_ds = daily_ds.rename(air="tas")
    gdd = xclim.atmos.growing_degree_days(
        thresh="10.0 degC", freq="MS", ds=renamed_daily_ds
    )
gdd
[10]:
<xarray.DataArray 'growing_degree_days' (time: 24, lat: 25, lon: 53)> Size: 127kB
array([[[  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        ...,
        [393.33267, 372.0302 , 364.96762, ..., 378.77014, 363.77008,
         344.67023],
        [412.90503, 408.17755, 393.6452 , ..., 385.5701 , 377.91254,
         363.1826 ],
        [431.00766, 430.65256, 421.1451 , ..., 407.7927 , 401.37015,
         399.56268]],

       [[  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
...
        [446.97253, 426.00262, 422.16013, ..., 445.85498, 435.3076 ,
         419.8702 ],
        [474.45016, 469.85767, 453.64764, ..., 448.62262, 452.1326 ,
         442.06757],
        [480.70255, 482.03006, 472.37274, ..., 466.99008, 467.75507,
         468.90762]],

       [[  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        [  0.     ,   0.     ,   0.     , ...,   0.     ,   0.     ,
           0.     ],
        ...,
        [412.275  , 388.78497, 390.86002, ..., 414.72757, 405.0926 ,
         392.55267],
        [443.17264, 442.87   , 434.98505, ..., 427.88004, 431.1575 ,
         420.95013],
        [461.8176 , 467.36002, 462.20013, ..., 450.97516, 451.77017,
         451.37265]]], dtype=float32)
Coordinates:
  * lat      (lat) float32 100B 75.0 72.5 70.0 67.5 65.0 ... 22.5 20.0 17.5 15.0
  * lon      (lon) float32 212B 200.0 202.5 205.0 207.5 ... 325.0 327.5 330.0
  * time     (time) datetime64[ns] 192B 2013-01-01 2013-02-01 ... 2014-12-01
Attributes:
    units:          K days
    cell_methods:    time: sum over days
    history:        [2024-02-27 17:46:21] growing_degree_days: GROWING_DEGREE...
    standard_name:  integral_of_air_temperature_excess_wrt_time
    long_name:      Cumulative sum of temperature degrees for mean daily temp...
    description:    Monthly growing degree days (mean temperature above 10.0 ...

Finally, we can also get datasets as an output with the as_dataset option.

[11]:
with xclim.set_options(as_dataset=True, cf_compliance="log"):
    gdd_ds = xclim.atmos.growing_degree_days(
        tas=daily_ds.air, thresh="10 degC", freq="YS", date_bounds=("04-01", "09-30")
    )
gdd_ds
[11]:
<xarray.Dataset> Size: 11kB
Dimensions:              (lat: 25, lon: 53, time: 2)
Coordinates:
  * lat                  (lat) float32 100B 75.0 72.5 70.0 ... 20.0 17.5 15.0
  * lon                  (lon) float32 212B 200.0 202.5 205.0 ... 327.5 330.0
  * time                 (time) datetime64[ns] 16B 2013-01-01 2014-01-01
Data variables:
    growing_degree_days  (time, lat, lon) float32 11kB 0.0 0.0 ... 2.611e+03
Attributes:
    history:  [2024-02-27 17:46:21] growing_degree_days: GROWING_DEGREE_DAYS(...

Graphics

Xclim does not have specific functions to create graphics. However, it is built to ensure that Indices and Indicators always have appropriate axis-related metadata that libraries like Matplotlib depend on to generate detailed and informative graphics.

This graphical functionality is entirely thanks to xarray, so the following examples are applicable to generic xarray.DataArray objects. For more examples, see the directions suggested by xarray’s plotting documentation

The xarray plot functions creates a histogram when the DataArray has 3 or more dimensions. In previous steps, xclim automatically filled the long_name and units attributes, which xarray uses to label the x-axis.

[12]:
import matplotlib.pyplot as plt

print("long_name:", gdd.attrs["long_name"])
print("units:", gdd.attrs["units"])

gdd.plot()
plt.suptitle("Summary Statistics Histogram")
plt.show()
long_name: Cumulative sum of temperature degrees for mean daily temperature above 10.0 degc
units: K days
../_images/notebooks_usage_24_1.png

When the DataArray only has a time dimension, xarray plots a timeseries. In this case, xarray uses the long_name and units attributes provided by xclim to label the y-axis.

[13]:
gdd.isel(lon=20, lat=10).plot()
plt.suptitle("Time Series at a Given Geographical Coordinate")
plt.show()
../_images/notebooks_usage_26_0.png

When the DataArray only has 2 dimensions, xarray plots a heatmap. In this case, xarray uses the long_name and units attributes provided by xclim to label the colorbar.

[14]:
gdd.sel(time="2013-07-01").plot()
plt.suptitle("Spatial Pattern at a Specific Time Period")
plt.show()
../_images/notebooks_usage_28_0.png

Writing DataArrays and Datasets to disk

To save the data as a new NetCDF, use to_netcdf:

[15]:
gdd.to_netcdf("monthly_growing_degree_days_data.nc")

It’s possible to save Dataset objects to other file formats. For more information see: xarray’s documentation