Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
16ba4a2
implemented the option to test for significance when comparing with a…
FranziskaWinterstein May 28, 2026
1f40778
correct attaching of ancillary variable
FranziskaWinterstein Jun 11, 2026
e22937c
prepared ttest as its own preprocessor function, still open how to ke…
FranziskaWinterstein Jun 12, 2026
fc338d0
Reverted code s.t. p_value is attached and not a mask 0/1
FranziskaWinterstein Jun 23, 2026
6cf75ac
added example recipe where ttest is used
FranziskaWinterstein Jun 24, 2026
2beb91e
Merge branch 'main' into add_ttest
FranziskaWinterstein Jun 24, 2026
e64db5d
Merge remote-tracking branch 'origin/main' into add_ttest
schlunma Jul 1, 2026
48844bc
Remove recipe from repo
schlunma Jul 1, 2026
04802a9
pre-commit
schlunma Jul 1, 2026
1caccba
Optimize preprocessor order
schlunma Jul 1, 2026
b4e86cf
Optimize function order
schlunma Jul 1, 2026
720faab
Add support for lazy and non-lazy data
schlunma Jul 1, 2026
8192833
pre-commit
schlunma Jul 1, 2026
16f3c2a
Add tests for t_test with products
schlunma Jul 1, 2026
93ee134
Remove duplicate bias test
schlunma Jul 1, 2026
e5ebef5
Fix lazy t-test calculation
schlunma Jul 1, 2026
0d1f95c
Add tests for t-test calculation of lazy and non-lazy cubes
schlunma Jul 1, 2026
e70a943
t_test intrinsically preserves metadata
schlunma Jul 1, 2026
aedd6a6
Support masked data
schlunma Jul 1, 2026
2066b5e
dask ttest_ind does not support axis=None, so we will not support it …
schlunma Jul 1, 2026
1a1bae8
Add test case for 1D data
schlunma Jul 1, 2026
bba79ac
Make sure that bias also preserves cell measures and add correspondin…
schlunma Jul 1, 2026
04006d7
Improve docs
schlunma Jul 1, 2026
48b89a2
Merge branch 'main' into add_ttest
schlunma Jul 21, 2026
9c7f297
Generalize t-test so it can handle multiple coordinates at once
schlunma Jul 21, 2026
32f57dd
Add early check for presence of t-test reference in recipe
schlunma Jul 21, 2026
980fdd6
Add docs for t-test calculation
schlunma Jul 21, 2026
f8e65ec
Add example of p-value ancillary var
schlunma Jul 21, 2026
69d2a96
metric -> t_test
schlunma Jul 21, 2026
e6d1168
Improve docs
schlunma Jul 21, 2026
1c73391
Better docs
schlunma Jul 21, 2026
33ace69
Better example
schlunma Jul 21, 2026
21862bc
Typo
schlunma Jul 21, 2026
8fc32d0
Better docstring
schlunma Jul 21, 2026
dcbe055
Full stop missing
schlunma Jul 21, 2026
cff6911
Better dosctring
schlunma Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 115 additions & 3 deletions doc/recipe/preprocessor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2638,8 +2638,11 @@ This module contains the following preprocessor functions:

* ``bias``: Calculate absolute or relative biases with respect to a reference
dataset.
* ``distance_metric``: Calculate absolute or relative biases with respect to a
reference dataset.
* ``distance_metric``: Calculate distance metrics with respect to a reference
dataset.
* ``t_test``: Calculate t-test (null hypothesis: 2 independent samples have
identical mean) with respect to a reference dataset and attach the
corresponding p-value as ancillary variable to the data.

``bias``
--------
Expand Down Expand Up @@ -2937,8 +2940,117 @@ See also :func:`esmvalcore.preprocessor.distance_metric`.
.. _Weighted Earth mover's distance: https://pythonot.github.io/
quickstart.html#computing-wasserstein-distance

.. _Other:
.. _t_test:

``t_test``
----------

This function performs a :func:`t-test <scipy.stats.ttest_ind>` (null
hypothesis: 2 independent samples have identical mean) with respect to a
reference dataset and attaches the corresponding p-value as ancillary variable
to the data.

With `Iris <https://scitools-iris.readthedocs.io/>`__, the p-value can be accessed with the following code:

.. code-block:: python

import iris
cube = iris.load_cube("esmvaltool_output/recipe_t_test_20260721_095242/preproc/t-test/tas/CMIP6_BCC-ESM1_Amon_historical_r1i1p1f1_tas_gn_2000-2005.nc")
p_value = cube.ancillary_variable("p-value")
print(p_value)

The returned p-value looks like this:

.. code-block::

AncillaryVariable : p-value / (1)
data: [
[0.21600565 , 0.18489039 , ..., 0.3673955 , 0.27788544 ],
[0.3407415 , 0.3905643 , ..., -- , 0.58700126 ],
...,
[-- , -- , ..., -- , -- ],
[-- , -- , ..., -- , -- ]]
shape: (18, 36)
dtype: float32
long_name: 'p-value'
var_name: 'pvalue'

