288 lines
8.2 KiB
Python
288 lines
8.2 KiB
Python
from __future__ import annotations
|
|
|
|
import contextlib
|
|
from datetime import time
|
|
from typing import TYPE_CHECKING, overload
|
|
|
|
from polars import functions as F
|
|
from polars._utils.parse import parse_into_expression
|
|
from polars._utils.wrap import wrap_expr
|
|
from polars.functions.range._utils import parse_interval_argument
|
|
|
|
with contextlib.suppress(ImportError): # Module not available when building docs
|
|
import polars._plr as plr
|
|
|
|
if TYPE_CHECKING:
|
|
from datetime import timedelta
|
|
from typing import Literal
|
|
|
|
from polars import Expr, Series
|
|
from polars._typing import ClosedInterval, IntoExprColumn
|
|
|
|
|
|
@overload
|
|
def time_range(
|
|
start: time | IntoExprColumn | None = ...,
|
|
end: time | IntoExprColumn | None = ...,
|
|
interval: str | timedelta = ...,
|
|
*,
|
|
closed: ClosedInterval = ...,
|
|
eager: Literal[False] = ...,
|
|
) -> Expr: ...
|
|
|
|
|
|
@overload
|
|
def time_range(
|
|
start: time | IntoExprColumn | None = ...,
|
|
end: time | IntoExprColumn | None = ...,
|
|
interval: str | timedelta = ...,
|
|
*,
|
|
closed: ClosedInterval = ...,
|
|
eager: Literal[True],
|
|
) -> Series: ...
|
|
|
|
|
|
@overload
|
|
def time_range(
|
|
start: time | IntoExprColumn | None = ...,
|
|
end: time | IntoExprColumn | None = ...,
|
|
interval: str | timedelta = ...,
|
|
*,
|
|
closed: ClosedInterval = ...,
|
|
eager: bool,
|
|
) -> Series | Expr: ...
|
|
|
|
|
|
def time_range(
|
|
start: time | IntoExprColumn | None = None,
|
|
end: time | IntoExprColumn | None = None,
|
|
interval: str | timedelta = "1h",
|
|
*,
|
|
closed: ClosedInterval = "both",
|
|
eager: bool = False,
|
|
) -> Series | Expr:
|
|
"""
|
|
Generate a time range.
|
|
|
|
Parameters
|
|
----------
|
|
start
|
|
Lower bound of the time range.
|
|
If omitted, defaults to `time(0,0,0,0)`.
|
|
end
|
|
Upper bound of the time range.
|
|
If omitted, defaults to `time(23,59,59,999999)`.
|
|
interval
|
|
Interval of the range periods, specified as a Python `timedelta` object
|
|
or using the Polars duration string language (see "Notes" section below).
|
|
closed : {'both', 'left', 'right', 'none'}
|
|
Define which sides of the range are closed (inclusive).
|
|
eager
|
|
Evaluate immediately and return a `Series`.
|
|
If set to `False` (default), return an expression instead.
|
|
|
|
Returns
|
|
-------
|
|
Expr or Series
|
|
Column of data type `:class:Time`.
|
|
|
|
Notes
|
|
-----
|
|
`interval` is created according to the following string language:
|
|
|
|
- 1ns (1 nanosecond)
|
|
- 1us (1 microsecond)
|
|
- 1ms (1 millisecond)
|
|
- 1s (1 second)
|
|
- 1m (1 minute)
|
|
- 1h (1 hour)
|
|
- 1d (1 calendar day)
|
|
- 1w (1 calendar week)
|
|
- 1mo (1 calendar month)
|
|
- 1q (1 calendar quarter)
|
|
- 1y (1 calendar year)
|
|
|
|
Or combine them:
|
|
"3d12h4m25s" # 3 days, 12 hours, 4 minutes, and 25 seconds
|
|
|
|
By "calendar day", we mean the corresponding time on the next day (which may
|
|
not be 24 hours, due to daylight savings). Similarly for "calendar week",
|
|
"calendar month", "calendar quarter", and "calendar year".
|
|
|
|
See Also
|
|
--------
|
|
time_ranges : Create a column of time ranges.
|
|
|
|
Examples
|
|
--------
|
|
>>> from datetime import time, timedelta
|
|
>>> pl.time_range(
|
|
... start=time(14, 0),
|
|
... interval=timedelta(hours=3, minutes=15),
|
|
... eager=True,
|
|
... ).alias("time")
|
|
shape: (4,)
|
|
Series: 'time' [time]
|
|
[
|
|
14:00:00
|
|
17:15:00
|
|
20:30:00
|
|
23:45:00
|
|
]
|
|
"""
|
|
interval = parse_interval_argument(interval)
|
|
for unit in ("y", "mo", "w", "d"):
|
|
if unit in interval:
|
|
msg = f"invalid interval unit for time_range: found {unit!r}"
|
|
raise ValueError(msg)
|
|
|
|
if start is None:
|
|
start = time(0, 0, 0)
|
|
if end is None:
|
|
end = time(23, 59, 59, 999999)
|
|
|
|
start_pyexpr = parse_into_expression(start)
|
|
end_pyexpr = parse_into_expression(end)
|
|
|
|
result = wrap_expr(plr.time_range(start_pyexpr, end_pyexpr, interval, closed))
|
|
|
|
if eager:
|
|
return F.select(result).to_series()
|
|
|
|
return result
|
|
|
|
|
|
@overload
|
|
def time_ranges(
|
|
start: time | IntoExprColumn | None = ...,
|
|
end: time | IntoExprColumn | None = ...,
|
|
interval: str | timedelta = ...,
|
|
*,
|
|
closed: ClosedInterval = ...,
|
|
eager: Literal[False] = ...,
|
|
) -> Expr: ...
|
|
|
|
|
|
@overload
|
|
def time_ranges(
|
|
start: time | IntoExprColumn | None = ...,
|
|
end: time | IntoExprColumn | None = ...,
|
|
interval: str | timedelta = ...,
|
|
*,
|
|
closed: ClosedInterval = ...,
|
|
eager: Literal[True],
|
|
) -> Series: ...
|
|
|
|
|
|
@overload
|
|
def time_ranges(
|
|
start: time | IntoExprColumn | None = ...,
|
|
end: time | IntoExprColumn | None = ...,
|
|
interval: str | timedelta = ...,
|
|
*,
|
|
closed: ClosedInterval = ...,
|
|
eager: bool,
|
|
) -> Series | Expr: ...
|
|
|
|
|
|
def time_ranges(
|
|
start: time | IntoExprColumn | None = None,
|
|
end: time | IntoExprColumn | None = None,
|
|
interval: str | timedelta = "1h",
|
|
*,
|
|
closed: ClosedInterval = "both",
|
|
eager: bool = False,
|
|
) -> Series | Expr:
|
|
"""
|
|
Create a column of time ranges.
|
|
|
|
Parameters
|
|
----------
|
|
start
|
|
Lower bound of the time range.
|
|
If omitted, defaults to `time(0, 0, 0, 0)`.
|
|
end
|
|
Upper bound of the time range.
|
|
If omitted, defaults to `time(23, 59, 59, 999999)`.
|
|
interval
|
|
Interval of the range periods, specified as a Python `timedelta` object
|
|
or using the Polars duration string language (see "Notes" section below).
|
|
closed : {'both', 'left', 'right', 'none'}
|
|
Define which sides of the range are closed (inclusive).
|
|
eager
|
|
Evaluate immediately and return a `Series`.
|
|
If set to `False` (default), return an expression instead.
|
|
|
|
Returns
|
|
-------
|
|
Expr or Series
|
|
Column of data type `List(Time)`.
|
|
|
|
Notes
|
|
-----
|
|
`interval` is created according to the following string language:
|
|
|
|
- 1ns (1 nanosecond)
|
|
- 1us (1 microsecond)
|
|
- 1ms (1 millisecond)
|
|
- 1s (1 second)
|
|
- 1m (1 minute)
|
|
- 1h (1 hour)
|
|
- 1d (1 calendar day)
|
|
- 1w (1 calendar week)
|
|
- 1mo (1 calendar month)
|
|
- 1q (1 calendar quarter)
|
|
- 1y (1 calendar year)
|
|
|
|
Or combine them:
|
|
"3d12h4m25s" # 3 days, 12 hours, 4 minutes, and 25 seconds
|
|
|
|
By "calendar day", we mean the corresponding time on the next day (which may
|
|
not be 24 hours, due to daylight savings). Similarly for "calendar week",
|
|
"calendar month", "calendar quarter", and "calendar year".
|
|
|
|
See Also
|
|
--------
|
|
time_range : Generate a single time range.
|
|
|
|
Examples
|
|
--------
|
|
>>> from datetime import time
|
|
>>> df = pl.DataFrame(
|
|
... {
|
|
... "start": [time(9, 0), time(10, 0)],
|
|
... "end": time(11, 0),
|
|
... }
|
|
... )
|
|
>>> df.with_columns(time_range=pl.time_ranges("start", "end"))
|
|
shape: (2, 3)
|
|
┌──────────┬──────────┬────────────────────────────────┐
|
|
│ start ┆ end ┆ time_range │
|
|
│ --- ┆ --- ┆ --- │
|
|
│ time ┆ time ┆ list[time] │
|
|
╞══════════╪══════════╪════════════════════════════════╡
|
|
│ 09:00:00 ┆ 11:00:00 ┆ [09:00:00, 10:00:00, 11:00:00] │
|
|
│ 10:00:00 ┆ 11:00:00 ┆ [10:00:00, 11:00:00] │
|
|
└──────────┴──────────┴────────────────────────────────┘
|
|
"""
|
|
interval = parse_interval_argument(interval)
|
|
for unit in ("y", "mo", "w", "d"):
|
|
if unit in interval:
|
|
msg = f"invalid interval unit for time_range: found {unit!r}"
|
|
raise ValueError(msg)
|
|
|
|
if start is None:
|
|
start = time(0, 0, 0)
|
|
if end is None:
|
|
end = time(23, 59, 59, 999999)
|
|
|
|
start_pyexpr = parse_into_expression(start)
|
|
end_pyexpr = parse_into_expression(end)
|
|
|
|
result = wrap_expr(plr.time_ranges(start_pyexpr, end_pyexpr, interval, closed))
|
|
|
|
if eager:
|
|
return F.select(result).to_series()
|
|
|
|
return result
|