Skip to content

Commit 65933b6

Browse files
authored
chore: support casting floats and list-likes to timedelta series (#1362)
* chore: support casting floats and list-likes to timedelta series * use singledispatch to handle different input types * fix format * stop using singledispatch due to compatibility issues
1 parent 3c3e14c commit 65933b6

File tree

3 files changed

+53
-11
lines changed

3 files changed

+53
-11
lines changed

bigframes/operations/timedelta_ops.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@ class ToTimedeltaOp(base_ops.UnaryOp):
2626
unit: typing.Literal["us", "ms", "s", "m", "h", "d", "W"]
2727

2828
def output_type(self, *input_types):
29-
if input_types[0] is not dtypes.INT_DTYPE:
30-
raise TypeError("expected integer input")
31-
return dtypes.TIMEDELTA_DTYPE
29+
if input_types[0] in (dtypes.INT_DTYPE, dtypes.FLOAT_DTYPE):
30+
return dtypes.TIMEDELTA_DTYPE
31+
raise TypeError("expected integer or float input")

bigframes/pandas/core/tools/timedeltas.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,26 @@
1818
timedeltas as vendored_pandas_timedeltas,
1919
)
2020
import pandas as pd
21+
import pandas.api.types as pdtypes
2122

2223
from bigframes import operations as ops
23-
from bigframes import series
24+
from bigframes import series, session
2425

2526

2627
def to_timedelta(
27-
arg: typing.Union[series.Series, str, int, float],
28+
arg,
2829
unit: typing.Optional[vendored_pandas_timedeltas.UnitChoices] = None,
29-
) -> typing.Union[series.Series, pd.Timedelta]:
30-
if not isinstance(arg, series.Series):
31-
return pd.to_timedelta(arg, unit)
30+
*,
31+
session: typing.Optional[session.Session] = None,
32+
):
33+
if isinstance(arg, series.Series):
34+
canonical_unit = "us" if unit is None else _canonicalize_unit(unit)
35+
return arg._apply_unary_op(ops.ToTimedeltaOp(canonical_unit))
3236

33-
canonical_unit = "us" if unit is None else _canonicalize_unit(unit)
34-
return arg._apply_unary_op(ops.ToTimedeltaOp(canonical_unit))
37+
if pdtypes.is_list_like(arg):
38+
return to_timedelta(series.Series(arg), unit, session=session)
39+
40+
return pd.to_timedelta(arg, unit)
3541

3642

3743
to_timedelta.__doc__ = vendored_pandas_timedeltas.to_timedelta.__doc__

tests/system/small/test_pandas.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ def test_to_datetime_timestamp_inputs(arg, utc, output_in_utc):
763763
"micros",
764764
],
765765
)
766-
def test_to_timedelta_with_bf_series(session, unit):
766+
def test_to_timedelta_with_bf_integer_series(session, unit):
767767
bf_series = bpd.Series([1, 2, 3], session=session)
768768
pd_series = pd.Series([1, 2, 3])
769769

@@ -779,6 +779,42 @@ def test_to_timedelta_with_bf_series(session, unit):
779779
)
780780

781781

782+
def test_to_timedelta_with_bf_float_series_value_rounded_down(session):
783+
bf_series = bpd.Series([1.2, 2.9], session=session)
784+
785+
actual_result = (
786+
typing.cast(bpd.Series, bpd.to_timedelta(bf_series, "us"))
787+
.to_pandas()
788+
.astype("timedelta64[ns]")
789+
)
790+
791+
expected_result = pd.Series([pd.Timedelta(1, "us"), pd.Timedelta(2, "us")])
792+
pd.testing.assert_series_equal(
793+
actual_result, expected_result, check_index_type=False
794+
)
795+
796+
797+
@pytest.mark.parametrize(
798+
"input",
799+
[
800+
pytest.param([1, 2, 3], id="list"),
801+
pytest.param((1, 2, 3), id="tuple"),
802+
pytest.param(pd.Series([1, 2, 3]), id="pandas-series"),
803+
],
804+
)
805+
def test_to_timedelta_with_list_like_input(session, input):
806+
actual_result = (
807+
typing.cast(bpd.Series, bpd.to_timedelta(input, "s", session=session))
808+
.to_pandas()
809+
.astype("timedelta64[ns]")
810+
)
811+
812+
expected_result = pd.Series(pd.to_timedelta(input, "s"))
813+
pd.testing.assert_series_equal(
814+
actual_result, expected_result, check_index_type=False
815+
)
816+
817+
782818
@pytest.mark.parametrize(
783819
"unit",
784820
["Y", "M", "whatever"],

0 commit comments

Comments
 (0)