For this preprocessor, exactly one input dataset needs to be declared as
``reference_for_t_test: true`` in the recipe, e.g.,

.. code-block:: yaml

datasets:
- {dataset: CanESM5, project: CMIP6, ensemble: r1i1p1f1, grid: gn}
- {dataset: CESM2, project: CMIP6, ensemble: r1i1p1f1, grid: gn}
- {dataset: MIROC6, project: CMIP6, ensemble: r1i1p1f1, grid: gn}
- {dataset: ERA-Interim, project: OBS6, tier: 3, type: reanaly, version: 1,
reference_for_t_test: true}

In the example above, ERA-Interim is used as reference dataset for the t-test
calculation.

It is also possible to use the output from the :ref:`multi-model statistics` or
:ref:`ensemble statistics` preprocessor as reference dataset.
In this case, make sure to use ``reference_for_t_test: true`` for each dataset
that will be used to create the reference dataset and use the option
``keep_input_datasets: false`` for the multi-dataset preprocessor.
For example:

.. code-block:: yaml

datasets:
- {dataset: CanESM5, group: ref, reference_for_t_test: true}
- {dataset: CESM2, group: ref, reference_for_t_test: true}
- {dataset: MIROC6, group: notref}

preprocessors:
calculate_t_test:
custom_order: true
multi_model_statistics:
statistics: [mean]
span: overlap
groupby: [group]
keep_input_datasets: false
t_test:

Here, the t-test for MIROC6 is calculated relative to the the multi-model mean
from the models CanESM5 and CESM2.

All datasets need to have the same shape.
To ensure this, the preprocessors :func:`esmvalcore.preprocessor.regrid` might
be helpful.

The ``t_test`` preprocessor supports the following arguments in the recipe:

* ``coords`` (:obj:`list` of :obj:`str`, default: ``None``): Coordinates over
which the t-test is calculated.
If ``None``, calculate the metric over all coordinates, which results in a
scalar cube.
* Other parameters are passed to :func:`scipy.stats.ttest_ind`.

Example:

.. code-block:: yaml

preprocessors:
welchs_t_test:
t_test:
coords: [time]
equal_var: false
nan_policy: omit

This will perform a `Welch's t-test
<https://en.wikipedia.org/wiki/Welch%27s_t-test>`__, which does not assume
equal variances between the samples.
In addition, all masked values will be omitted in the t-test calculation (the
default behavior is to return masked values if at least one input value is
masked).

See also :func:`esmvalcore.preprocessor.t_test`.


.. _Other:

Other
=====
Expand Down
7 changes: 7 additions & 0 deletions esmvalcore/_recipe/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,13 @@ def _check_ref_attributes(products: set, *, step: str, attr_name: str) -> None:
)


reference_for_t_test_preproc = partial(
_check_ref_attributes,
step="t_test",
attr_name="reference_for_t_test",
)


def statistics_preprocessors(settings: dict) -> None:
"""Check options of statistics preprocessors."""
mm_stats = (
Expand Down
1 change: 1 addition & 0 deletions esmvalcore/_recipe/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ def _get_preprocessor_products(

check.reference_for_bias_preproc(products)
check.reference_for_distance_metric_preproc(products)
check.reference_for_t_test_preproc(products)

_configure_multi_product_preprocessor(
products=products,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,12 @@ projects:
ta:
raw_name: [tm1_cav, tm1_ave, tm1]
channel: Amon
# tro3:
# raw_name: [O3_cav, O3_ave, O3]
# channel: Amon
tro3:
raw_name: [O3_cav, O3_ave, O3]
channel: Amon
raw_name: [O3_p39_cav, O3_cav, O3_ave, O3]
channel: AERmon
ua:
raw_name: [um1_cav, um1_ave, um1]
channel: Amon
Expand Down
8 changes: 7 additions & 1 deletion esmvalcore/preprocessor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@
meridional_statistics,
zonal_statistics,
)
from esmvalcore.preprocessor._compare_with_refs import bias, distance_metric
from esmvalcore.preprocessor._compare_with_refs import (
bias,
distance_metric,
t_test,
)
from esmvalcore.preprocessor._concatenate import concatenate
from esmvalcore.preprocessor._cycles import amplitude
from esmvalcore.preprocessor._dask_progress import _compute_with_progress
Expand Down Expand Up @@ -218,6 +222,7 @@
# Multi model statistics
"multi_model_statistics",
# Comparison with reference datasets
"t_test",
"bias",
"distance_metric",
# Remove supplementary variables from cube
Expand Down Expand Up @@ -261,6 +266,7 @@
"multi_model_statistics",
"mask_multimodel",
"mask_fillvalues",
"t_test",
}


Expand Down
Loading