Extending the API#
Providing new functionality#
These functions allow you to register custom functionality in a dedicated namespace on the underlying Polars classes without requiring subclassing or mixins. Expr, DataFrame, LazyFrame, and Series are all supported targets.
This feature is primarily intended for use by library authors providing domain-specific capabilities which may not exist (or belong) in the core library.
Available registrations#
|
Decorator for registering custom functionality with a Polars Expr. |
Decorator for registering custom functionality with a Polars DataFrame. |
|
Decorator for registering custom functionality with a Polars LazyFrame. |
|
Decorator for registering custom functionality with a polars Series. |
Note
You cannot override existing Polars namespaces (such as .str or .dt), and attempting to do so
will raise an AttributeError.
However, you can override other custom namespaces (which will only generate a
UserWarning).
Examples#
@pl.api.register_expr_namespace("greetings")
class Greetings:
def __init__(self, expr: pl.Expr) -> None:
self._expr = expr
def hello(self) -> pl.Expr:
return (pl.lit("Hello ") + self._expr).alias("hi there")
def goodbye(self) -> pl.Expr:
return (pl.lit("SayΕnara ") + self._expr).alias("bye")
pl.DataFrame(data=["world", "world!", "world!!"]).select(
[
pl.all().greetings.hello(),
pl.all().greetings.goodbye(),
]
)
# shape: (3, 1) shape: (3, 2)
# ββββββββββββ βββββββββββββββββ¬βββββββββββββββββββ
# β column_0 β β hi there β bye β
# β --- β β --- β --- β
# β str β β str β str β
# ββββββββββββ‘ >> βββββββββββββββββͺβββββββββββββββββββ‘
# β world β β Hello world β SayΕnara world β
# β world! β β Hello world! β SayΕnara world! β
# β world!! β β Hello world!! β SayΕnara world!! β
# ββββββββββββ βββββββββββββββββ΄βββββββββββββββββββ
@pl.api.register_dataframe_namespace("split")
class SplitFrame:
def __init__(self, df: pl.DataFrame) -> None:
self._df = df
def by_alternate_rows(self) -> list[pl.DataFrame]:
df = self._df.with_row_index(name="n")
return [
df.filter((pl.col("n") % 2) == 0).drop("n"),
df.filter((pl.col("n") % 2) != 0).drop("n"),
]
pl.DataFrame(
data=["aaa", "bbb", "ccc", "ddd", "eee", "fff"],
schema=[("txt", pl.String)],
).split.by_alternate_rows()
# [βββββββ βββββββ
# β txt β β txt β
# β --- β β --- β
# β str β β str β
# βββββββ‘ βββββββ‘
# β aaa β β bbb β
# β ccc β β ddd β
# β eee β β fff β
# βββββββ, βββββββ]
@pl.api.register_lazyframe_namespace("types")
class DTypeOperations:
def __init__(self, ldf: pl.LazyFrame) -> None:
self._ldf = ldf
def upcast_integer_types(self) -> pl.LazyFrame:
return self._ldf.with_columns(
pl.col(tp).cast(pl.Int64)
for tp in (pl.Int8, pl.Int16, pl.Int32)
)
ldf = pl.DataFrame(
data={"a": [1, 2], "b": [3, 4], "c": [5.6, 6.7]},
schema=[("a", pl.Int16), ("b", pl.Int32), ("c", pl.Float32)],
).lazy()
ldf.types.upcast_integer_types()
# shape: (2, 3) shape: (2, 3)
# βββββββ¬ββββββ¬ββββββ βββββββ¬ββββββ¬ββββββ
# β a β b β c β β a β b β c β
# β --- β --- β --- β β --- β --- β --- β
# β i16 β i32 β f32 β >> β i64 β i64 β f32 β
# βββββββͺββββββͺββββββ‘ βββββββͺββββββͺββββββ‘
# β 1 β 3 β 5.6 β β 1 β 3 β 5.6 β
# β 2 β 4 β 6.7 β β 2 β 4 β 6.7 β
# βββββββ΄ββββββ΄ββββββ βββββββ΄ββββββ΄ββββββ
@pl.api.register_series_namespace("math")
class MathShortcuts:
def __init__(self, s: pl.Series) -> None:
self._s = s
def square(self) -> pl.Series:
return self._s * self._s
def cube(self) -> pl.Series:
return self._s * self._s * self._s
s = pl.Series("n", [1, 2, 3, 4, 5])
s2 = s.math.square().rename("n2")
s3 = s.math.cube().rename("n3")
# shape: (5,) shape: (5,) shape: (5,)
# Series: 'n' [i64] Series: 'n2' [i64] Series: 'n3' [i64]
# [ [ [
# 1 1 1
# 2 4 8
# 3 9 27
# 4 16 64
# 5 25 125
# ] ] ]