Skip to content

Commit

Permalink
use pandas.to_timedelta function instead of invoking deprecated ctor …
Browse files Browse the repository at this point in the history
…arg (#918)

* use pandas.to_timedelta function instead of invoking deprecated ctor (unit argument will be gone)

* handle all old units

* pin pandas >=1.5

* Update weldx/tags/time/timedeltaindex.py

Co-authored-by: Çağtay Fabry <cagtay.fabry@bam.de>
  • Loading branch information
marscher and CagtayFabry authored Mar 12, 2024
1 parent 82b79b1 commit ab583be
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 158 deletions.
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
# Release Notes

## 0.6.8 (unreleased)

### Changes

- use pandas.to_timedelta function to pass units to the TimeDeltaIndex object \[{pull}`918`\].

### Dependencies

- unpin nbval testing dependency.

## 0.6.7 (2023.08.24)

### Added

- added `weldx.exceptions` module with `WeldxException` \[{pull}`871`\] .
- added `weldx.exceptions` module with `WeldxException` \[{pull}`871`\].

### Fixes

Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ requires-python = ">=3.9"
dependencies = [
"numpy >=1.20",
"asdf >=2.15.1",
"pandas >=1.0",
"pandas >=1.5",
"xarray >=2022.9.0",
"scipy >=1.6.2",
"sympy >=1.6",
Expand All @@ -64,7 +64,7 @@ test = [
"pytest >=6",
"pytest-cov",
"pytest-xdist",
"nbval <0.10",
"nbval",
]
vis = [
"weldx_widgets >=0.2",
Expand Down Expand Up @@ -125,6 +125,7 @@ filterwarnings = [
"ignore::DeprecationWarning:traittypes.*:",
"ignore:Passing method to :FutureWarning:xarray.*:",
"error::pint.UnitStrippedWarning",
#"error::FutureWarning", # todo: we want to enable this, as it notifies us about upcoming failures due to upstream changes.
]

[tool.coverage.run]
Expand Down
2 changes: 1 addition & 1 deletion weldx/schemas/weldx.bam.de/weldx/time/time-0.1.0.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ examples:
values: !<asdf://weldx.bam.de/weldx/tags/time/datetimeindex-0.1.0>
start: !<asdf://weldx.bam.de/weldx/tags/time/timestamp-0.1.0> '2021-01-01T00:00:00'
end: !<asdf://weldx.bam.de/weldx/tags/time/timestamp-0.1.0> '2021-01-01T00:00:02'
freq: S
freq: s
min: !<asdf://weldx.bam.de/weldx/tags/time/timestamp-0.1.0> '2021-01-01T00:00:00'
max: !<asdf://weldx.bam.de/weldx/tags/time/timestamp-0.1.0> '2021-01-01T00:00:02'
reference_time: !<asdf://weldx.bam.de/weldx/tags/time/timestamp-0.1.0> '2021-01-01T00:00:00'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ examples:
!<asdf://weldx.bam.de/weldx/tags/time/timedeltaindex-0.1.0>
start: !<asdf://weldx.bam.de/weldx/tags/time/timedelta-0.1.0> P0DT0H0M0S
end: !<asdf://weldx.bam.de/weldx/tags/time/timedelta-0.1.0> P0DT0H0M10S
freq: 2S
freq: 2s
min: !<asdf://weldx.bam.de/weldx/tags/time/timedelta-0.1.0> P0DT0H0M0S
max: !<asdf://weldx.bam.de/weldx/tags/time/timedelta-0.1.0> P0DT0H0M10S
-
Expand Down
10 changes: 10 additions & 0 deletions weldx/tags/time/timedeltaindex.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@

__all__ = ["TimedeltaIndexConverter"]

PANDAS_OLD_UNIT_SUFFIXES = dict(H="h", T="min", S="s", L="ms", U="us", N="ns")


def _handle_converted_pd_tdi_units(node: TaggedDict):
"""Convert changed units in Pandas.Datetimeindex to valid values."""
for suf in PANDAS_OLD_UNIT_SUFFIXES:
node["freq"] = node["freq"].replace(suf, PANDAS_OLD_UNIT_SUFFIXES[suf])


class TimedeltaIndexConverter(WeldxConverter):
"""A simple implementation of serializing pandas TimedeltaIndex."""
Expand All @@ -33,6 +41,7 @@ def to_yaml_tree(self, obj: pd.TimedeltaIndex, tag: str, ctx) -> dict:
def from_yaml_tree(self, node: dict, tag: str, ctx):
"""Construct TimedeltaIndex from tree."""
if "freq" in node:
_handle_converted_pd_tdi_units(node)
return pd.timedelta_range(
start=node["start"], end=node["end"], freq=node["freq"]
)
Expand All @@ -43,6 +52,7 @@ def from_yaml_tree(self, node: dict, tag: str, ctx):
def shape_from_tagged(node: TaggedDict) -> list[int]:
"""Calculate the shape from static tagged tree instance."""
if "freq" in node:
_handle_converted_pd_tdi_units(node)
tdi_temp = pd.timedelta_range(
start=str(node["start"]), # can't handle TaggedString directly
end=str(node["end"]),
Expand Down
4 changes: 2 additions & 2 deletions weldx/tests/asdf_tests/test_asdf_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,12 +424,12 @@ def test_coordinate_system_manager_time_dependencies(
lcs_tdp_1_time_ref = pd.Timestamp("2000-03-17")
lcs_tdp_1 = tf.LocalCoordinateSystem(
coordinates=Q_([[1, 2, 3], [4, 5, 6]], "mm"),
time=pd.TimedeltaIndex([1, 2], "D"),
time=pd.to_timedelta([1, 2], "D"),
time_ref=lcs_tdp_1_time_ref,
)
lcs_tdp_2 = tf.LocalCoordinateSystem(
coordinates=Q_([[3, 7, 3], [9, 5, 8]], "mm"),
time=pd.TimedeltaIndex([1, 2], "D"),
time=pd.to_timedelta([1, 2], "D"),
time_ref=pd.Timestamp("2000-03-21"),
)

Expand Down
24 changes: 14 additions & 10 deletions weldx/tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,9 @@ class TestTimeSeries:

ME = MathematicalExpression
DTI = pd.DatetimeIndex
TDI = pd.TimedeltaIndex
TS = TimeSeries

time_discrete = pd.TimedeltaIndex([0, 1, 2, 3, 4], unit="s")
time_discrete = pd.to_timedelta([0, 1, 2, 3, 4], "s")
value_constant = Q_(1, "m")
values_discrete = Q_(np.array([10, 11, 12, 14, 16]), "mm")
me_expr_str = "a*t + b"
Expand All @@ -251,7 +250,7 @@ class TestTimeSeries:
"data, time, interpolation, shape_exp",
[
(Q_(1, "m"), None, None, (1,)),
(Q_([3, 7, 1], "m"), TDI([0, 1, 2], unit="s"), "step", (3,)),
(Q_([3, 7, 1], "m"), pd.to_timedelta([0, 1, 2], unit="s"), "step", (3,)),
(Q_([3, 7, 1], ""), Q_([0, 1, 2], "s"), "step", (3,)),
(Q_([3, 7, 1], ""), DTI(["2010", "2011", "2012"]), "step", (3,)),
],
Expand Down Expand Up @@ -329,12 +328,17 @@ def test_construction_expression(data, shape_exp, unit_exp):
@pytest.mark.parametrize(
"data, dims, coords, exception_type",
[
(Q_([1, 2, 3], "m"), "time", dict(time=TDI([1, 2, 3])), None),
(Q_([1, 2, 3], "m"), "a", dict(a=TDI([1, 2, 3])), KeyError),
(Q_([[1, 2]], "m"), ("a", "time"), dict(a=[2], time=TDI([1, 2])), None),
(Q_([1, 2, 3], "m"), "time", dict(time=pd.to_timedelta([1, 2, 3])), None),
(Q_([1, 2, 3], "m"), "a", dict(a=pd.to_timedelta([1, 2, 3])), KeyError),
(
Q_([[1, 2]], "m"),
("a", "time"),
dict(a=[2], time=pd.to_timedelta([1, 2])),
None,
),
(Q_([1, 2, 3], "m"), "time", None, KeyError),
(Q_([1, 2, 3], "m"), "time", dict(time=[1, 2, 3]), TypeError),
([1, 2, 3], "time", dict(time=TDI([1, 2, 3])), TypeError),
([1, 2, 3], "time", dict(time=pd.to_timedelta([1, 2, 3])), TypeError),
],
)
@pytest.mark.parametrize("reference_time", [None, "2000-01-01"])
Expand Down Expand Up @@ -382,7 +386,7 @@ def test_construction_exceptions(

# test_comparison -------------------------------------

time_wrong_values = TDI([0, 1, 2, 3, 5], unit="s")
time_wrong_values = pd.to_timedelta([0, 1, 2, 3, 5], "s")
values_discrete_wrong = Q_(np.array([10, 11, 12, 15, 16]), "mm")
values_unit_wrong = Q_(np.array([10, 11, 12, 14, 16]), "s")
values_unit_prefix_wrong = Q_(np.array([10, 11, 12, 14, 16]), "m")
Expand Down Expand Up @@ -426,9 +430,9 @@ def test_comparison(ts, ts_other, result_exp):

# test_interp_time -----------------------------------------------------------------

time_single = pd.TimedeltaIndex([2.1], "s")
time_single = pd.to_timedelta([2.1], "s")
time_single_q = Q_(2.1, "s")
time_mul = pd.TimedeltaIndex([-3, 0.7, 1.1, 1.9, 2.5, 3, 4, 7], "s")
time_mul = pd.to_timedelta([-3, 0.7, 1.1, 1.9, 2.5, 3, 4, 7], "s")
time_mul_q = Q_([-3, 0.7, 1.1, 1.9, 2.5, 3, 4, 7], "s")
results_exp_vec = [
[-8, 3, -3],
Expand Down
70 changes: 43 additions & 27 deletions weldx/tests/test_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import xarray as xr
from pandas import DatetimeIndex as DTI
from pandas import Timedelta, Timestamp, date_range
from pandas import TimedeltaIndex as TDI
from pint import DimensionalityError

from weldx.constants import Q_
Expand Down Expand Up @@ -77,7 +76,7 @@ def _initialize_time_type(

def _is_timedelta(cls_type):
"""Return ``True`` if the passed type is a timedelta type."""
return cls_type in [TDI, Timedelta, np.timedelta64] or (
return cls_type in [pd.to_timedelta, Timedelta, np.timedelta64] or (
cls_type is Time and not Time.is_absolute
)

Expand Down Expand Up @@ -143,7 +142,9 @@ def _get_init_exp_values(
val = [v + offset for v in delta_val]

val = val[0] if data_was_scalar else val
exp_timedelta = Timedelta(val, "s") if data_was_scalar else TDI(val, "s")
exp_timedelta = (
Timedelta(val, "s") if data_was_scalar else pd.to_timedelta(val, "s")
)

# expected datetime
exp_datetime = None
Expand All @@ -168,7 +169,7 @@ def _get_init_exp_values(
(str, "timedelta"),
(Time, "timedelta"),
(Q_, "timedelta"),
TDI,
pd.to_timedelta,
Timedelta,
np.timedelta64,
(str, "datetime"),
Expand Down Expand Up @@ -209,7 +210,7 @@ def test_init(
# skip matrix cases that do not work --------------------
if arr and input_type in [Timedelta, Timestamp]:
return
if not arr and input_type in [DTI, TDI]:
if not arr and input_type in [DTI, pd.to_timedelta]:
return

# create input values -----------------------------------
Expand Down Expand Up @@ -247,7 +248,7 @@ def test_init(
LocalCoordinateSystem(
coordinates=Q_(np.zeros((2, 3)), "mm"), time=["2000", "2001"]
),
TimeSeries(Q_([2, 4, 1], "m"), TDI([1, 2, 3], "s")),
TimeSeries(Q_([2, 4, 1], "m"), pd.to_timedelta([1, 2, 3], "s")),
TimeSeries(Q_([2, 4, 1], "m"), ["2001", "2002", "2003"]),
],
)
Expand All @@ -262,7 +263,7 @@ def test_init_from_time_dependent_types(time_dep_type):
@pytest.mark.parametrize(
"time, time_ref, raises",
[
(TDI([3, 2, 1]), None, ValueError),
(pd.to_timedelta([3, 2, 1]), None, ValueError),
(DTI(["2010", "2000"]), None, ValueError),
(["2010", "2000"], None, ValueError),
(Q_([3, 2, 1], "s"), None, ValueError),
Expand Down Expand Up @@ -290,7 +291,7 @@ def test_init_exception(time, time_ref, raises):
(str, "timedelta"),
(Time, "timedelta"),
(Q_, "timedelta"),
TDI,
pd.to_timedelta,
Timedelta,
np.timedelta64,
(str, "datetime"),
Expand Down Expand Up @@ -331,7 +332,7 @@ def test_add_timedelta(
# skip array cases where the type does not support arrays
if other_type in [Timedelta, Timestamp] and other_is_array:
return
if not other_is_array and other_type in [DTI, TDI]:
if not other_is_array and other_type in [DTI, pd.to_timedelta]:
return

# skip __radd__ cases where we got conflicts with the other types' __add__
Expand All @@ -341,7 +342,7 @@ def test_add_timedelta(
np.timedelta64,
np.datetime64,
DTI,
TDI,
pd.to_timedelta,
):
return

Expand Down Expand Up @@ -392,7 +393,7 @@ def test_add_timedelta(
str,
Time,
Q_,
TDI,
pd.to_timedelta,
Timedelta,
np.timedelta64,
],
Expand Down Expand Up @@ -421,11 +422,16 @@ def test_add_datetime(
# skip array cases where the type does not support arrays
if other_type in [Timedelta, Timestamp] and other_is_array:
return
if not other_is_array and other_type in [DTI, TDI]:
if not other_is_array and other_type in [DTI, pd.to_timedelta]:
return

# skip __radd__ cases where we got conflicts with the other types' __add__
if not other_on_rhs and other_type in (Q_, np.ndarray, np.timedelta64, TDI):
if not other_on_rhs and other_type in (
Q_,
np.ndarray,
np.timedelta64,
pd.to_timedelta,
):
return

# setup rhs
Expand Down Expand Up @@ -477,7 +483,7 @@ def _date_diff(date_1: str, date_2: str, unit: str) -> int:
(str, "timedelta"),
(Time, "timedelta"),
(Q_, "timedelta"),
TDI,
pd.to_timedelta,
Timedelta,
np.timedelta64,
(str, "datetime"),
Expand Down Expand Up @@ -532,7 +538,7 @@ def test_sub(
# skip array cases where the type does not support arrays or scalars
if other_type in [Timedelta, Timestamp] and other_is_array:
return
if not other_is_array and other_type in [DTI, TDI]:
if not other_is_array and other_type in [DTI, pd.to_timedelta]:
return

# skip __rsub__ cases where we got conflicts with the other types' __sub__
Expand All @@ -542,7 +548,7 @@ def test_sub(
np.timedelta64,
np.datetime64,
DTI,
TDI,
pd.to_timedelta,
):
return

Expand Down Expand Up @@ -612,13 +618,16 @@ def test_sub(
"arg, expected",
[
# timedeltas
(TDI([42], unit="ns"), TDI([42], unit="ns")),
(pd.to_timedelta([42], unit="ns"), pd.to_timedelta([42], unit="ns")),
(pd.timedelta_range("0s", "20s", 10), pd.timedelta_range("0s", "20s", 10)),
(np.timedelta64(42), TDI([42], unit="ns")),
(np.array([-10, 0, 20]).astype("timedelta64[ns]"), TDI([-10, 0, 20], "ns")),
(Q_(42, "ns"), TDI([42], unit="ns")),
("10s", TDI(["10s"])),
(["5ms", "10s", "2D"], TDI(["5 ms", "10s", "2D"])),
(np.timedelta64(42), pd.to_timedelta([42], unit="ns")),
(
np.array([-10, 0, 20]).astype("timedelta64[ns]"),
pd.to_timedelta([-10, 0, 20], "ns"),
),
(Q_(42, "ns"), pd.to_timedelta([42], unit="ns")),
("10s", pd.to_timedelta(["10s"])),
(["5ms", "10s", "2D"], pd.to_timedelta(["5 ms", "10s", "2D"])),
# datetimes
(np.datetime64(50, "Y"), DTI(["2020-01-01"])),
("2020-01-01", DTI(["2020-01-01"])),
Expand Down Expand Up @@ -647,10 +656,10 @@ def test_pandas_index(arg, expected):
("1s", "ms", 1000),
("1s", "us", 1000000),
("1s", "ns", 1000000000),
(TDI([1, 2, 3], "s"), "s", [1, 2, 3]),
(TDI([1, 2, 3], "s"), "ms", np.array([1, 2, 3]) * 1e3),
(TDI([1, 2, 3], "s"), "us", np.array([1, 2, 3]) * 1e6),
(TDI([1, 2, 3], "s"), "ns", np.array([1, 2, 3]) * 1e9),
(pd.to_timedelta([1, 2, 3], "s"), "s", [1, 2, 3]),
(pd.to_timedelta([1, 2, 3], "s"), "ms", np.array([1, 2, 3]) * 1e3),
(pd.to_timedelta([1, 2, 3], "s"), "us", np.array([1, 2, 3]) * 1e6),
(pd.to_timedelta([1, 2, 3], "s"), "ns", np.array([1, 2, 3]) * 1e9),
("2020-01-01", "s", 0),
],
)
Expand Down Expand Up @@ -775,7 +784,14 @@ def test_resample_exceptions(values, number_or_interval, raises):
],
date_range("2020-02-01", periods=8, freq="1D"),
),
([TDI([1, 5]), TDI([2, 6, 7]), TDI([1, 3, 7])], TDI([1, 2, 3, 5, 6, 7])),
(
[
pd.to_timedelta([1, 5]),
pd.to_timedelta([2, 6, 7]),
pd.to_timedelta([1, 3, 7]),
],
pd.to_timedelta([1, 2, 3, 5, 6, 7]),
),
],
)
@pytest.mark.parametrize("test_instance", [True, False])
Expand Down
Loading

0 comments on commit ab583be

Please sign in to comment.