Getting Started with Polars.NET
Welcome to Polars.NET! This quickstart walks you through creating DataFrames, transforming data, computing window functions, and leveraging the Lazy engine with real-world execution plans.
0. Prerequisites & Native Packages
Unlike Python's wheel distribution, .NET projects require the managed package plus the native runtime package matching your OS and architecture:
# Core managed package
dotnet add package Polars.NET
# Required: Native runtime package matching your deployment environment
dotnet add package Polars.NET.runtime.linux-x64
# Or: Polars.NET.runtime.win-x64 / Polars.NET.runtime.osx-arm64# Managed packages (includes functional extensions and pipeline helpers)
dotnet add package Polars.NET
dotnet add package Polars.FSharp
# Required: Native runtime package
dotnet add package Polars.NET.runtime.linux-x64
# Or: Polars.NET.runtime.win-x64 / Polars.NET.runtime.osx-arm641. Creating a DataFrame
Polars.NET supports creating DataFrames from in-memory records, strongly-typed collections, CSV, Parquet, Arrow streams, and databases.
using Polars;
using Polars.CSharp;
using static Polars.CSharp.Polars;
public record Weather(string Date, string City, double Temperature, bool Rain);
// 1. Ingest directly from List<T> or any IEnumerable<T> (Most idiomatic .NET pattern)
var weatherList = new List<Weather>
{
new("2023-01-01", "London", 10.5, true),
new("2023-01-01", "Manchester", 9.0, true),
new("2023-01-02", "London", 12.1, false)
};
var df = DataFrame.FromRows(weatherList);
// Alternatively: Create from columnar object (Structure of Arrays)
var dfCols = DataFrame.FromColumns(new
{
Date = new[] { "2023-01-01", "2023-01-01", "2023-01-02" },
City = new[] { "London", "Manchester", "London" },
Temperature = new[] { 10.5, 9.0, 12.1 },
Rain = new[] { true, true, false }
});
// 2. Parse Date string to native Date32 type
df = df.WithColumn(Col("Date").Str.ToDate("%Y-%m-%d"));open Polars.FSharp
type Weather = { Date: string; City: string; Temperature: float; Rain: bool }
let data = [
{ Date="2023-01-01"; City="London"; Temperature=10.5; Rain=true }
{ Date="2023-01-01"; City="Manchester"; Temperature=9.0; Rain=true }
{ Date="2023-01-02"; City="London"; Temperature=12.1; Rain=false }
]
let df =
DataFrame.ofRecords data
|> DataFrame.withColumn (pl.col("Date").Str.ToDate "%Y-%m-%d")using Polars.CSharp;
Series sDate = Series.From("Date", ["2023-01-01", "2023-01-01", "2023-01-02"]);
Series sCity = Series.From("City", ["London", "Manchester", "London"]);
Series sTemp = Series.From("Temperature", [10.5, 9.0, 12.1]);
Series sRain = Series.From("Rain", [true, true, false]);
// Construct DataFrame using C# 12 [CollectionBuilder]
DataFrame df = [sDate, sCity, sTemp, sRain];| Datedate | Citystr | Temperaturef64 | Rainbool |
|---|---|---|---|
| 1/1/2023 | London | 10.5 | True |
| 1/1/2023 | Manchester | 9 | True |
| 1/2/2023 | London | 12.1 | False |
2. Filtering & Grouped Aggregations
Polars expressions (Expr) execute multi-threaded SIMD parallel calculations across columns.
var aggDf = df
.Filter(Col("Temperature") > 9.5)
.GroupBy("City")
.Agg(
Col("Temperature").Mean().Alias("Avg_Temperature"),
Col("Temperature").Count().Alias("Record_Count")
)
.Sort("Avg_Temperature", descending: true);let aggDf =
df
|> DataFrame.filter (pl.col "Temperature" .> pl.lit 9.5)
|> DataFrame.groupBy [ pl.col "City" ]
|> DataFrame.agg [
pl.col("Temperature").Mean().Alias "Avg_Temperature"
pl.col("Temperature").Count().Alias "Record_Count"
]
|> DataFrame.sortDescending [ pl.col "Avg_Temperature" ]// Strongly-typed LINQ to Polars expression
var aggDf = (from r in df.AsQueryable()
where r.Temperature > 9.5
group r by r.City into g
select new {
City = g.Key,
Avg_Temperature = g.Average(x => x.Temperature),
Record_Count = g.Count()
})
.ToDataFrame()
.Sort("Avg_Temperature", descending: true);| Citystr | Avg_Temperaturef64 | Record_Countu32 |
|---|---|---|
| London | 11.3 | 2 |
3. Window Functions & Expr AST Visualization
Window functions (.Over()) evaluate group-level metrics without reducing rows. Polars.NET provides full introspection into expression syntax trees via .ToHtml().
// Calculate temperature deviation from the city average
var diffExpr = (Col("Temperature") - Col("Temperature").Mean().Over("City"))
.Alias("Temp_Diff");
var windowDf = df.WithColumn(diffExpr);let diffExpr =
(pl.col("Temperature") - pl.col("Temperature").Mean().Over(pl.col "City"))
.Alias("Temp_Diff")
let windowDf = df |> DataFrame.withColumn diffExpr// Over() extension operator in LINQ
var windowDf = df.AsQueryable()
.Select(r => new {
r.City,
r.Temperature,
Temp_Diff = r.Temperature - r.Temperature.Over(r.City).Average()
})
.ToDataFrame();0 1 2 ┌─────────────────────────────────────────────────────────── │ │ ╭───────────╮ 0 │ │ binary: - │ │ ╰─────┬┬────╯ │ ││ │ │╰──────────────────────────────────────╮ │ │ │ │ ╭───┴────╮ ╭────────┴─────────╮ 1 │ │ window │ │ col(Temperature) │ │ ╰───┬┬───╯ ╰──────────────────╯ │ ││ │ │╰────────────────╮ │ │ │ │ ╭─────┴─────╮ ╭──┴───╮ 2 │ │ col(City) │ │ mean │ │ ╰───────────╯ ╰──┬───╯ │ │ │ │ │ │ │ ╭────────┴─────────╮ 3 │ │ col(Temperature) │ │ ╰──────────────────╯
4. The Lazy Engine & Execution Planning
LazyFrame builds a declarative query graph without reading raw data into memory. When calling .Collect(), Polars optimizes the query graph via predicate pushdown, projection pruning, and streaming chunking.
var lf = df.Lazy()
.Filter(Col("Temperature") > 10.0)
.GroupBy("City")
.Agg(Col("Temperature").Mean().Alias("Avg_Temp"));
// Inspect the optimized query plan
Console.WriteLine(lf.Explain(optimized: true));
// Execute in parallel
var result = lf.Collect();let lf =
df.Lazy()
|> LazyFrame.filter (pl.col "Temperature" .> pl.lit 10.0)
|> LazyFrame.groupBy [ pl.col "City" ]
|> LazyFrame.agg [
pl.col("Temperature").Mean().Alias "Avg_Temp"
]
printfn "%s" (lf.Explain(optimized = true))
let result = lf.Collect()Next Steps & Guides
- C# Idioms & Best Practices - Fluent chaining, Selectors, Schema dictionary, and memory safety.
- F# Idioms & Best Practices - Functional pipelines, anonymous records, and typed streaming.
- LINQ & Query Computation Expressions - 3-tier fallback strategy, LINQ to Polars, and F#
query { ... }. - Ecosystem & Native Interoperability - Zero-copy Arrow C Data Interface, ADBC, ADO.NET, ML.NET, and Tensors.
- API Reference: Explore the full list of available Expressions.