Skip to content

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:

bash
# 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
bash
# 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-arm64

1. Creating a DataFrame ​

Polars.NET supports creating DataFrames from in-memory records, strongly-typed collections, CSV, Parquet, Arrow streams, and databases.

csharp
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"));
fsharp
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")
csharp
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];
Polars DataFrame Output
Polars DataFrame: (3 rows, 4 columns)
DatedateCitystrTemperaturef64Rainbool
1/1/2023London10.5True
1/1/2023Manchester9True
1/2/2023London12.1False

2. Filtering & Grouped Aggregations ​

Polars expressions (Expr) execute multi-threaded SIMD parallel calculations across columns.

csharp
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);
fsharp
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" ]
csharp
// 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);
Aggregated DataFrame Output
Polars DataFrame: (1 rows, 3 columns)
CitystrAvg_Temperaturef64Record_Countu32
London11.32

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().

csharp
// Calculate temperature deviation from the city average
var diffExpr = (Col("Temperature") - Col("Temperature").Mean().Over("City"))
    .Alias("Temp_Diff");

var windowDf = df.WithColumn(diffExpr);
fsharp
let diffExpr = 
    (pl.col("Temperature") - pl.col("Temperature").Mean().Over(pl.col "City"))
        .Alias("Temp_Diff")

let windowDf = df |> DataFrame.withColumn diffExpr
csharp
// 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();
Polars Expr AST Tree & Formula
Polars Expr AST
[(col("Temperature")) - (col("Temperature").mean().over([col("City")]))].alias("Temp_Diff")
            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.

csharp
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();
fsharp
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()
Optimized LazyFrame Logical Plan
Polars LazyFrame UNCOLLECTED
Schema: City: str, Avg_Temp: f64
AGGREGATE[maintain_order: false] [col("Temperature").mean().alias("Avg_Temp")] BY [col("City")] FROM FILTER col("Temperature") > 10.0 FROM DF ["Date", "City", "Temperature", "Rain"]; PROJECT["City", "Temperature"] 2/4 COLUMNS
Tip: Call .Collect() to eagerly execute this query plan.

Next Steps & Guides ​

  • API Reference: Explore the full list of available Expressions.

Released under the MIT License. Powered by the Polars Rust core & Apache Arrow.