Question presented to candidate:
"In Polars, if I write pl.scan_parquet(path).filter(...).select(...).head(10).collect(), what actually happens between building that chain and getting a DataFrame back? Which optimizations does Polars apply, and how would you check that they happened?"
What a strong answer should cover:
- A
LazyFrameholds a logical query plan, not data; methods add nodes;.collect()optimizes the plan and then executes it (scan_*functions start a lazy plan from a file) - Predicate pushdown (filters applied as early as possible, at the scan), projection pushdown (only needed columns are read), slice pushdown (only the needed rows)
- Expression and plan rewrites: expression simplification / constant folding, common subplan elimination (cache repeated subtrees, for example in self-joins), plus type coercion, join ordering and cardinality estimation listed in the Polars user guide
- How to inspect:
lf.explain()(optimized plan) vslf.explain(optimized=False); readPROJECT n/m COLUMNSandSELECTIONon the scan line - What defeats it: Python UDFs (
map_elements) the optimizer cannot see into, reading eagerly withread_csvbefore going lazy, collecting in the middle of a pipeline
Code / implementation expected: Optional β a lazy query and its explain() output, or a sketch of how a filter moves below a projection into the scan.