Migrate from Spark
A PySpark batch job that reads files from object storage, transforms them with the DataFrame API and
writes files back runs on Polars Nanook after rewriting the transformation against the Polars
LazyFrame API and submitting it with .remote(). The same job in both APIs:
# PySpark
from pyspark.sql import SparkSession
from pyspark.sql import functions as F
spark = SparkSession.builder.getOrCreate()
customer = spark.read.parquet("s3://<BUCKET>/customer/")
orders = spark.read.parquet("s3://<BUCKET>/orders/")
result = (
customer.filter(F.col("c_mktsegment") == "BUILDING")
.join(orders, customer.c_custkey == orders.o_custkey)
.groupBy("o_orderkey")
.agg(F.sum("o_totalprice").alias("revenue"))
)
result.write.mode("overwrite").parquet("s3://<BUCKET>/revenue/")
import polars as pl
import polars_nanook as pn
ctx: pn.ComputeContext | pn.ClusterContext
customer = pl.scan_parquet("s3://<BUCKET>/customer/*.parquet")
orders = pl.scan_parquet("s3://<BUCKET>/orders/*.parquet")
result = (
customer.filter(pl.col("c_mktsegment") == "BUILDING")
.join(orders, left_on="c_custkey", right_on="o_custkey")
.group_by("o_orderkey")
.agg(pl.sum("o_totalprice").alias("revenue"))
)
result.remote(ctx).sink_parquet("s3://<BUCKET>/revenue/").await_result()
What changed:
- The
SparkSessionand its cluster configuration become a compute context or a cluster context, and there is no session to keep alive. spark.read.parqueton a directory becomespl.scan_parquetwith a glob, or withhive_partitioning=Trueforkey=valuelayouts. Nothing runs until the query is submitted.write.parquetbecomes.remote(ctx).sink_parquet(...).await_result(), and.remote(ctx).show()previews rows. See Run a remote query.- Column expressions combine differently from Spark's row-based DataFrame. The Coming from Apache Spark page of the Polars user guide covers those API differences.
What does not move
- Continuous stream processing: Polars Nanook runs a bounded query and stops.
- A SQL endpoint for many concurrent users: results are written to storage for other tools to read.
- Hosted notebooks or experiment tracking: not part of Polars Nanook.
- Python UDFs and expression plugins do move, provided their packages are available on the compute. See Use Python packages and plugins.
Size the compute from the memory the job needs, measured with the profiler, not from the old cluster size (Scale a query), and run it from Airflow, Dagster, Prefect or Lambda with a service account.