Skip to content

Commit 9577830

Browse files
authored
Merge pull request #316 from openscm/xarray-update
Fix xarray pin
2 parents 112f969 + 3a89ec1 commit 9577830

12 files changed

Lines changed: 103 additions & 92 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ jobs:
6464
runs-on: ubuntu-latest
6565
strategy:
6666
matrix:
67-
python-version: [ "3.9" ]
68-
xarray-version: [ "0.16.2", "0.17.0", "0.18.2" ]
67+
python-version: [ "3.11" ]
68+
xarray-version: [ "0.18.2", "2025.01", "2025.03" ]
6969

7070
steps:
7171
- name: Checkout repository

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,6 @@ repos:
4545
hooks:
4646
- id: blackdoc
4747
- repo: https://github.com/python-poetry/poetry
48-
rev: '1.4.2'
48+
rev: '2.1.2'
4949
hooks:
5050
- id: poetry-check

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ licence-check: ## Check that licences of the dependencies are suitable
5454

5555
.PHONY: virtual-environment
5656
virtual-environment: ## update virtual environment, create a new one if it doesn't already exist
57-
poetry lock --no-update
57+
poetry lock
5858
# Put virtual environments in the project
5959
poetry config virtualenvs.in-project true
6060
poetry install --all-extras

changelog/316.fix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed up incompatibility with latest xarray releases

docs/source/notebooks/ops.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@
217217
# [Pint](https://pint.readthedocs.io) and some other work, this operation
218218
# is also unit aware.
219219
#
220-
# There are two methods of integration available, `cumtrapz` and `cumsum`.
220+
# There are two methods of integration available, `cumulative_trapezoid` and `cumsum`.
221221
# The method that should be used depends on the data you are integrating,
222222
# specifically whether the data are piecewise linear or piecewise constant.
223223
#
@@ -226,18 +226,18 @@
226226
#
227227
# Other output such as effective radiative forcing, concentrations or decadal
228228
# timeseries of emissions, represent a point estimate or an average over a period.
229-
# These timeseries are therefore piecewise linear and should use the `cumtrapz`
229+
# These timeseries are therefore piecewise linear and should use the `cumulative_trapezoid`
230230
# method.
231231

232232
# %%
233233
with warnings.catch_warnings():
234234
# Ignore warning about nans in the historical timeseries
235235
warnings.filterwarnings("ignore", module="scmdata.ops")
236236

237-
# Radiative Forcings are piecewise-linear so `cumtrapz` should be used
237+
# Radiative Forcings are piecewise-linear so `cumulative_trapezoid` should be used
238238
erf_integral = (
239239
db_forcing.filter(variable="Effective Radiative Forcing")
240-
.cumtrapz()
240+
.cumulative_trapezoid()
241241
.convert_unit("TJ / m^2")
242242
)
243243

@@ -416,13 +416,7 @@
416416
)
417417
erf_total_for_shift_rel_to_ref_period_shifted[
418418
"label"
419-
] = "rel. to {} - {} (median of {} - {} mean adjusted to {})".format(
420-
ref_period[0],
421-
ref_period[-1],
422-
evaluation_period[0],
423-
evaluation_period[-1],
424-
target,
425-
)
419+
] = f"rel. to {ref_period[0]} - {ref_period[-1]} (median of {evaluation_period[0]} - {evaluation_period[-1]} mean adjusted to {target})" # noqa: E501
426420

