Selecting many columns: selectors

Polars · Practical · 12 min

Wide tables need column-wise operations without writing one line per column.

One expression can expand into many. pl.col("a", "b").round(1) applies to both columns; pl.col(pl.Float64) picks every Float64 column; pl.all() picks everything; pl.col("^sales_.*$") picks by regex (the pattern must start with ^ and end with $). pl.all().exclude("id") drops some.

The selectors module, imported as import polars.selectors as cs, is more expressive and composes with set operations:

  • cs.numeric(), cs.string(), cs.temporal(), cs.boolean()
  • cs.starts_with("sales_"), cs.ends_with("_id"), cs.contains("price"), cs.matches(regex)
  • cs.by_name("a", "b"), cs.first(), cs.last()
  • combine with | (union), & (intersection), - (difference) and ~ (complement)

Expanded outputs keep their original names, so in with_columns they overwrite the inputs. To keep both, rename with the .name namespace: .name.suffix("_z"), .name.prefix(...), .name.map(func).

In Polars 2, combining a selector with pl.col(...) using &, | or ^ is an element-wise boolean operation, not a column selection. Stay within selectors, using cs.by_name(...), when you mean set logic.

cs.expand_selector(df, selector) shows which columns a selector picks, handy for debugging.

Example

import polars as pl
import polars.selectors as cs

df = pl.DataFrame({
    "id": [1, 2], "name": ["a", "b"],
    "sales_q1": [10.0, 20.0], "sales_q2": [30.0, 10.0], "rate": [0.5, 0.25],
})

print(cs.expand_selector(df, cs.numeric() - cs.by_name("id")))
# ('sales_q1', 'sales_q2', 'rate')

out = df.with_columns(
    (cs.starts_with("sales_") / 10).name.suffix("_k"),
    pl.col(pl.String).str.to_uppercase(),
)
print(out.columns)
# ['id', 'name', 'sales_q1', 'sales_q2', 'rate', 'sales_q1_k', 'sales_q2_k']
print(out.select(pl.sum_horizontal(cs.ends_with("_k"))).to_series().to_list())
# [4.0, 3.0]

Remember

  • pl.col(dtype), pl.all() and regex names expand into many columns
  • polars.selectors compose with |, &, -, ~
  • Use .name.suffix() to keep originals when transforming many columns

Watch out for

  • Forgetting ^...$ around a regex in pl.col, so it is read as a plain column name.

Check yourself

  1. What does df.with_columns(cs.numeric() * 2) do to the numeric columns?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.pola.rs/user-guide/expressions/expression-expansion/