diff --git a/.github/workflows/add-to-project.yml b/.github/workflows/add-to-project.yml index f189731aa..d11e98554 100644 --- a/.github/workflows/add-to-project.yml +++ b/.github/workflows/add-to-project.yml @@ -24,7 +24,7 @@ jobs: allowed-endpoints: > api.github.com:443 - - uses: actions/add-to-project@1b844f0c5ac6446a402e0cb3693f9be5eca188c5 # v0.6.1 + - uses: actions/add-to-project@9bfe908f2eaa7ba10340b31e314148fcfe6a2458 # v1.0.1 with: project-url: https://github.com/orgs/Ouranosinc/projects/6 github-token: ${{ secrets.ADD_TO_PROJECT_TOKEN }} diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index c7d7e5b17..f1f87e84c 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -46,7 +46,7 @@ jobs: - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 with: persist-credentials: false - - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 + - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.x" - name: Config Commit Bot diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 8ab5ecb6a..5ae49b592 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -29,4 +29,4 @@ jobs: uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: 'Dependency Review' - uses: actions/dependency-review-action@0fa40c3c10055986a88de3baa0d6ec17c5a894b3 + uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b diff --git a/.github/workflows/label-on-approval.yml b/.github/workflows/label-on-approval.yml index 8195d7b4d..16bd7a59c 100644 --- a/.github/workflows/label-on-approval.yml +++ b/.github/workflows/label-on-approval.yml @@ -63,7 +63,7 @@ jobs: allowed-endpoints: > api.github.com:443 - name: Find comment - uses: peter-evans/find-comment@d5fe37641ad8451bdd80312415672ba26c86575e # v3.0.0 + uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0 id: fc with: issue-number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 614576d98..d1acec8ff 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,7 +54,7 @@ jobs: pypi.org:443 - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Set up Python${{ matrix.python-version }} - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: ${{ matrix.python-version }} - name: Install pylint and tox @@ -92,7 +92,7 @@ jobs: raw.githubusercontent.com:443 - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Set up Python${{ matrix.python-version }} - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: ${{ matrix.python-version }} - name: Install tox @@ -175,7 +175,7 @@ jobs: sudo apt-get update sudo apt-get install libeigen3-dev - name: Set up Python${{ matrix.python-version }} - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: ${{ matrix.python-version }} - name: Install tox diff --git a/.github/workflows/publish-mastodon.yml b/.github/workflows/publish-mastodon.yml index f498af56f..108cdbdd0 100644 --- a/.github/workflows/publish-mastodon.yml +++ b/.github/workflows/publish-mastodon.yml @@ -80,7 +80,7 @@ jobs: - name: Send toot to Mastodon if: ${{ github.event.inputs.dry-run != 'true' }} || ${{ github.event_name == 'release' }} - uses: cbrgm/mastodon-github-action@23ae66e7b229f11174f6018873c5f8ce3d7a6391 # v2.0.5 + uses: cbrgm/mastodon-github-action@1c5321597ce853344afff70aa5685887243f43fa # v2.0.6 with: url: ${{ secrets.MASTODON_URL }} access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 5fa99b8a6..3a14bf29d 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -29,7 +29,7 @@ jobs: upload.pypi.org:443 - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Set up Python3 - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.x" - name: Install packaging libraries diff --git a/.github/workflows/tag-testpypi.yml b/.github/workflows/tag-testpypi.yml index 2d7a22a9f..b679cf7d6 100644 --- a/.github/workflows/tag-testpypi.yml +++ b/.github/workflows/tag-testpypi.yml @@ -29,7 +29,7 @@ jobs: test.pypi.org:443 - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 - name: Set up Python3 - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0 + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: "3.x" - name: Install packaging libraries diff --git a/.github/workflows/testdata-version.yml b/.github/workflows/testdata-version.yml index 9a7277b06..60452f569 100644 --- a/.github/workflows/testdata-version.yml +++ b/.github/workflows/testdata-version.yml @@ -46,7 +46,7 @@ jobs: echo "Latest xclim-testdata tag: ${{ env.XCLIM_TESTDATA_TAG }}" echo "Tag for xclim-testdata in CI: ${{ env.XCLIM_TESTDATA_BRANCH }}" - name: Find Comment - uses: peter-evans/find-comment@d5fe37641ad8451bdd80312415672ba26c86575e # v3.0.0 + uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0 id: fc with: issue-number: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/workflow-warning.yml b/.github/workflows/workflow-warning.yml index ce4d99752..729cf0142 100644 --- a/.github/workflows/workflow-warning.yml +++ b/.github/workflows/workflow-warning.yml @@ -33,7 +33,7 @@ jobs: allowed-endpoints: > api.github.com:443 - name: Find comment - uses: peter-evans/find-comment@d5fe37641ad8451bdd80312415672ba26c86575e # v3.0.0 + uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0 id: fc with: issue-number: ${{ github.event.pull_request.number }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6c81166d4..80a94f490 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ default_language_version: repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.2 hooks: - id: pyupgrade args: ['--py39-plus'] @@ -28,7 +28,7 @@ repos: - id: toml-sort-fix exclude: '.pylintrc.toml' - repo: https://github.com/adrienverge/yamllint.git - rev: v1.33.0 + rev: v1.35.1 hooks: - id: yamllint args: [ '--config-file=.yamllint.yaml' ] @@ -41,11 +41,11 @@ repos: hooks: - id: isort - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 + rev: v0.3.5 hooks: - id: ruff - repo: https://github.com/pylint-dev/pylint - rev: v3.0.3 + rev: v3.1.0 hooks: - id: pylint args: [ '--rcfile=.pylintrc.toml', '--errors-only', '--jobs=0', '--disable=import-error' ] @@ -56,7 +56,7 @@ repos: additional_dependencies: [ 'flake8-alphabetize', 'flake8-rst-docstrings '] args: [ '--config=.flake8' ] - repo: https://github.com/nbQA-dev/nbQA - rev: 1.7.1 + rev: 1.8.5 hooks: - id: nbqa-pyupgrade args: [ '--py39-plus' ] @@ -85,7 +85,7 @@ repos: hooks: - id: gitleaks - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.27.4 + rev: 0.28.1 hooks: - id: check-github-workflows - id: check-readthedocs diff --git a/CHANGES.rst b/CHANGES.rst index 6d56a2b1c..4e706f662 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,7 @@ Changelog v0.49.0 (unreleased) -------------------- -Contributors to this version: Trevor James Smith (:user:`Zeitsperre`), Pascal Bourgault (:user:`aulemahal`), Éric Dupuis (:user:`coxipi`). +Contributors to this version: Trevor James Smith (:user:`Zeitsperre`), Pascal Bourgault (:user:`aulemahal`), Juliette Lavoie (:user:`juliettelavoie`), Éric Dupuis (:user:`coxipi`). Announcements ^^^^^^^^^^^^^ @@ -22,6 +22,8 @@ Bug fixes ^^^^^^^^^ * Fixed an bug in sdba's ``map_groups`` that prevented passing DataArrays with cftime coordinates if the ``sdba_encode_cf`` option was True. (:issue:`1673`, :pull:`1674`). * Fixed bug (:issue:`1678`, :pull:`1679`) in sdba where a loaded training dataset could not be used for adjustment +* Fixed bug with loess smoothing for an array full of NaNs. (:pull:`1699`). +* Fixed and adapted ``time_bnds`` to the newest xarray. (:pull:`1700`) Internal changes ^^^^^^^^^^^^^^^^ diff --git a/pyproject.toml b/pyproject.toml index 2965ba57d..d5a61354d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,7 +123,7 @@ target-version = [ ] [tool.bumpversion] -current_version = "0.48.3-dev.3" +current_version = "0.48.3-dev.5" commit = true commit_args = "--no-verify" tag = false diff --git a/tests/test_calendar.py b/tests/test_calendar.py index c0e0a0ac3..2d37f0348 100644 --- a/tests/test_calendar.py +++ b/tests/test_calendar.py @@ -61,6 +61,7 @@ def da(index): @pytest.mark.parametrize("freq", ["6480h", "302431min", "23144781s"]) def test_time_bnds(freq, datetime_index, cftime_index): da_datetime = da(datetime_index).resample(time=freq) + out_time = da_datetime.mean() da_cftime = da(cftime_index).resample(time=freq) cftime_bounds = time_bnds(da_cftime, freq=freq) @@ -72,16 +73,9 @@ def test_time_bnds(freq, datetime_index, cftime_index): # cftime resolution goes down to microsecond only, code below corrects # that to allow for comparison with pandas datetime cftime_ends += np.timedelta64(999, "ns") - if hasattr(da_datetime, "_full_index"): - datetime_starts = da_datetime._full_index.to_period(freq).start_time - datetime_ends = da_datetime._full_index.to_period(freq).end_time - else: - datetime_starts = ( - da_datetime.groupers[0].group_as_index.to_period(freq).start_time - ) - datetime_ends = da_datetime.groupers[0].group_as_index.to_period(freq).end_time - assert_array_equal(cftime_starts, datetime_starts) - assert_array_equal(cftime_ends, datetime_ends) + out_periods = out_time.indexes["time"].to_period(freq) + assert_array_equal(cftime_starts, out_periods.start_time) + assert_array_equal(cftime_ends, out_periods.end_time) @pytest.mark.parametrize("typ", ["pd", "xr"]) diff --git a/tests/test_sdba/test_loess.py b/tests/test_sdba/test_loess.py index 189bf6cb0..15835014e 100644 --- a/tests/test_sdba/test_loess.py +++ b/tests/test_sdba/test_loess.py @@ -1,7 +1,9 @@ from __future__ import annotations import numpy as np +import pandas as pd import pytest +import xarray as xr from xclim.sdba.loess import _constant_regression # noqa from xclim.sdba.loess import _gaussian_weighting # noqa @@ -62,3 +64,22 @@ def test_loess_smoothing(use_dask, open_dataset): # Same but we force not to use the optimization tasmooth3 = loess_smoothing(tas, f=0.1, equal_spacing=False) np.testing.assert_allclose(tasmooth, tasmooth3, rtol=1e-3, atol=1e-3) + + +@pytest.mark.slow +@pytest.mark.parametrize("use_dask", [True, False]) +def test_loess_smoothing_nan(use_dask): + # create data with one axis full of nan + data = np.random.randn(2, 2, 10) + data[0, 0] = [np.nan] * 10 + da = xr.DataArray( + data, + dims=["scenario", "model", "time"], + coords={"time": pd.date_range("2000-01-01", periods=10, freq="YS")}, + ).chunk({"time": -1}) + + out = loess_smoothing(da) + + assert out.dims == da.dims + # check that the output is all nan on the axis with nan in the input + assert np.isnan(out.values[0, 0]).all() diff --git a/xclim/__init__.py b/xclim/__init__.py index cf8cb04d8..ccc4ad28f 100644 --- a/xclim/__init__.py +++ b/xclim/__init__.py @@ -16,7 +16,7 @@ __author__ = """Travis Logan""" __email__ = "logan.travis@ouranos.ca" -__version__ = "0.48.3-dev.3" +__version__ = "0.48.3-dev.5" _module_data = _files("xclim.data") diff --git a/xclim/core/calendar.py b/xclim/core/calendar.py index 3da213480..de66bbefd 100644 --- a/xclim/core/calendar.py +++ b/xclim/core/calendar.py @@ -1077,8 +1077,14 @@ def time_bnds( # noqa: C901 elif isinstance(time, (DataArrayResample, DatasetResample)): for grouper in time.groupers: if "time" in grouper.dims: - time = grouper.group_as_index + datetime = grouper.unique_coord.data + freq = freq or grouper.grouper.freq + if datetime.dtype == "O": + time = xr.CFTimeIndex(datetime) + else: + time = pd.DatetimeIndex(datetime) break + else: raise ValueError( 'Got object resampled along another dimension than "time".' diff --git a/xclim/sdba/loess.py b/xclim/sdba/loess.py index d98c4ff68..2a527d993 100644 --- a/xclim/sdba/loess.py +++ b/xclim/sdba/loess.py @@ -95,6 +95,8 @@ def _loess_nb( out = np.full(x.size, np.NaN) y = y[~nan] x = x[~nan] + if x.size == 0: + return out n = x.size yest = np.zeros(n)