DataFrames and expressions
Create Polars Series and DataFrames in Python, inspect schemas, and understand expressions and LazyFrames before transforming a dataset.
Rows, columns, types, and pipelines#
A table organizes many records so the same operation can be applied to all of them.
Polars cleans and analyzes tables. A row is one record, a column is one attribute, and a cell holds one value. Define the row’s meaning before aggregating: three order lines represent three line records but may belong to one order.
| order_id | customer | quantity | unit_price_cents |
|---|---|---|---|
| 1001 | C01 | 2 | 1250 |
| 1002 | C02 | 1 | 3000 |
| 1003 | C01 | 3 | 500 |
A schema maps column names to types. Customer IDs stay as text to preserve leading zeros; prices use integer cents. null means missing data and differs from zero, an empty string, and NaN.
Polars stores values by column and runs native operations in Rust across CPU threads. File reads, large joins, extra columns, and Python callbacks can still dominate query time.
A pipeline reads, cleans, validates, transforms, and writes data. A query describes those operations; an aggregation summarizes rows.
Save a Python code card as example.py and run it. Each local program includes its input and expected output. File examples use temporary folders; use persistent paths in your own pipelines.
Official reference: Rows, columns, types, and pipelines.
Series, DataFrame, LazyFrame, and expressions#
Choose the object by whether you need one column, a table now, or a plan for later.
| Tool | Meaning | When to use it |
|---|---|---|
Series | One named, typed sequence of values. | Inspect or calculate with one column already in memory. |
DataFrame | A table of equal-length, uniquely named Series. | Explore small or already-loaded data; operations return results immediately. |
LazyFrame | A plan describing a table and its transformations. | Build file pipelines; let Polars optimize the whole query before executing it. |
Expr | A description of a computation, such as quantity × price. | Use inside select, with_columns, filter, or aggregation on either kind of frame. |
Eager execution performs each table operation now. Lazy execution records operations and runs them when you call collect() or write with a lazy sink. A sink is an output destination. pl.col("quantity") creates an expression referring to a column; it is not that column's actual values. df["quantity"] returns a materialized Series. Materialized means the values have been computed and occupy memory.
python -m venv .venv
# macOS / Linux:
source .venv/bin/activate
# Windows PowerShell instead: .venv\\Scripts\\Activate.ps1
python -m pip install "polars==1.44.2"
python -c "import polars as pl; print(pl.__version__)"The examples use Python 3.12 and Polars 1.44.2. Install companion packages where indicated. Check release notes before upgrading APIs marked unstable.
import polars as pl
quantity = pl.Series("quantity", [2, 1, 3], dtype=pl.Int64)
df = pl.DataFrame({"quantity": quantity, "price_cents": [1250, 3000, 500]})
revenue = (pl.col("quantity") * pl.col("price_cents")).alias("revenue_cents")
print("series:", quantity.to_list())
print("eager:", df.select(revenue)["revenue_cents"].to_list())
plan = df.lazy().select(revenue)
print("lazy:", plan.collect()["revenue_cents"].to_list())series: [2, 1, 3]
eager: [2500, 3000, 1500]
lazy: [2500, 3000, 1500]select returns the calculated revenue column. The lazy plan runs when collect() is called. Converting an existing DataFrame to lazy does not release its allocated data.
Official reference: Series, DataFrame, LazyFrame, and expressions.
Create and inspect tables#
Give empty and uncertain inputs a schema instead of guessing their types.
Create tables from dictionaries of columns or lists of row dictionaries. Inference means choosing types from input values; it can fail when later values differ. schema specifies all fields, while schema_overrides changes selected inferred fields. Use orient="row" for tuple rows so orientation is unambiguous. Polars has no implicit pandas-style row index; keep IDs as explicit columns.
import polars as pl
orders = pl.DataFrame(
[
{"order_id": "001", "quantity": 2, "channel": "web"},
{"order_id": "002", "quantity": 1, "channel": None},
],
schema={"order_id": pl.String, "quantity": pl.Int64, "channel": pl.String},
)
print("shape:", orders.shape)
print("schema:", dict(orders.schema))
print("head:", orders.head(1).to_dicts())
print("nulls:", orders.null_count().to_dicts())
print("quantity:", orders.get_column("quantity").to_list())
empty = pl.DataFrame(schema=orders.schema)
print("empty:", empty.shape, dict(empty.schema))
rows = pl.DataFrame(
[("C01", 10), ("C02", 20)],
schema=[("customer", pl.String), ("spend", pl.Int64)],
orient="row",
)
print("tuple input:", rows.to_dicts())shape: (2, 3)
schema: {'order_id': String, 'quantity': Int64, 'channel': String}
head: [{'order_id': '001', 'quantity': 2, 'channel': 'web'}]
nulls: [{'order_id': 0, 'quantity': 0, 'channel': 1}]
quantity: [2, 1]
empty: (0, 3) {'order_id': String, 'quantity': Int64, 'channel': String}
tuple input: [{'customer': 'C01', 'spend': 10}, {'customer': 'C02', 'spend': 20}]| Inspect | Result |
|---|---|
df.head(5), df.tail(5) | First or last five rows. |
df.glimpse() | Print column types and sample values. |
df.describe() | Exploratory statistics; do not depend on its output format as a production contract. |
df.columns, df.dtypes, df.height, df.width | Names, types, row count, column count. |
df.sample(n=2, seed=7) | A repeatable sample for this environment, rather than always looking at the first rows. |
lf.collect_schema() | Resolve a lazy schema; it may read file metadata and take time. |
Official reference: Create and inspect tables.
Help improve CodeRef
Allow Google Analytics to measure page visits and use of the guides? It uses cookies after you accept. Your choice won’t affect access to the site. Privacy notice