Polars is a high-performance, column-oriented DataFrame library written in Rust and exposed to Python (and other languages like Node.js, R, and Rust itself). It is designed to be extremely fast and memory-efficient for data manipulation and analysis, often outperforming traditional Python data libraries like Pandas, especially on larger datasets or when leveraging multi-core CPUs.
Key characteristics and advantages of Polars include:
- Speed and Performance: Built on Rust, Polars benefits from its memory safety and speed. It is highly optimized for modern hardware, utilizing SIMD instructions and parallel processing across multiple CPU cores by default for many operations.
- Memory Efficiency: Polars uses a columnar storage format, which is very efficient for analytical queries as it only loads the necessary columns into memory. It also uses efficient data types and avoids unnecessary copies of data.
- Eager and Lazy Execution:
- Eager API: Operations are executed immediately, similar to Pandas. This is intuitive for interactive data exploration.
- Lazy API: Operations are chained and optimized before execution. Polars builds an execution plan and can optimize the order of operations, push down predicates (filters), and parallelize tasks, leading to significant performance gains and lower memory usage, especially on complex pipelines or large datasets. The `collect()` method triggers the execution of the lazy plan.
- Expression-based API: Polars uses an expressive and powerful 'expressions' system for selecting, transforming, and aggregating data. This allows for complex operations to be specified concisely and provides Polars with the necessary information to optimize the execution plan.
- Strict Typing: Unlike Pandas, Polars is more type-strict. This can help prevent common data type errors and makes the behavior of operations more predictable.
- Out-of-Core Capabilities: While not its primary mode, Polars can handle datasets larger than available RAM when using its lazy API and specific I/O operations (like reading from Parquet), by optimizing which chunks of data are processed at a time.
- Parallel Processing: Many Polars operations are parallelized automatically, leveraging all available CPU cores without explicit user intervention, which is a major contributor to its speed.
Polars is an excellent choice for data engineers and data scientists dealing with medium to large-scale datasets, complex data pipelines, or when performance and memory footprint are critical considerations. Its design philosophy and Rust backend make it a strong contender for the next generation of data processing tools.
Example Code
import polars as pl
import datetime
--- 1. Creating a Polars DataFrame (Eager API) ---
data = {
"id": [1, 2, 3, 4, 5],
"name": ["Alice", "Bob", "Charlie", "David", "Eve"],
"value": [10.5, 20.1, 15.7, 25.0, 12.3],
"category": ["A", "B", "A", "C", "B"],
"date": [
datetime.date(2023, 1, 1),
datetime.date(2023, 1, 2),
datetime.date(2023, 1, 1),
datetime.date(2023, 1, 3),
datetime.date(2023, 1, 2)
]
}
df_eager = pl.DataFrame(data)
print("--- Eager DataFrame ---")
print(df_eager)
print("-" - 30)
--- 2. Common Eager Operations ---
print("--- Eager Operations ---")
Select columns
selected_df = df_eager.select("name", "value")
print("Selected columns (name, value):")
print(selected_df)
Filter rows
filtered_df = df_eager.filter(pl.col("value") > 15)
print("\nFiltered by value > 15:")
print(filtered_df)
Group by and aggregate
grouped_df = df_eager.group_by("category").agg(
pl.col("value").mean().alias("avg_value"),
pl.col("id").count().alias("count")
)
print("\nGrouped by category, aggregated mean value and count:")
print(grouped_df)
print("-" - 30)
--- 3. Lazy API Demonstration ---
print("--- Lazy API ---")
Start a lazy frame from a DataFrame or a file (e.g., Parquet, CSV)
For demonstration, we'll convert our eager df to lazy
lf = df_eager.lazy()
Chain multiple lazy operations
1. Filter rows where 'value' is greater than 10
2. Select 'name', 'category', and 'value'
3. Create a new column 'value_x2'
4. Filter again where 'category' is 'A' or 'B'
5. Group by 'category' and calculate sum of 'value_x2'
lazy_pipeline = lf.filter(pl.col("value") > 10) \
.select("name", "category", "value") \
.with_columns(
(pl.col("value") - 2).alias("value_x2")
) \
.filter(pl.col("category").is_in(["A", "B"])) \
.group_by("category").agg(
pl.col("value_x2").sum().alias("sum_doubled_value")
)
print("Lazy pipeline defined, not yet executed. Query plan:")
print(lazy_pipeline.explain()) See the optimized execution plan
Execute the lazy pipeline and collect the result
result_df_lazy = lazy_pipeline.collect()
print("\nResult of lazy pipeline after collect():")
print(result_df_lazy)
print("-" - 30)
--- 4. Reading from CSV (Lazy) ---
To run this part, create a file named 'dummy_data.csv' in the same directory
with content like:
id,name,value,category,date
1,Alice,10.5,A,2023-01-01
2,Bob,20.1,B,2023-01-02
3,Charlie,15.7,A,2023-01-01
4,David,25.0,C,2023-01-03
5,Eve,12.3,B,2023-01-02
Uncomment the following block to test CSV reading
try:
lf_csv = pl.read_csv("dummy_data.csv").lazy()
result_csv = lf_csv.filter(pl.col("value") > 15).collect()
print("\nResult from reading dummy_data.csv lazily and filtering:")
print(result_csv)
except Exception as e:
print(f"\nCould not read dummy_data.csv (expected if file not created): {e}")








Polars