2025, Dec 19 15:00
Polars list.eval: pl.element vs pl.all explained, with pl.col('') and pl.col('*') behaving the same
Learn why pl.element, pl.all, pl.col(''), and pl.col('*') behave identically inside Polars list.eval. Understand practical use, avoid overthinking selectors.
Working with Lists in Polars often involves list.eval, and that’s where a subtle naming distinction can be confusing. The user guide says you can refer to an individual element with pl.element and to all elements with pl.all. In practice, both may seem interchangeable inside list.eval, which raises a fair question: what is the actual difference and when does it matter?
What the docs say
The function eval gives us access to the list elements and pl.element refers to each individual element, but we can also use pl.all() to refer to all of the elements of the list.
Problem demo
Consider a list column and the same set of expressions evaluated once with pl.element and once with pl.all. Both paths yield identical results.
import polars as pl
frame = pl.DataFrame({
"arr": [[1], [3, 2], [6, 4, 5]]
})
print(frame)
out_elem = frame.with_columns(
pl.col("arr").list.eval(pl.element()**2).alias("square"),
pl.col("arr").list.eval(pl.element().rank()).alias("rank"),
pl.col("arr").list.eval(pl.element().count()).alias("count")
)
out_all = frame.with_columns(
pl.col("arr").list.eval(pl.all()**2).alias("square"),
pl.col("arr").list.eval(pl.all().rank()).alias("rank"),
pl.col("arr").list.eval(pl.all().count()).alias("count")
)
print(out_elem.equals(out_all))
The equality check returns True, which suggests there’s no practical difference in this context.
What’s actually going on
pl.all() without arguments refers to all columns available in the current context. Inside list.eval the only thing available is the list itself as a single column of elements. Because there is no other column in scope, pl.all() inside list.eval effectively points to the same data as pl.element. In other words, there is no special meaning for pl.all() within list.eval; it behaves the same simply because the context reduces to the list’s elements.
This behavior also explains why two other selectors map to the same result inside list.eval. You can target the list by its internal name, which is an empty string, using pl.col(''), and you can target “everything” using pl.col('*'). Both resolve to the same data inside list.eval because there’s only one selectable column in that micro-context.
Solution and a concise check
The snippet below shows the equivalence among pl.element, pl.all, pl.col(''), and pl.col('*') in this setting.
import polars as pl
from polars.testing import assert_frame_equal
tbl = pl.DataFrame({"seq": [[1, 2, 3]]})
def build_expr(expr):
return pl.col("seq").list.eval(expr.mul(2).add(1))
baseline = tbl.select(build_expr(pl.element()))
for expr in [pl.all(), pl.col(""), pl.col("*")]:
assert_frame_equal(
baseline,
tbl.select(build_expr(expr))
)
Why this is worth knowing
When expressions look different but resolve to the same outcome, it’s easy to overthink the choice and assume hidden semantics. Understanding that pl.all() refers to all columns in scope, and that list.eval narrows that scope down to the list’s elements, removes ambiguity. It also clarifies why alternative selectors like pl.col('') and pl.col('*') behave identically in this precise scenario.
Takeaways
Inside list.eval, pl.element and pl.all refer to the same data because the evaluation context exposes only the list’s elements. If you need a selector there, you can use any of pl.element, pl.all, pl.col(''), or pl.col('*') and expect the same behavior. Knowing this helps keep expressions clear and avoids spending time searching for non-existent differences.