Expr Expressions
Expr is a lazy, composable column expression built from pl.col(name) or pl.lit(value), evaluated in DataFrame's select / with_columns / filter and GroupBy.agg.
pl.col("amount") # reference a column
pl.col("amount") * 1.1 # arithmetic yields a new expression
pl.col("amount").sum().alias("total") # aggregate and rename
:::warning Comparison returns Expr
Unlike normal Python semantics, comparison operators on Expr build a lazy boolean expression node and return Expr, NOT bool. So pl.col("a") > 5 is an expression that can be used directly in filter.
:::
General Methods
| Method | Description |
|---|---|
e.alias(name) | Rename the output column of this expression |
e.cast(dtype) | Cast the result to another dtype |
e.is_null() / e.is_not_null() | Whether each value is null / non-null |
e.fill_null(value) | Replace null with an expression or scalar |
e.is_in(values) | Whether each value is a member of values |
e.abs() | Element-wise absolute value |
e.round(ndigits=0) | Round to ndigits decimal places |
Aggregation Methods
When used in GroupBy.agg or evaluated over the entire column, these reduce a column to a single value:
| Method | Description |
|---|---|
e.sum() | Sum of non-null values |
e.mean() | Arithmetic mean |
e.min() / e.max() | Minimum / maximum value |
e.median() | Median |
e.std() / e.var() | Sample standard deviation / variance |
e.count() | Count of non-null values |
e.n_unique() | Count of distinct values |
e.first() / e.last() | First / last value |
e.skew(bias=True) | Sample skewness |
e.kurtosis(fisher=True, bias=True) | Kurtosis (fisher=True for excess kurtosis) |
e.mode() | Most frequently occurring value(s) |
e.quantile(q, interpolation="nearest") | Value at the given quantile ("nearest" only) |
Rolling Windows
Fixed-size sliding window calculations evaluated in select / with_columns / agg:
| Method | Description |
|---|---|
e.rolling_min(window_size, *, min_samples, center) | Rolling minimum |
e.rolling_max(window_size, *, min_samples, center) | Rolling maximum |
e.rolling_sum(window_size, *, min_samples, center) | Rolling sum |
e.rolling_mean(window_size, *, min_samples, center) | Rolling mean |
e.rolling_median(window_size, *, min_samples, center) | Rolling median |
e.rolling_std(window_size, *, min_samples, center, ddof=1) | Rolling std |
e.rolling_var(window_size, *, min_samples, center, ddof=1) | Rolling variance |
e.rolling_skew(window_size, *, bias, min_samples, center) | Rolling skewness |
e.rolling_kurtosis(window_size, *, fisher, bias, min_samples, center) | Rolling kurtosis |
e.rolling_quantile(q, window_size=2, *, min_samples, center) | Rolling quantile |
e.rolling_map(function, window_size, *, min_samples, center) | Apply function(Series) per window |
Rolling By (Indexed Windows)
Variable-length windows based on by (a column name or expression, assumed sorted ascending), defined by duration strings:
| Method | Description |
|---|---|
e.rolling_min_by(by, window, *, min_samples, closed) | Rolling minimum by index |
e.rolling_max_by(by, window, *, min_samples, closed) | Rolling maximum by index |
e.rolling_sum_by(by, window, *, min_samples, closed) | Rolling sum by index |
e.rolling_mean_by(by, window, *, min_samples, closed) | Rolling mean by index |
e.rolling_median_by(by, window, *, min_samples, closed) | Rolling median by index |
e.rolling_std_by(by, window, *, min_samples, closed, ddof=1) | Rolling std by index |
e.rolling_var_by(by, window, *, min_samples, closed, ddof=1) | Rolling variance by index |
Exponentially Weighted Moving (EWM)
Provide exactly one of com / span / half_life / alpha:
| Method | Description |
|---|---|
e.ewm_mean(*, com/span/half_life/alpha, adjust, min_samples, ignore_nulls) | EWM average |
e.ewm_sum(*, same params) | EWM sum |
e.ewm_var(*, same params, bias=False) | EWM variance |
e.ewm_std(*, same params, bias=False) | EWM std |
Transformations & Sorting
| Method | Description |
|---|---|
e.diff(n=1) | Difference from value n positions before |
e.cum_sum(*, reverse=False) | Cumulative sum |
e.cum_prod(*, reverse=False) | Cumulative product |
e.pct_change(n=1) | Percentage change, returns Float64 |
e.sort(*, descending=False, nulls_last=False) | Sort by value (stable) |
e.forward_fill(limit=None) | Fill nulls with previous non-null value |
e.backward_fill(limit=None) | Fill nulls with next non-null value |
e.unique(*, maintain_order=False) | Distinct values |
e.shift(n=1, *, fill_value) | Shift by n positions; vacated → fill_value |
e.interpolate(method="linear") | Linear interpolation for nulls ("linear" only) |
e.top_k(k=5) | k largest values |
e.bottom_k(k=5) | k smallest values |
e.replace_strict(old, new, *, default, return_dtype) | Strict value replacement |
Window Function over
e.over(*partition_by) -> Expr
Evaluates per partition (grouped by partition_by columns/expressions), then broadcasts each group's result back to the original rows.
# Each row's proportion of its category total
df.with_columns(
(pl.col("amount") / pl.col("amount").sum().over("category")).alias("pct_of_category")
)
Element-wise Mapping map_elements
e.map_elements(function, return_dtype=None, skip_nulls=True) -> Expr
Applies a Python callback to each element. skip_nulls=True (default) skips calling the callback on null elements. return_dtype is inferred when not given.
df.with_columns(
pl.col("score").map_elements(lambda x: "A" if x >= 90 else "B").alias("grade")
)
Conditional Expressions pl.when
pl.when(*predicates) → When → .then(value) → Then → .when(...) / .otherwise(value) / .alias(name)
# when/then/otherwise builds a conditional column
df.with_columns(
pl.when(pl.col("amount") > 100).then(pl.lit("high"))
.when(pl.col("amount") > 50).then(pl.lit("mid"))
.otherwise(pl.lit("low"))
.alias("level")
)
# Without .otherwise(), unmatched rows are null
df.with_columns(
pl.when(pl.col("amount") > 100).then(pl.lit("flagged")).alias("flag")
)
Operators
- Arithmetic:
+-*///%**, and unary-(negate) - Comparison:
==!=<<=>>=(build boolean expressions) - Logical:
&|~
String Namespace .str
Access string operations via e.str (available on Expr only, not Series):
| Method | Description |
|---|---|
.str.contains(pat) | Whether string contains pat |
.str.starts_with(pat) / .str.ends_with(pat) | Whether string starts / ends with pat |
.str.to_uppercase() / .str.to_lowercase() | Uppercase / lowercase |
.str.strip_chars(chars=None) | Strip leading/trailing whitespace, or given chars |
.str.replace(old, new) | Replace first match |
.str.replace_all(old, new) | Replace all matches |
.str.len_chars() | Number of characters |
.str.slice(offset, length=None) | Slice length characters starting at offset |
.str.to_datetime(format=None) | Parse to Datetime, optionally with explicit format |
.str.to_date(format=None) | Parse to Date, optionally with explicit format |
# Convert SQL string timestamp column to Datetime, then extract year
df.with_columns(
pl.col("created_at").str.to_datetime().dt.year().alias("year")
)
Datetime Namespace .dt
Access datetime/date operations via e.dt (available on Expr only):
| Method | Description |
|---|---|
.dt.year() / .dt.month() / .dt.day() | Extract year / month / day |
.dt.hour() / .dt.minute() / .dt.second() | Extract hour / minute / second |
.dt.weekday() | ISO weekday number |
.dt.truncate(every) | Truncate to time bucket boundary (e.g. "1mo", "1d") |
.dt.strftime(format) | Format as string using strftime-style format |
# Monthly aggregation
df.group_by(pl.col("created_at").dt.truncate("1mo").alias("month")).agg(
pl.col("amount").sum().alias("total")
)