Skip to content

Commit

Permalink
depr(python) Deprecate the offset argument in dt.round, `dt.trunc…
Browse files Browse the repository at this point in the history
…ate`, and `DataFrame.upsample` (#15478)

Co-authored-by: Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com>
Co-authored-by: Marco Edward Gorelli <marcogorelli@protonmail.com>
  • Loading branch information
3 people committed Apr 9, 2024
1 parent 44f1097 commit 33eedfb
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 49 deletions.
10 changes: 10 additions & 0 deletions py-polars/polars/dataframe/frame.py
Expand Up @@ -5483,6 +5483,10 @@ def upsample(
Interval will start 'every' duration.
offset
Change the start of the date_range by this offset.
.. deprecated:: 0.20.19
This argument is deprecated and will be removed in the next breaking
release. Instead, chain `upsample` with `dt.offset_by`.
group_by
First group by these columns and then upsample for every group.
maintain_order
Expand Down Expand Up @@ -5530,6 +5534,12 @@ def upsample(
└─────────────────────┴────────┴────────┘
"""
every = deprecate_saturating(every)
if offset is not None:
issue_deprecation_warning(
"`offset` is deprecated and will be removed in the next breaking release. "
"Instead, chain `upsample` with `dt.offset_by`.",
version="0.20.19",
)
offset = deprecate_saturating(offset)
if group_by is None:
group_by = []
Expand Down
20 changes: 20 additions & 0 deletions py-polars/polars/expr/datetime.py
Expand Up @@ -57,6 +57,10 @@ def truncate(
Every interval start and period length
offset
Offset the window
.. deprecated:: 0.20.19
This argument is deprecated and will be removed in the next breaking
release. Instead, chain `dt.truncate` with `dt.offset_by`.
use_earliest
Determine how to deal with ambiguous datetimes:
Expand Down Expand Up @@ -182,6 +186,12 @@ def truncate(
"""
every = deprecate_saturating(every)
offset = deprecate_saturating(offset)
if offset is not None:
issue_deprecation_warning(
"`offset` is deprecated and will be removed in the next breaking release. "
"Instead, chain `dt.truncate` with `dt.offset_by`.",
version="0.20.19",
)
if not isinstance(every, pl.Expr):
every = parse_as_duration_string(every)

Expand Down Expand Up @@ -237,6 +247,10 @@ def round(
Every interval start and period length
offset
Offset the window
.. deprecated:: 0.20.19
This argument is deprecated and will be removed in the next breaking
release. Instead, chain `dt.round` with `dt.offset_by`.
ambiguous
Determine how to deal with ambiguous datetimes:
Expand Down Expand Up @@ -329,6 +343,12 @@ def round(
"""
every = deprecate_saturating(every)
offset = deprecate_saturating(offset)
if offset is not None:
issue_deprecation_warning(
"`offset` is deprecated and will be removed in the next breaking release. "
"Instead, chain `dt.round` with `dt.offset_by`.",
version="0.20.19",
)
if offset is None:
offset = "0ns"

Expand Down
8 changes: 8 additions & 0 deletions py-polars/polars/series/datetime.py
Expand Up @@ -1589,6 +1589,10 @@ def truncate(
Every interval start and period length
offset
Offset the window
.. deprecated:: 0.20.19
This argument is deprecated and will be removed in the next breaking
release. Instead, chain `dt.truncate` with `dt.offset_by`.
use_earliest
Determine how to deal with ambiguous datetimes:
Expand Down Expand Up @@ -1736,6 +1740,10 @@ def round(
Every interval start and period length
offset
Offset the window
.. deprecated:: 0.20.19
This argument is deprecated and will be removed in the next breaking
release. Instead, chain `dt.round` with `dt.offset_by`.
ambiguous
Determine how to deal with ambiguous datetimes:
Expand Down
165 changes: 116 additions & 49 deletions py-polars/tests/unit/datatypes/test_temporal.py
Expand Up @@ -562,7 +562,10 @@ def test_rolling() -> None:
("Europe/Warsaw", ZoneInfo("Europe/Warsaw")),
],
)
def test_upsample(time_zone: str | None, tzinfo: ZoneInfo | timezone | None) -> None:
@pytest.mark.parametrize("offset", [None, "1mo"])
def test_upsample(
time_zone: str | None, tzinfo: ZoneInfo | timezone | None, offset: None | str
) -> None:
df = pl.DataFrame(
{
"time": [
Expand All @@ -576,38 +579,101 @@ def test_upsample(time_zone: str | None, tzinfo: ZoneInfo | timezone | None) ->
}
).with_columns(pl.col("time").dt.replace_time_zone(time_zone).set_sorted())

up = df.upsample(
time_column="time", every="1mo", group_by="admin", maintain_order=True
).select(pl.all().forward_fill())
context_manager: contextlib.AbstractContextManager[pytest.WarningsRecorder | None]
msg = (
"`offset` is deprecated and will be removed in the next breaking release. "
"Instead, chain `upsample` with `dt.offset_by`."
)
if offset is not None:
context_manager = pytest.deprecated_call(match=msg)
else:
context_manager = contextlib.nullcontext()

with context_manager:
up = df.upsample(
time_column="time",
every="1mo",
group_by="admin",
maintain_order=True,
offset=offset,
).select(pl.all().forward_fill())
# this print will panic if timezones feature is not activated
# don't remove
print(up)

expected = pl.DataFrame(
if offset is not None:
expected = pl.DataFrame(
{
"time": [
datetime(2021, 3, 1, 0, 0),
datetime(2021, 4, 1, 0, 0),
datetime(2021, 5, 1, 0, 0),
datetime(2021, 5, 1, 0, 0),
datetime(2021, 6, 1, 0, 0),
],
"admin": [None, None, "Åland", "Åland", "Netherlands"],
"test2": [None, None, 2, 2, 3],
}
)
else:
expected = pl.DataFrame(
{
"time": [
datetime(2021, 2, 1, 0, 0),
datetime(2021, 3, 1, 0, 0),
datetime(2021, 4, 1, 0, 0),
datetime(2021, 5, 1, 0, 0),
datetime(2021, 4, 1, 0, 0),
datetime(2021, 5, 1, 0, 0),
datetime(2021, 6, 1, 0, 0),
],
"admin": [
"Åland",
"Åland",
"Åland",
"Åland",
"Netherlands",
"Netherlands",
"Netherlands",
],
"test2": [0, 0, 0, 2, 1, 1, 3],
}
)
expected = expected.with_columns(pl.col("time").dt.replace_time_zone(time_zone))

assert_frame_equal(up, expected)


def test_offset_deprecated() -> None:
df = pl.DataFrame(
{
"time": [
datetime(2021, 2, 1, 0, 0),
datetime(2021, 3, 1, 0, 0),
datetime(2021, 4, 1, 0, 0),
datetime(2021, 5, 1, 0, 0),
datetime(2021, 4, 1, 0, 0),
datetime(2021, 5, 1, 0, 0),
datetime(2021, 6, 1, 0, 0),
],
"admin": [
"Åland",
"Åland",
"Åland",
"Åland",
"Netherlands",
"Netherlands",
"Netherlands",
datetime(2021, 2, 1),
datetime(2021, 4, 1),
datetime(2021, 5, 1),
datetime(2021, 6, 1),
],
"test2": [0, 0, 0, 2, 1, 1, 3],
"admin": ["Åland", "Netherlands", "Åland", "Netherlands"],
"test2": [0, 1, 2, 3],
}
).with_columns(pl.col("time").dt.replace_time_zone(time_zone))
).sort("time")

assert_frame_equal(up, expected)
# truncate
with pytest.deprecated_call():
df.select(pl.col("time").dt.truncate(every="1mo", offset="1d"))

# round
with pytest.deprecated_call():
df.select(pl.col("time").dt.round(every="1mo", offset="1d"))

ser = df.to_series(0)
# truncate
with pytest.deprecated_call():
ser.dt.truncate(every="1mo", offset="1d")

# round
with pytest.deprecated_call():
ser.dt.round(every="1mo", offset="1d")


@pytest.mark.parametrize("time_zone", [None, "US/Central"])
Expand Down Expand Up @@ -639,7 +705,18 @@ def test_upsample_crossing_dst(
"values": [1, 2, 3],
}
)
result = df.upsample(time_column="time", every="1d", offset=offset)
context_manager: contextlib.AbstractContextManager[pytest.WarningsRecorder | None]
msg = (
"`offset` is deprecated and will be removed in the next breaking release. "
"Instead, chain `upsample` with `dt.offset_by`."
)
if offset is not None:
context_manager = pytest.deprecated_call(match=msg)
else:
context_manager = contextlib.nullcontext()

with context_manager:
result = df.upsample(time_column="time", every="1d", offset=offset)
expected = pl.DataFrame(
{
"time": expected_time,
Expand Down Expand Up @@ -1751,9 +1828,8 @@ def test_replace_time_zone_ambiguous_null() -> None:

def test_use_earliest_deprecation() -> None:
# strptime
with pytest.warns(
DeprecationWarning,
match="Please replace `use_earliest=True` with `ambiguous='earliest'`",
with pytest.deprecated_call(
match="Please replace `use_earliest=True` with `ambiguous='earliest'`"
):
result = pl.Series(["2020-10-25 01:00"]).str.strptime(
pl.Datetime("us", "Europe/London"), use_earliest=True
Expand All @@ -1762,9 +1838,8 @@ def test_use_earliest_deprecation() -> None:
pl.Datetime("us", "Europe/London"), ambiguous="earliest"
)
assert_series_equal(result, expected)
with pytest.warns(
DeprecationWarning,
match="Please replace `use_earliest=False` with `ambiguous='latest'`",
with pytest.deprecated_call(
match="Please replace `use_earliest=False` with `ambiguous='latest'`"
):
result = pl.Series(["2020-10-25 01:00"]).str.strptime(
pl.Datetime("us", "Europe/London"), use_earliest=False
Expand All @@ -1778,47 +1853,39 @@ def test_use_earliest_deprecation() -> None:
ser = pl.Series(["2020-10-25 01:00"]).str.to_datetime(
time_zone="Europe/London", ambiguous="latest"
)
with pytest.warns(
DeprecationWarning,
):
with pytest.deprecated_call():
result = ser.dt.truncate("1h", use_earliest=True)
expected = ser.dt.truncate("1h")
assert_series_equal(result, expected)
with pytest.warns(
DeprecationWarning,
):
with pytest.deprecated_call():
result = ser.dt.truncate("1h", use_earliest=True)
expected = ser.dt.truncate("1h")
assert_series_equal(result, expected)

# replace_time_zone
ser = pl.Series([datetime(2020, 10, 25, 1)])
with pytest.warns(
DeprecationWarning,
match="Please replace `use_earliest=True` with `ambiguous='earliest'`",
with pytest.deprecated_call(
match="Please replace `use_earliest=True` with `ambiguous='earliest'`"
):
result = ser.dt.replace_time_zone("Europe/London", use_earliest=True)
expected = ser.dt.replace_time_zone("Europe/London", ambiguous="earliest")
assert_series_equal(result, expected)
with pytest.warns(
DeprecationWarning,
match="Please replace `use_earliest=False` with `ambiguous='latest'`",
with pytest.deprecated_call(
match="Please replace `use_earliest=False` with `ambiguous='latest'`"
):
result = ser.dt.replace_time_zone("Europe/London", use_earliest=False)
expected = ser.dt.replace_time_zone("Europe/London", ambiguous="latest")
assert_series_equal(result, expected)

# pl.datetime
with pytest.warns(
DeprecationWarning,
match="Please replace `use_earliest=True` with `ambiguous='earliest'`",
with pytest.deprecated_call(
match="Please replace `use_earliest=True` with `ambiguous='earliest'`"
):
result = pl.select(pl.datetime(2020, 10, 25, 1, use_earliest=True))["datetime"]
expected = pl.select(pl.datetime(2020, 10, 25, 1, ambiguous="earliest"))["datetime"]
assert_series_equal(result, expected)
with pytest.warns(
DeprecationWarning,
match="Please replace `use_earliest=False` with `ambiguous='latest'`",
with pytest.deprecated_call(
match="Please replace `use_earliest=False` with `ambiguous='latest'`"
):
result = pl.select(pl.datetime(2020, 10, 25, 1, use_earliest=False))["datetime"]
expected = pl.select(pl.datetime(2020, 10, 25, 1, ambiguous="latest"))["datetime"]
Expand Down

0 comments on commit 33eedfb

Please sign in to comment.