427421
# %%
428422
pdf = run_append(

pyproject.toml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ classifiers = [
2727
]
2828

2929
[tool.poetry.dependencies]
30-
python = "^3.9"
30+
# TODO: get rid of this stupid upper level bound
31+
python = ">=3.9,<4"
3132
cftime = ">=1.5"
3233
numpy = "*"
3334
openscm-units = "*"
@@ -38,10 +39,13 @@ pint-pandas = "*"
3839
python-dateutil = "*"
3940
tqdm = "*"
4041
six = "*"
41-
xarray = "*"
42+
xarray = [
43+
{ version = "*", python = ">=3.10" },
44+
{ version = "<=2025.03", python = ">=3.9,<3.10" },
45+
]
4246
nc-time-axis = { version = ">=1.2.0", optional = true }
4347
typing-extensions = "*"
44-
matplotlib = { version = "^3.7.1", optional = true }
48+
matplotlib = { version = ">=3.7.1", optional = true }
4549
seaborn = { version = ">=0.12.0", optional = true }
4650
netCDF4 = { version = "*", optional = true }
4751
openpyxl = { version = "*", optional = true }
@@ -88,6 +92,9 @@ sphinx-autodocgen = "^1.3"
8892
jupytext = "^1.14.5"
8993
sphinx-copybutton = "^0.5.2"
9094

95+
[tool.poetry.requires-plugins]
96+
poetry-plugin-export = ">=1.8"
97+
9198
[build-system]
9299
requires = ["poetry-core"]
93100
build-backend = "poetry.core.masonry.api"
@@ -260,6 +267,7 @@ authorized_licenses = [
260267
"mit",
261268
"mit license",
262269
"Mozilla Public License 2.0 (MPL 2.0)",
270+
"psf-2.0",
263271
"python software foundation",
264272
"python software foundation license",
265273
"zpl 2.1",

src/scmdata/groupby.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
Functionality for grouping and filtering ScmRun objects
33
"""
4+
45
from __future__ import annotations
56

67
import warnings
@@ -9,13 +10,20 @@
910

1011
import numpy as np
1112
import pandas as pd
13+
import xarray as xr
1214
from numpy.typing import NDArray
13-
from xarray.core import ops
1415
from xarray.core.common import ImplementsArrayReduce
1516

1617
from scmdata._typing import MetadataValue
1718
from scmdata.run import BaseScmRun, GenericRun
1819

20+
# Hack central, a better way to do this would be nice
21+
xr_version_split = xr.__version__.split(".")
22+
if int(xr_version_split[0]) < 2025 or xr_version_split[1] == "01": # noqa: PLR2004
23+
from xarray.core import ops
24+
else:
25+
from xarray.computation import ops
26+
1927
if TYPE_CHECKING:
2028
from pandas.core.groupby.generic import DataFrameGroupBy
2129
from typing_extensions import Concatenate, ParamSpec
@@ -246,7 +254,7 @@ def _combine(
246254

247255
def reduce(
248256
self,
249-
func: Callable[Concatenate[NDArray[np.float_], P], NDArray[np.float_]],
257+
func: Callable[Concatenate[NDArray[np.float64], P], NDArray[np.float64]],
250258
dim: str | Iterable[str] | None = None,
251259
axis: str | Iterable[int] | None = None,
252260
*args: P.args,

src/scmdata/ops.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
`Pint's Pandas interface <https://pint.readthedocs.io/en/0.13/pint-pandas.html>`_
66
to handle unit conversions automatically
77
"""
8+
89
import warnings
910

1011
import numpy as np
@@ -113,10 +114,9 @@ def _perform_op(base, other, op, use_pint_units=True):
113114
elif op == "subtract":
114115
out.append(base[col] - other[col])
115116

116-
except KeyError:
117-
raise KeyError( # noqa: TRY200
118-
f"No equivalent in `other` for {list(zip(col_names, col))}"
119-
)
117+
except KeyError as exc:
118+
msg = f"No equivalent in `other` for {list(zip(col_names, col))}"
119+
raise KeyError(msg) from exc
120120

121121
elif op == "multiply":
122122
out.append(base[col] * other[col])
@@ -227,17 +227,17 @@ def subtract(self, other, op_cols, **kwargs):
227227
>>> fos_minus_afolu.head()
228228
time 2010-01-01 2020-01-01
229229
model region scenario unit variable
230-
idealised World|NH idealised gigatC / a Emissions|CO2|Fossil - AFOLU -0.001 3.995
231-
World|SH idealised gigatC / a Emissions|CO2|Fossil - AFOLU 1.997 5.993
230+
idealised World|NH idealised gigatC / yr Emissions|CO2|Fossil - AFOLU -0.001 3.995
231+
World|SH idealised gigatC / yr Emissions|CO2|Fossil - AFOLU 1.997 5.993
232232
233233
>>> nh = start.filter(region="World|NH")
234234
>>> sh = start.filter(region="World|SH")
235235
>>> nh_minus_sh = nh.subtract(sh, op_cols={"region": "World|NH - SH"})
236236
>>> nh_minus_sh.head()
237237
time 2010-01-01 2020-01-01
238238
model region scenario unit variable
239-
idealised World|NH - SH idealised gigatC / a Emissions|CO2|Fossil -2.0 -2.0
240-
megatC / a Emissions|CO2|AFOLU -2.0 -2.0
239+
idealised World|NH - SH idealised gigatC / yr Emissions|CO2|Fossil -2.0 -2.0
240+
megatC / yr Emissions|CO2|AFOLU -2.0 -2.0
241241
"""
242242
out = _perform_op(
243243
prep_for_op(self, op_cols, self.meta.columns, **kwargs),
@@ -336,17 +336,17 @@ def add(self, other, op_cols, **kwargs):
336336
>>> fos_plus_afolu.head()
337337
time 2010-01-01 2020-01-01
338338
model region scenario unit variable
339-
idealised World|NH idealised gigatC / a Emissions|CO2|Fossil + AFOLU 0.001 4.005
340-
World|SH idealised gigatC / a Emissions|CO2|Fossil + AFOLU 2.003 6.007
339+
idealised World|NH idealised gigatC / yr Emissions|CO2|Fossil + AFOLU 0.001 4.005
340+
World|SH idealised gigatC / yr Emissions|CO2|Fossil + AFOLU 2.003 6.007
341341
342342
>>> nh = start.filter(region="World|NH")
343343
>>> sh = start.filter(region="World|SH")
344344
>>> nh_plus_sh = nh.add(sh, op_cols={"region": "World|NH + SH"})
345345
>>> nh_plus_sh.head()
346346
time 2010-01-01 2020-01-01
347347
model region scenario unit variable
348-
idealised World|NH + SH idealised gigatC / a Emissions|CO2|Fossil 2.0 10.0
349-
megatC / a Emissions|CO2|AFOLU 4.0 12.0
348+
idealised World|NH + SH idealised gigatC / yr Emissions|CO2|Fossil 2.0 10.0
349+
megatC / yr Emissions|CO2|AFOLU 4.0 12.0
350350
"""
351351
out = _perform_op(
352352
prep_for_op(self, op_cols, self.meta.columns, **kwargs),
@@ -544,14 +544,14 @@ def divide(self, other, op_cols, **kwargs):
544544
World|SH idealised MtC / yr Emissions|CO2|AFOLU 3.0 7.0
545545
546546
>>> fos_divide_afolu = fos.divide(
547-
... afolu, op_cols={"variable": "Emissions|CO2|Fossil / AFOLU"}
547+
... afolu, op_cols={"variable": "Emissions|CO2|Fossil / yrFOLU"}
548548
... )
549549
>>> # The rows align and the units are handled automatically
550550
>>> fos_divide_afolu.convert_unit("dimensionless").head()
551551
time 2010-01-01 2020-01-01
552552
model region scenario unit variable
553-
idealised World|NH idealised dimensionless Emissions|CO2|Fossil / AFOLU 0.000000 800.000000
554-
World|SH idealised dimensionless Emissions|CO2|Fossil / AFOLU 666.666667 857.142857
553+
idealised World|NH idealised dimensionless Emissions|CO2|Fossil / yrFOLU 0.000000 800.000000
554+
World|SH idealised dimensionless Emissions|CO2|Fossil / yrFOLU 666.666667 857.142857
555555
556556
>>> nh = start.filter(region="World|NH")
557557
>>> sh = start.filter(region="World|SH")
@@ -597,7 +597,7 @@ def cumsum(self, out_var=None, check_annual=True):
597597
arbitrary day/month of the year has not been implemented, if that would be
598598
useful raise an issue on GitHub.
599599
600-
If the timeseries are piecewise-linear, :meth:`cumtrapz` should be used instead.
600+
If the timeseries are piecewise-linear, :meth:`cumulative_trapezoid` should be used instead.
601601
602602
Parameters
603603
----------
@@ -618,7 +618,7 @@ def cumsum(self, out_var=None, check_annual=True):
618618
619619
See Also
620620
--------
621-
:func:`cumtrapz`
621+
:func:`cumulative_trapezoid`
622622
623623
Raises
624624
------
@@ -677,7 +677,7 @@ def cumsum(self, out_var=None, check_annual=True):
677677
return out
678678

679679

680-
def cumtrapz(self, out_var=None):
680+
def cumulative_trapezoid(self, out_var=None):
681681
"""
682682
Integrate with respect to time using the trapezoid rule
683683
@@ -743,7 +743,9 @@ def cumtrapz(self, out_var=None):
743743
# some thinking about unit handling
744744
_initial = 0.0
745745
out = pd.DataFrame(
746-
scipy.integrate.cumtrapz(y=ts, x=times_in_s, axis=1, initial=_initial)
746+
scipy.integrate.cumulative_trapezoid(
747+
y=ts, x=times_in_s, axis=1, initial=_initial
748+
)
747749
)
748750
out.index = ts.index
749751
out.columns = ts.columns
@@ -790,7 +792,7 @@ def integrate(self, out_var=None):
790792
See Also
791793
--------
792794
:meth:`cumsum`
793-
:meth:`cumtrapz`
795+
:meth:`cumulative_trapezoid`
794796
795797
Raises
796798
------
@@ -805,14 +807,14 @@ def integrate(self, out_var=None):
805807
data will also contain nans.
806808
DeprecationWarning
807809
This function has been deprecated in preference to :meth:`cumsum` and
808-
:meth:`cumtrapz`.
810+
:meth:`cumulative_trapezoid`.
809811
"""
810812
warnings.warn(
811-
"integrate has been deprecated in preference of cumsum and cumtrapz",
813+
"integrate has been deprecated in preference of cumsum and cumulative_trapezoid",
812814
DeprecationWarning,
813815
)
814816

815-
return cumtrapz(self, out_var)
817+
return cumulative_trapezoid(self, out_var)
816818

817819

818820
def delta_per_delta_time(self, out_var=None):
@@ -1144,7 +1146,7 @@ def inject_ops_methods(cls):
11441146
("divide", divide),
11451147
("integrate", integrate),
11461148
("cumsum", cumsum),
1147-
("cumtrapz", cumtrapz),
1149+
("cumulative_trapezoid", cumulative_trapezoid),
11481150
("delta_per_delta_time", delta_per_delta_time),
11491151
("linear_regression", linear_regression),
11501152
("linear_regression_gradient", linear_regression_gradient),

0 commit comments

Comments
 (0)