Source code for corr_vars.utils.base
"""Dependency-free primitives shared by the other :mod:`corr_vars.utils` modules.
Everything here operates on a single frame, expression or offset and pulls in
nothing from the rest of ``corr_vars.utils``, so any module may import it without
risking an import cycle.
"""
from __future__ import annotations
from datetime import timedelta
import polars as pl
from typing import TYPE_CHECKING, cast
if TYPE_CHECKING:
from corr_vars.definitions import OffsetLike
from polars._typing import ColumnNameOrSelector
__all__ = [
"as_expr",
"col_length",
"column_selector",
"columns",
"is_empty",
"normalize_offset",
"row_length",
"struct_fields",
]
[docs]
def as_expr(value: pl.Expr | str) -> pl.Expr:
if isinstance(value, pl.Expr):
return value
return pl.col(value)
[docs]
def columns(value: pl.DataFrame | pl.LazyFrame) -> list[str]:
if isinstance(value, pl.DataFrame):
return value.columns
return value.collect_schema().names()
[docs]
def struct_fields(
value: pl.DataFrame | pl.LazyFrame, field: ColumnNameOrSelector
) -> list[str]:
return columns(value.select(field).unnest(field))
[docs]
def col_length(value: pl.DataFrame | pl.LazyFrame) -> int:
return len(columns(value))
[docs]
def row_length(value: pl.DataFrame | pl.LazyFrame) -> int:
if isinstance(value, pl.DataFrame):
return len(value)
return cast("int", value.select(pl.len()).collect().item())
[docs]
def is_empty(value: pl.DataFrame | pl.LazyFrame) -> bool:
return row_length(value) == 0
[docs]
def column_selector(value: str) -> pl.Expr:
splits = value.split(".")
curr = pl.col(splits[0])
if len(splits) > 1:
for part in splits[1:]:
curr = curr.struct.field(part)
return curr
[docs]
def normalize_offset(offset: OffsetLike) -> str:
"""Coerce an offset to the polars duration language.
A polars-duration string (``"28d"``, ``"6h"``) is returned unchanged; a
:class:`~datetime.timedelta` is rendered in microseconds. This is the single place
that turns an :data:`~corr_vars.definitions.typing.OffsetLike` into the string
``dt.offset_by`` needs — which does not accept a timedelta.
"""
if isinstance(offset, timedelta):
return f"{offset // timedelta(microseconds=1)}us"
return offset