Help us shape the future of Go! Take the 2026 Go Developer Survey

examples

package
v0.6.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2026 License: MIT Imports: 0 Imported by: 0

Documentation

Overview

Package examples is the documentation site's code. Each Example function in it is a code block on a page, and `go test` holds it to its output, so a page cannot show code that does not run or an answer it does not give (context_files/docs-site-scope.md §2).

Example (AggByExpression)
df, err := sales().
	GroupBy(ursus.Col("qty").Ge(8).Alias("bulk")).
	Agg(ursus.Len().Alias("orders"), ursus.Col("qty").Sum().Alias("qty")).
	Sort(ursus.Asc(ursus.Col("bulk"))).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (2, 3)
┌───────┬────────┬────────┐
│ bulk  │ orders │ qty    │
│ Bool  │ Uint64 │ Int128 │
├───────┼────────┼────────┤
│ false │ 5      │ 22     │
│ true  │ 3      │ 31     │
└───────┴────────┴────────┘
Example (AggConditional)
df, err := sales().
	GroupBy(ursus.Col("region")).
	Agg(
		ursus.Col("qty").Filter(ursus.Col("product").Eq("apple")).Sum().
			Alias("apple_qty"),
		ursus.Col("qty").Ge(8).Sum().Alias("bulk_orders"),
		ursus.Col("product").MaxBy(ursus.Col("qty")).Alias("biggest"),
	).
	Sort(ursus.Asc(ursus.Col("region"))).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 4)
┌─────────┬───────────┬─────────────┬─────────┐
│ region  │ apple_qty │ bulk_orders │ biggest │
│ String  │ Int128    │ Int128      │ String  │
├─────────┼───────────┼─────────────┼─────────┤
│ "east"  │ 12        │ 1           │ "apple" │
│ "north" │ 10        │ 1           │ "apple" │
│ "south" │ 4         │ 1           │ "fig"   │
└─────────┴───────────┴─────────────┴─────────┘
Example (AggCounting)
df, err := sales().
	GroupBy(ursus.Col("product")).
	Agg(
		ursus.Len().Alias("orders"),
		ursus.Col("region").NUnique().Alias("regions"),
		ursus.Col("region").First().Alias("first"),
		ursus.Col("region").Last().Alias("last"),
	).
	Sort(ursus.Asc(ursus.Col("product"))).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 5)
┌─────────┬────────┬─────────┬─────────┬─────────┐
│ product │ orders │ regions │ first   │ last    │
│ String  │ Uint64 │ Uint64  │ String  │ String  │
├─────────┼────────┼─────────┼─────────┼─────────┤
│ "apple" │ 3      │ 3       │ "north" │ "east"  │
│ "fig"   │ 2      │ 2       │ "south" │ "north" │
│ "pear"  │ 3      │ 3       │ "north" │ "south" │
└─────────┴────────┴─────────┴─────────┴─────────┘
Example (AggDescribe)
df, err := sales().
	Select(ursus.Col("qty"), ursus.Col("price")).
	Describe(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (9, 3)
┌──────────────┬───────────────────┬────────────────────┐
│ statistic    │ qty               │ price              │
│ String       │ Float64           │ Float64            │
├──────────────┼───────────────────┼────────────────────┤
│ "count"      │ 8                 │ 8                  │
│ "null_count" │ 0                 │ 0                  │
│ "mean"       │ 6.625             │ 2.15625            │
│ "std"        │ 3.543101950067402 │ 0.6258922202972101 │
│ "min"        │ 2                 │ 1.5                │
│ "25%"        │ 4                 │ 1.5                │
│ "50%"        │ 7                 │ 2.25               │
│ "75%"        │ 9                 │ 2.25               │
│ "max"        │ 12                │ 3                  │
└──────────────┴───────────────────┴────────────────────┘
Example (AggFirstNonNull)
temps, read := []float64{0, 21.5, 22.0, 0}, []bool{false, true, true, false}
readings := ursus.Frame(
	ursus.Values("sensor", []string{"a", "a", "a", "b"}),
	ursus.ValuesNullable("temp", temps, read),
)
g := readings.GroupBy(ursus.Col("sensor")).MaintainOrder()
show(g.Agg(
	ursus.Col("temp").First().Alias("first"),
	ursus.Col("temp").FirstNonNull().Alias("first_seen"),
	ursus.Col("temp").LastNonNull().Alias("last_seen"),
))
show(g.All())
Output:
shape: (2, 4)
┌────────┬─────────┬────────────┬───────────┐
│ sensor │ first   │ first_seen │ last_seen │
│ String │ Float64 │ Float64    │ Float64   │
├────────┼─────────┼────────────┼───────────┤
│ "a"    │ null    │ 21.5       │ 22        │
│ "b"    │ null    │ null       │ null      │
└────────┴─────────┴────────────┴───────────┘

shape: (2, 2)
┌────────┬──────────────────┐
│ sensor │ temp             │
│ String │ List(Float64)    │
├────────┼──────────────────┤
│ "a"    │ [null, 21.5, 22] │
│ "b"    │ [null]           │
└────────┴──────────────────┘
Example (AggFrame)
lines := sales().Select(ursus.Col("region"), ursus.Col("qty"), ursus.Col("price"))
show(lines.Mean())
show(lines.Max())
Output:
shape: (1, 3)
┌────────┬─────────┬─────────┐
│ region │ qty     │ price   │
│ String │ Float64 │ Float64 │
├────────┼─────────┼─────────┤
│ null   │ 6.625   │ 2.15625 │
└────────┴─────────┴─────────┘

shape: (1, 3)
┌─────────┬───────┬─────────┐
│ region  │ qty   │ price   │
│ String  │ Int64 │ Float64 │
├─────────┼───────┼─────────┤
│ "south" │ 12    │ 3       │
└─────────┴───────┴─────────┘
Example (AggHaving)
show(sales().
	GroupBy(ursus.Col("region")).
	MaintainOrder().
	Having(ursus.Len().Gt(2)).
	Agg(ursus.Col("qty").Sum().Alias("qty")))
Output:
shape: (2, 2)
┌─────────┬────────┐
│ region  │ qty    │
│ String  │ Int128 │
├─────────┼────────┤
│ "north" │ 20     │
│ "south" │ 19     │
└─────────┴────────┘
Example (AggHeadTail)
show(sales().
	Select(ursus.Col("region"), ursus.Col("product"), ursus.Col("qty")).
	GroupBy(ursus.Col("region")).
	Tail(1))
Output:
shape: (3, 3)
┌─────────┬─────────┬───────┐
│ region  │ product │ qty   │
│ String  │ String  │ Int64 │
├─────────┼─────────┼───────┤
│ "north" │ "fig"   │ 3     │
│ "south" │ "pear"  │ 6     │
│ "east"  │ "apple" │ 12    │
└─────────┴─────────┴───────┘
Example (AggLists)
df, err := sales().
	GroupBy(ursus.Col("region")).
	Agg(
		ursus.Col("order_id").Implode().Alias("orders"),
		ursus.Col("product").Unique().Sort().Alias("products"),
	).
	Sort(ursus.Asc(ursus.Col("region"))).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 3)
┌─────────┬─────────────┬──────────────────────────┐
│ region  │ orders      │ products                 │
│ String  │ List(Int64) │ List(String)             │
├─────────┼─────────────┼──────────────────────────┤
│ "east"  │ [4, 6]      │ ["apple", "pear"]        │
│ "north" │ [1, 3, 7]   │ ["apple", "fig", "pear"] │
│ "south" │ [2, 5, 8]   │ ["apple", "fig", "pear"] │
└─────────┴─────────────┴──────────────────────────┘
Example (AggMaintainOrder)
df, err := sales().
	GroupBy(ursus.Col("product")).
	MaintainOrder().
	Agg(ursus.Col("qty").Sum().Alias("qty")).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 2)
┌─────────┬────────┐
│ product │ qty    │
│ String  │ Int128 │
├─────────┼────────┤
│ "apple" │ 26     │
│ "pear"  │ 15     │
│ "fig"   │ 12     │
└─────────┴────────┘
Example (AggSeveralKeys)
df, err := sales().
	GroupBy(ursus.Col("region"), ursus.Col("product")).
	Agg(ursus.Col("qty").Sum().Alias("qty")).
	Sort(ursus.Asc(ursus.Col("region")), ursus.Asc(ursus.Col("product"))).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (8, 3)
┌─────────┬─────────┬────────┐
│ region  │ product │ qty    │
│ String  │ String  │ Int128 │
├─────────┼─────────┼────────┤
│ "east"  │ "apple" │ 12     │
│ "east"  │ "pear"  │ 2      │
│ "north" │ "apple" │ 10     │
│ "north" │ "fig"   │ 3      │
│ "north" │ "pear"  │ 7      │
│ "south" │ "apple" │ 4      │
│ "south" │ "fig"   │ 9      │
│ "south" │ "pear"  │ 6      │
└─────────┴─────────┴────────┘
Example (AggShortcut)
df, err := sales().
	Select(ursus.Col("region"), ursus.Col("qty"), ursus.Col("price")).
	GroupBy(ursus.Col("region")).
	Max().
	Sort(ursus.Asc(ursus.Col("region"))).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 3)
┌─────────┬───────┬─────────┐
│ region  │ qty   │ price   │
│ String  │ Int64 │ Float64 │
├─────────┼───────┼─────────┤
│ "east"  │ 12    │ 2.25    │
│ "north" │ 10    │ 3       │
│ "south" │ 9     │ 3       │
└─────────┴───────┴─────────┘
Example (AggStatistics)
df, err := sales().
	GroupBy(ursus.Col("region")).
	Agg(
		ursus.Col("qty").Sum().Alias("sum"),
		ursus.Col("qty").Mean().Alias("mean"),
		ursus.Col("qty").Median().Alias("median"),
		ursus.Col("qty").Min().Alias("min"),
		ursus.Col("qty").Max().Alias("max"),
		ursus.Col("qty").Std(1).Round(2).Alias("std"),
	).
	Sort(ursus.Asc(ursus.Col("region"))).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 7)
┌─────────┬────────┬───────────────────┬─────────┬───────┬───────┬─────────┐
│ region  │ sum    │ mean              │ median  │ min   │ max   │ std     │
│ String  │ Int128 │ Float64           │ Float64 │ Int64 │ Int64 │ Float64 │
├─────────┼────────┼───────────────────┼─────────┼───────┼───────┼─────────┤
│ "east"  │ 14     │ 7                 │ 7       │ 2     │ 12    │ 7.07    │
│ "north" │ 20     │ 6.666666666666667 │ 7       │ 3     │ 10    │ 3.51    │
│ "south" │ 19     │ 6.333333333333333 │ 6       │ 4     │ 9     │ 2.52    │
└─────────┴────────┴───────────────────┴─────────┴───────┴───────┴─────────┘
Example (AggValueCounts)
df, err := sales().ValueCounts(ursus.Col("product")).Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 2)
┌─────────┬────────┐
│ product │ count  │
│ String  │ Uint64 │
├─────────┼────────┤
│ "apple" │ 3      │
│ "pear"  │ 3      │
│ "fig"   │ 2      │
└─────────┴────────┘
Example (AggWholeFrame)
df, err := sales().
	Select(
		ursus.Len().Alias("orders"),
		ursus.Col("qty").Sum().Alias("qty"),
		ursus.Col("qty").Mul(ursus.Col("price")).Sum().Alias("revenue"),
		ursus.Col("date").Max().Alias("last_day"),
	).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (1, 4)
┌────────┬────────┬─────────┬────────────┐
│ orders │ qty    │ revenue │ last_day   │
│ Uint64 │ Int128 │ Float64 │ Date       │
├────────┼────────┼─────────┼────────────┤
│ 8      │ 53     │ 108.75  │ 2026-01-07 │
└────────┴────────┴─────────┴────────────┘
Example (Arrays)
points := ursus.FromRows([]point{
	{"a", [3]float64{1, 2, 2}},
	{"b", [3]float64{0, 3, 4}},
})
show(points.WithColumns(
	ursus.Col("XYZ").Arr().ToList().List().Sum().Alias("sum"),
	ursus.Col("XYZ").Arr().ToList().List().Head(2).List().ToArray(2).Alias("xy"),
))
Output:
shape: (2, 4)
┌────────┬───────────────────┬─────────┬───────────────────┐
│ Name   │ XYZ               │ sum     │ xy                │
│ String │ Array(Float64, 3) │ Float64 │ Array(Float64, 2) │
├────────┼───────────────────┼─────────┼───────────────────┤
│ "a"    │ [1, 2, 2]         │ 5       │ [1, 2]            │
│ "b"    │ [0, 3, 4]         │ 7       │ [0, 3]            │
└────────┴───────────────────┴─────────┴───────────────────┘
Example (Eager)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// sales is the CSV the concept pages read, its date column a Date.
func sales() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	ctx := context.Background()
	df, err := sales().Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	big, err := df.Filter(ctx, ursus.Col("qty").Ge(int64(9)))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(big.Shape())
}
Output:
3 6
Example (ExprContexts)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// sales is the CSV the concept pages read, its date column a Date.
func sales() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	ctx := context.Background()
	revenue := ursus.Col("qty").Mul(ursus.Col("price"))

	perRow, err := sales().
		Select(ursus.Col("order_id"), revenue.Alias("revenue")).
		Head(3).
		Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	total, err := sales().Select(revenue.Sum().Alias("revenue")).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	byRegion, err := sales().GroupBy(ursus.Col("region")).
		Agg(revenue.Sum().Alias("revenue")).
		Sort(ursus.Asc(ursus.Col("region"))).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(perRow)
	fmt.Println(total)
	fmt.Println(byRegion)
}
Output:
shape: (3, 2)
┌──────────┬─────────┐
│ order_id │ revenue │
│ Int64    │ Float64 │
├──────────┼─────────┤
│ 1        │ 15      │
│ 2        │ 6       │
│ 3        │ 15.75   │
└──────────┴─────────┘

shape: (1, 1)
┌─────────┐
│ revenue │
│ Float64 │
├─────────┤
│ 108.75  │
└─────────┘

shape: (3, 2)
┌─────────┬─────────┐
│ region  │ revenue │
│ String  │ Float64 │
├─────────┼─────────┤
│ "east"  │ 22.5    │
│ "north" │ 39.75   │
│ "south" │ 46.5    │
└─────────┴─────────┘
Example (ExprHorizontal)
scores := ursus.Frame(
	ursus.Values("name", []string{"ada", "ben"}),
	ursus.Values("maths", []int64{90, 60}),
	ursus.Values("art", []int64{70, 85}),
	ursus.Values("music", []int64{80, 75}),
)
show(scores.Select(
	ursus.Col("name"),
	ursus.SumHorizontal(ursus.ColDType(ursus.Int64)).Alias("total"),
	ursus.MaxHorizontal(ursus.ColDType(ursus.Int64)).Alias("best"),
))
Output:
shape: (2, 3)
┌────────┬────────┬───────┐
│ name   │ total  │ best  │
│ String │ Int128 │ Int64 │
├────────┼────────┼───────┤
│ "ada"  │ 240    │ 90    │
│ "ben"  │ 220    │ 85    │
└────────┴────────┴───────┘
Example (ExprMany)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// sales is the CSV the concept pages read, its date column a Date.
func sales() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	df, err := sales().
		Select(
			ursus.Col("product"),
			ursus.Col("qty", "price").Mul(int64(2)).Suffix("_x2"),
		).
		Head(3).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (3, 3)
┌─────────┬────────┬──────────┐
│ product │ qty_x2 │ price_x2 │
│ String  │ Int64  │ Float64  │
├─────────┼────────┼──────────┤
│ "apple" │ 20     │ 3        │
│ "apple" │ 8      │ 3        │
│ "pear"  │ 14     │ 4.5      │
└─────────┴────────┴──────────┘
Example (ExprNamespaces)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// sales is the CSV the concept pages read, its date column a Date.
func sales() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	df, err := sales().
		Select(
			ursus.Col("product").Str().ToUpper().Alias("product"),
			ursus.Col("date").Dt().Weekday().Alias("weekday"),
			ursus.Col("product").Str().Contains("p", true).Alias("has_p"),
		).
		Head(3).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (3, 3)
┌─────────┬─────────┬───────┐
│ product │ weekday │ has_p │
│ String  │ Int32   │ Bool  │
├─────────┼─────────┼───────┤
│ "APPLE" │ 6       │ true  │
│ "APPLE" │ 6       │ true  │
│ "PEAR"  │ 7       │ true  │
└─────────┴─────────┴───────┘
Example (ExprOver)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// sales is the CSV the concept pages read, its date column a Date.
func sales() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	df, err := sales().
		Select(
			ursus.Col("order_id"),
			ursus.Col("region"),
			ursus.Col("qty"),
			ursus.Col("qty").Sum().Over(ursus.Col("region")).Alias("region_qty"),
			ursus.Col("qty").Rank(ursus.RankDense, true).
				Over(ursus.Col("region")).Alias("rank"),
		).
		Sort(ursus.Asc(ursus.Col("region")), ursus.Asc(ursus.Col("rank"))).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (8, 5)
┌──────────┬─────────┬───────┬────────────┬────────┐
│ order_id │ region  │ qty   │ region_qty │ rank   │
│ Int64    │ String  │ Int64 │ Int128     │ Uint32 │
├──────────┼─────────┼───────┼────────────┼────────┤
│ 6        │ "east"  │ 12    │ 14         │ 1      │
│ 4        │ "east"  │ 2     │ 14         │ 2      │
│ 1        │ "north" │ 10    │ 20         │ 1      │
│ 3        │ "north" │ 7     │ 20         │ 2      │
│ 7        │ "north" │ 3     │ 20         │ 3      │
│ 5        │ "south" │ 9     │ 19         │ 1      │
│ 8        │ "south" │ 6     │ 19         │ 2      │
│ 2        │ "south" │ 4     │ 19         │ 3      │
└──────────┴─────────┴───────┴────────────┴────────┘
Example (FilterAggregate)
df, err := sales().
	Filter(ursus.Col("qty").Gt(ursus.Col("qty").Mean())).
	Select(ursus.Col("order_id"), ursus.Col("qty")).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (4, 2)
┌──────────┬───────┐
│ order_id │ qty   │
│ Int64    │ Int64 │
├──────────┼───────┤
│ 1        │ 10    │
│ 3        │ 7     │
│ 5        │ 9     │
│ 6        │ 12    │
└──────────┴───────┘
Example (FilterNulls)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	ctx := context.Background()
	scores := ursus.Frame(
		ursus.Values("name", []string{"ana", "ben", "cai", "dee"}),
		ursus.ValuesNullable("score",
			[]int64{90, 40, 0, 75},
			[]bool{true, true, false, true}),
	)
	kept, err := scores.Filter(ursus.Col("score").Ge(50)).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	removed, err := scores.Remove(ursus.Col("score").Ge(50)).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	missing, err := scores.Filter(ursus.Col("score").IsNull()).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(kept)
	fmt.Println(removed)
	fmt.Println(missing)
}
Output:
shape: (2, 2)
┌────────┬───────┐
│ name   │ score │
│ String │ Int64 │
├────────┼───────┤
│ "ana"  │ 90    │
│ "dee"  │ 75    │
└────────┴───────┘

shape: (1, 2)
┌────────┬───────┐
│ name   │ score │
│ String │ Int64 │
├────────┼───────┤
│ "ben"  │ 40    │
└────────┴───────┘

shape: (1, 2)
┌────────┬───────┐
│ name   │ score │
│ String │ Int64 │
├────────┼───────┤
│ "cai"  │ null  │
└────────┴───────┘
Example (FilterOr)
df, err := sales().
	Filter(ursus.Col("region").Eq("east").Or(ursus.Col("price").Ge(3.0))).
	Select(ursus.Col("order_id"), ursus.Col("region"), ursus.Col("price")).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (4, 3)
┌──────────┬─────────┬─────────┐
│ order_id │ region  │ price   │
│ Int64    │ String  │ Float64 │
├──────────┼─────────┼─────────┤
│ 4        │ "east"  │ 2.25    │
│ 5        │ "south" │ 3       │
│ 6        │ "east"  │ 1.5     │
│ 7        │ "north" │ 3       │
└──────────┴─────────┴─────────┘
Example (FilterPredicates)
df, err := sales().
	Filter(
		ursus.Col("product").IsIn("apple", "fig"),
		ursus.Col("qty").IsBetween(4, 10),
	).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 6)
┌──────────┬────────────┬─────────┬─────────┬───────┬─────────┐
│ order_id │ date       │ region  │ product │ qty   │ price   │
│ Int64    │ Date       │ String  │ String  │ Int64 │ Float64 │
├──────────┼────────────┼─────────┼─────────┼───────┼─────────┤
│ 1        │ 2026-01-03 │ "north" │ "apple" │ 10    │ 1.5     │
│ 2        │ 2026-01-03 │ "south" │ "apple" │ 4     │ 1.5     │
│ 5        │ 2026-01-05 │ "south" │ "fig"   │ 9     │ 3       │
└──────────┴────────────┴─────────┴─────────┴───────┴─────────┘
Example (GoBatches)
type sale struct {
	ID  int64 `ursus:"order_id"`
	Qty int64
}
ctx := context.Background()
total := int64(0)
for batch, err := range sales().CollectBatches(ctx, ursus.WithBatchSize(3)) {
	if err != nil {
		log.Fatal(err)
	}
	rows, err := batch.Rows[sale]()
	if err != nil {
		log.Fatal(err)
	}
	for _, r := range rows {
		total += r.Qty
	}
	fmt.Println("batch of", len(rows), "- running total", total)
}
Output:
batch of 3 - running total 21
batch of 3 - running total 44
batch of 2 - running total 53
Example (GoColumn)
df, err := sales().Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
qty, err := df.Column[int64]("qty")
if err != nil {
	log.Fatal(err)
}
fmt.Println(qty.Name(), qty.Len(), qty.NullCount())
fmt.Println(qty.Values())

total := qty.Fold(int64(0), func(acc, v int64) int64 { return acc + v })
fmt.Println("total:", total)

kinds := qty.Map[string](func(v int64) string {
	if v >= 8 {
		return "bulk"
	}
	return "single"
})
fmt.Println(kinds)
Output:
qty 8 0
[10 4 7 2 9 12 3 6]
total: 53
[bulk single single single bulk bulk single single]
Example (GoFromRows)
type order struct {
	ID     int64     `ursus:"id"`
	Placed time.Time `ursus:"placed"`
	Total  float64   `ursus:"total"`
	Note   *string   `ursus:"note"` // nil: no note
}
note := "gift wrap"
show(ursus.FromRows([]order{
	{1, time.Date(2026, 1, 3, 9, 0, 0, 0, time.UTC), 15, nil},
	{2, time.Date(2026, 1, 4, 14, 30, 0, 0, time.UTC), 27, &note},
}))
Output:
shape: (2, 4)
┌───────┬────────────────────────────────┬─────────┬─────────────┐
│ id    │ placed                         │ total   │ note        │
│ Int64 │ Datetime(ns, UTC)              │ Float64 │ String      │
├───────┼────────────────────────────────┼─────────┼─────────────┤
│ 1     │ 2026-01-03T09:00:00.000000000Z │ 15      │ null        │
│ 2     │ 2026-01-04T14:30:00.000000000Z │ 27      │ "gift wrap" │
└───────┴────────────────────────────────┴─────────┴─────────────┘
Example (GoIn)
type order struct {
	ID     int64
	Placed time.Time
	Total  float64
	Note   *string // nil: no note
}
note := "gift wrap"
in := []order{
	{1, time.Date(2026, 1, 3, 9, 0, 0, 0, time.UTC), 15, nil},
	{2, time.Date(2026, 1, 4, 14, 30, 0, 0, time.UTC), 27, &note},
}

ids, totals := make([]int64, len(in)), make([]float64, len(in))
placed := make([]time.Time, len(in))
notes, hasNote := make([]string, len(in)), make([]bool, len(in))
for i, o := range in {
	ids[i], placed[i], totals[i] = o.ID, o.Placed, o.Total
	if o.Note != nil {
		notes[i], hasNote[i] = *o.Note, true
	}
}
show(ursus.Frame(
	ursus.Values("id", ids),
	ursus.Values("placed", placed),
	ursus.Values("total", totals),
	ursus.ValuesNullable("note", notes, hasNote),
))
Output:
shape: (2, 4)
┌───────┬────────────────────────────────┬─────────┬─────────────┐
│ id    │ placed                         │ total   │ note        │
│ Int64 │ Datetime(ns, UTC)              │ Float64 │ String      │
├───────┼────────────────────────────────┼─────────┼─────────────┤
│ 1     │ 2026-01-03T09:00:00.000000000Z │ 15      │ null        │
│ 2     │ 2026-01-04T14:30:00.000000000Z │ 27      │ "gift wrap" │
└───────┴────────────────────────────────┴─────────┴─────────────┘
Example (GoNulls)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.Frame(
		ursus.ValuesNullable("score", []int64{90, 0, 75}, []bool{true, false, true}),
	).Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	score, err := df.Column[int64]("score")
	if err != nil {
		log.Fatal(err)
	}
	for v, present := range score.All() {
		fmt.Println(v, present)
	}
	v, ok := score.Get(1)
	fmt.Println("row 1:", v, ok)
}
Output:
90 true
0 false
75 true
row 1: 0 false
Example (GoRows)
type sale struct {
	ID      int64     `ursus:"order_id"`
	Day     time.Time `ursus:"date"`
	Product string
	Qty     int64
	Revenue float64 `ursus:"-"` // filled by the program, not read
}
df, err := sales().
	Filter(ursus.Col("region").Eq("east")).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
rows, err := df.Rows[sale]()
if err != nil {
	log.Fatal(err)
}
for _, r := range rows {
	fmt.Println(r.ID, r.Day.Format("Jan 2"), r.Product, r.Qty)
}
Output:
4 Jan 5 pear 2
6 Jan 6 apple 12
Example (GoRowsNulls)
type reading struct {
	Hour int64
	Temp *float64 // nil where the reading is null
}
df, err := readings().Head(3).Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
rows, err := df.Rows[reading]()
if err != nil {
	log.Fatal(err)
}
for _, r := range rows {
	if r.Temp == nil {
		fmt.Println(r.Hour, "no reading")
		continue
	}
	fmt.Println(r.Hour, *r.Temp)
}
Output:
0 10
1 no reading
2 no reading
Example (Home)

The home page's query: a CSV scanned, filtered, grouped and sorted.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.ScanCSV("testdata/sales.csv").
		Filter(ursus.Col("qty").Gt(int64(3))).
		GroupBy(ursus.Col("region")).
		Agg(
			ursus.Col("qty").Mul(ursus.Col("price")).Sum().Alias("revenue"),
			ursus.Len().Alias("orders"),
		).
		Sort(ursus.Desc(ursus.Col("revenue"))).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (3, 3)
┌─────────┬─────────┬────────┐
│ region  │ revenue │ orders │
│ String  │ Float64 │ Uint64 │
├─────────┼─────────┼────────┤
│ "south" │ 46.5    │ 3      │
│ "north" │ 30.75   │ 2      │
│ "east"  │ 18      │ 1      │
└─────────┴─────────┴────────┘
Example (Install)

The installation page's first program, shown whole.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.Frame(
		ursus.Values("name", []string{"ursus", "polars", "pandas"}),
		ursus.Values("language", []string{"Go", "Rust", "Python"}),
	).Filter(ursus.Col("language").Eq("Go")).Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (1, 2)
┌─────────┬──────────┐
│ name    │ language │
│ String  │ String   │
├─────────┼──────────┤
│ "ursus" │ "Go"     │
└─────────┴──────────┘
Example (IoArrow)
ctx := context.Background()
df, err := sales().
	Select(ursus.Col("order_id"), ursus.Col("qty")).
	Head(3).
	Collect(ctx)
if err != nil {
	log.Fatal(err)
}
rec, err := df.Record() // the frame's memory, shared with arrow-go
if err != nil {
	log.Fatal(err)
}
fmt.Println(rec.NumRows(), "rows,", rec.Schema().Field(1).Type)

back := ursus.ScanArrowRecords(rec) // copies, so rec can be released
rec.Release()
show(back.WithColumns(ursus.Col("qty").Mul(2).Alias("double")))
Output:
3 rows, int64
shape: (3, 3)
┌──────────┬───────┬────────┐
│ order_id │ qty   │ double │
│ Int64    │ Int64 │ Int64  │
├──────────┼───────┼────────┤
│ 1        │ 10    │ 20     │
│ 2        │ 4     │ 8      │
│ 3        │ 7     │ 14     │
└──────────┴───────┴────────┘
Example (IoCSVGlob)
show(ursus.ScanCSVGlob("testdata/parts/revenue-*.csv").
	GroupBy(ursus.Col("month")).
	Agg(ursus.Col("revenue").Sum().Alias("revenue")).
	Sort(ursus.Asc(ursus.Col("month"))))
Output:
shape: (2, 2)
┌───────────┬─────────┐
│ month     │ revenue │
│ String    │ Float64 │
├───────────┼─────────┤
│ "2026-01" │ 86.25   │
│ "2026-02" │ 79.25   │
└───────────┴─────────┘
Example (IoCSVOptions)
data := []byte("id;name;score\n1;Ada;90\n2;Ben;NA\n3;Cai;75\n")
show(ursus.ScanCSVReader(data, "scores.csv",
	ursus.WithSeparator(';'),
	ursus.WithNullValues("NA"),
	ursus.WithSchemaOverrides(map[string]ursus.DataType{"score": ursus.Float64}),
))
Output:
shape: (3, 3)
┌───────┬────────┬─────────┐
│ id    │ name   │ score   │
│ Int64 │ String │ Float64 │
├───────┼────────┼─────────┤
│ 1     │ "Ada"  │ 90      │
│ 2     │ "Ben"  │ null    │
│ 3     │ "Cai"  │ 75      │
└───────┴────────┴─────────┘
Example (IoCSVWrite)
var out bytes.Buffer
err := readings().Head(3).
	WriteCSV(context.Background(), &out, ursus.WithNullValue("NA"))
if err != nil {
	log.Fatal(err)
}
fmt.Print(out.String())
Output:
hour,temp,backup
0,10.0,9.0
1,NA,11.0
2,NA,NA
Example (IoParquet)
ctx := context.Background()
var file bytes.Buffer
if err := sales().WriteParquet(ctx, &file, ursus.WithRowGroupRows(4)); err != nil {
	log.Fatal(err)
}
q := ursus.ScanParquetBytes(file.Bytes(), "sales.parquet").
	Filter(ursus.Col("order_id").Gt(6)).
	Select(ursus.Col("order_id"), ursus.Col("product"))
plan, err := q.Explain(ctx)
if err != nil {
	log.Fatal(err)
}
fmt.Println(plan)
show(q)
Output:
FILTER [(col("order_id") > lit(6))]
  PARQUET SCAN sales.parquet
    projection: [order_id, product] (2/6 cols)
    predicate: [(col("order_id") > lit(6))]

shape: (2, 2)
┌──────────┬─────────┐
│ order_id │ product │
│ Int64    │ String  │
├──────────┼─────────┤
│ 7        │ "fig"   │
│ 8        │ "pear"  │
└──────────┴─────────┘
Example (IoParquetFrom)
ctx := context.Background()
var file bytes.Buffer
if err := sales().WriteParquet(ctx, &file); err != nil {
	log.Fatal(err)
}
stored := file.Bytes() // standing in for an object in a bucket

lf := ursus.ScanParquetFrom([]ursus.ParquetFile{{
	Name: "s3://shop/sales.parquet",
	Open: func(ctx context.Context) (io.ReaderAt, int64, error) {
		return bytes.NewReader(stored), int64(len(stored)), nil
	},
}})
n, err := lf.Filter(ursus.Col("region").Eq("south")).Count(ctx)
if err != nil {
	log.Fatal(err)
}
fmt.Println(n, "orders from the south")
Output:
3 orders from the south
Example (JoinAnti)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// customers and orders are the joins chapter's frames: customer 2 has no order, and
// order 13's customer, 5, is no customer.
func customers() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("customer_id", []int64{1, 2, 3, 4}),
		ursus.Values("name", []string{"Ada", "Ben", "Cai", "Dee"}),
	)
}

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	on := ursus.JoinOn(ursus.Col("customer_id"))
	show(customers().Join(orders(), on, ursus.JoinHow(ursus.JoinAnti)))
}
Output:
shape: (1, 2)
┌─────────────┬────────┐
│ customer_id │ name   │
│ Int64       │ String │
├─────────────┼────────┤
│ 2           │ "Ben"  │
└─────────────┴────────┘
Example (JoinAsOf)
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/advenn/ursus"
)

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	at := func(s int) time.Time { return time.Date(2026, 1, 5, 9, 0, s, 0, time.UTC) }
	trades := ursus.Frame(
		ursus.Values("time", []time.Time{at(1), at(5), at(9)}),
		ursus.Values("ticker", []string{"ACME", "BOLT", "ACME"}),
		ursus.Values("qty", []int64{100, 50, 70}),
	)
	quotes := ursus.Frame(
		ursus.Values("time", []time.Time{at(0), at(2), at(4), at(6)}),
		ursus.Values("ticker", []string{"ACME", "BOLT", "ACME", "BOLT"}),
		ursus.Values("bid", []float64{10.0, 20.0, 10.1, 20.2}),
	)
	show(trades.JoinAsOf(quotes,
		ursus.AsOfOn(ursus.Col("time")),
		ursus.AsOfBy(ursus.Col("ticker")),
		ursus.AsOfTolerance(ursus.Every("3s")),
	))
}
Output:
shape: (3, 4)
┌────────────────────────────────┬────────┬───────┬─────────┐
│ time                           │ ticker │ qty   │ bid     │
│ Datetime(ns, UTC)              │ String │ Int64 │ Float64 │
├────────────────────────────────┼────────┼───────┼─────────┤
│ 2026-01-05T09:00:01.000000000Z │ "ACME" │ 100   │ 10      │
│ 2026-01-05T09:00:05.000000000Z │ "BOLT" │ 50    │ 20      │
│ 2026-01-05T09:00:09.000000000Z │ "ACME" │ 70    │ null    │
└────────────────────────────────┴────────┴───────┴─────────┘
Example (JoinCoalesce)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// customers and orders are the joins chapter's frames: customer 2 has no order, and
// order 13's customer, 5, is no customer.
func customers() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("customer_id", []int64{1, 2, 3, 4}),
		ursus.Values("name", []string{"Ada", "Ben", "Cai", "Dee"}),
	)
}

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	on := ursus.JoinOn(ursus.Col("customer_id"))
	show(orders().Join(customers(), on,
		ursus.JoinHow(ursus.JoinFull),
		ursus.JoinCoalesce(true)))
}
Output:
shape: (6, 4)
┌───────┬─────────────┬─────────┬────────┐
│ order │ customer_id │ amount  │ name   │
│ Int64 │ Int64       │ Float64 │ String │
├───────┼─────────────┼─────────┼────────┤
│ 10    │ 1           │ 25      │ "Ada"  │
│ 11    │ 3           │ 40      │ "Cai"  │
│ 12    │ 1           │ 10      │ "Ada"  │
│ 13    │ 5           │ 99      │ null   │
│ 14    │ 4           │ 15      │ "Dee"  │
│ null  │ 2           │ null    │ "Ben"  │
└───────┴─────────────┴─────────┴────────┘
Example (JoinCross)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	sizes := ursus.Frame(ursus.Values("size", []string{"S", "M"}))
	colours := ursus.Frame(ursus.Values("colour", []string{"red", "blue", "green"}))
	show(sizes.Join(colours, ursus.JoinHow(ursus.JoinCross)))
}
Output:
shape: (6, 2)
┌────────┬─────────┐
│ size   │ colour  │
│ String │ String  │
├────────┼─────────┤
│ "S"    │ "red"   │
│ "S"    │ "blue"  │
│ "S"    │ "green" │
│ "M"    │ "red"   │
│ "M"    │ "blue"  │
│ "M"    │ "green" │
└────────┴─────────┘
Example (JoinFull)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// customers and orders are the joins chapter's frames: customer 2 has no order, and
// order 13's customer, 5, is no customer.
func customers() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("customer_id", []int64{1, 2, 3, 4}),
		ursus.Values("name", []string{"Ada", "Ben", "Cai", "Dee"}),
	)
}

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	on := ursus.JoinOn(ursus.Col("customer_id"))
	show(orders().Join(customers(), on, ursus.JoinHow(ursus.JoinFull)))
}
Output:
shape: (6, 5)
┌───────┬─────────────┬─────────┬───────────────────┬────────┐
│ order │ customer_id │ amount  │ customer_id_right │ name   │
│ Int64 │ Int64       │ Float64 │ Int64             │ String │
├───────┼─────────────┼─────────┼───────────────────┼────────┤
│ 10    │ 1           │ 25      │ 1                 │ "Ada"  │
│ 11    │ 3           │ 40      │ 3                 │ "Cai"  │
│ 12    │ 1           │ 10      │ 1                 │ "Ada"  │
│ 13    │ 5           │ 99      │ null              │ null   │
│ 14    │ 4           │ 15      │ 4                 │ "Dee"  │
│ null  │ null        │ null    │ 2                 │ "Ben"  │
└───────┴─────────────┴─────────┴───────────────────┴────────┘
Example (JoinInner)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// customers and orders are the joins chapter's frames: customer 2 has no order, and
// order 13's customer, 5, is no customer.
func customers() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("customer_id", []int64{1, 2, 3, 4}),
		ursus.Values("name", []string{"Ada", "Ben", "Cai", "Dee"}),
	)
}

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	on := ursus.JoinOn(ursus.Col("customer_id"))
	show(orders().Join(customers(), on, ursus.JoinMaintainOrder(ursus.JoinOrderLeft)))
}
Output:
shape: (4, 4)
┌───────┬─────────────┬─────────┬────────┐
│ order │ customer_id │ amount  │ name   │
│ Int64 │ Int64       │ Float64 │ String │
├───────┼─────────────┼─────────┼────────┤
│ 10    │ 1           │ 25      │ "Ada"  │
│ 11    │ 3           │ 40      │ "Cai"  │
│ 12    │ 1           │ 10      │ "Ada"  │
│ 14    │ 4           │ 15      │ "Dee"  │
└───────┴─────────────┴─────────┴────────┘
Example (JoinKeysByName)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	people := ursus.Frame(
		ursus.Values("id", []int64{1, 3}),
		ursus.Values("name", []string{"Ada", "Cai"}),
	)
	show(orders().
		Join(people,
			ursus.JoinLeftOn(ursus.Col("customer_id")),
			ursus.JoinRightOn(ursus.Col("id"))).
		Sort(ursus.Asc(ursus.Col("order"))))
}
Output:
shape: (3, 5)
┌───────┬─────────────┬─────────┬───────┬────────┐
│ order │ customer_id │ amount  │ id    │ name   │
│ Int64 │ Int64       │ Float64 │ Int64 │ String │
├───────┼─────────────┼─────────┼───────┼────────┤
│ 10    │ 1           │ 25      │ 1     │ "Ada"  │
│ 11    │ 3           │ 40      │ 3     │ "Cai"  │
│ 12    │ 1           │ 10      │ 1     │ "Ada"  │
└───────┴─────────────┴─────────┴───────┴────────┘
Example (JoinLeft)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// customers and orders are the joins chapter's frames: customer 2 has no order, and
// order 13's customer, 5, is no customer.
func customers() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("customer_id", []int64{1, 2, 3, 4}),
		ursus.Values("name", []string{"Ada", "Ben", "Cai", "Dee"}),
	)
}

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	on := ursus.JoinOn(ursus.Col("customer_id"))
	show(orders().Join(customers(), on, ursus.JoinHow(ursus.JoinLeft)))
}
Output:
shape: (5, 4)
┌───────┬─────────────┬─────────┬────────┐
│ order │ customer_id │ amount  │ name   │
│ Int64 │ Int64       │ Float64 │ String │
├───────┼─────────────┼─────────┼────────┤
│ 10    │ 1           │ 25      │ "Ada"  │
│ 11    │ 3           │ 40      │ "Cai"  │
│ 12    │ 1           │ 10      │ "Ada"  │
│ 13    │ 5           │ 99      │ null   │
│ 14    │ 4           │ 15      │ "Dee"  │
└───────┴─────────────┴─────────┴────────┘
Example (JoinNulls)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	key := ursus.ValuesNullable("k", []int64{1, 0}, []bool{true, false}) // 1 and null
	left := ursus.Frame(key, ursus.Values("l", []string{"a", "b"}))
	right := ursus.Frame(key, ursus.Values("r", []string{"x", "y"}))
	on := ursus.JoinOn(ursus.Col("k"))
	ordered := ursus.JoinMaintainOrder(ursus.JoinOrderLeft)
	show(left.Join(right, on, ordered))
	show(left.Join(right, on, ordered, ursus.JoinNullsEqual(true)))
}
Output:
shape: (1, 3)
┌───────┬────────┬────────┐
│ k     │ l      │ r      │
│ Int64 │ String │ String │
├───────┼────────┼────────┤
│ 1     │ "a"    │ "x"    │
└───────┴────────┴────────┘

shape: (2, 3)
┌───────┬────────┬────────┐
│ k     │ l      │ r      │
│ Int64 │ String │ String │
├───────┼────────┼────────┤
│ 1     │ "a"    │ "x"    │
│ null  │ "b"    │ "y"    │
└───────┴────────┴────────┘
Example (JoinRight)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// customers and orders are the joins chapter's frames: customer 2 has no order, and
// order 13's customer, 5, is no customer.
func customers() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("customer_id", []int64{1, 2, 3, 4}),
		ursus.Values("name", []string{"Ada", "Ben", "Cai", "Dee"}),
	)
}

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	on := ursus.JoinOn(ursus.Col("customer_id"))
	show(orders().Join(customers(), on, ursus.JoinHow(ursus.JoinRight)))
}
Output:
shape: (5, 4)
┌───────┬─────────────┬─────────┬────────┐
│ order │ customer_id │ amount  │ name   │
│ Int64 │ Int64       │ Float64 │ String │
├───────┼─────────────┼─────────┼────────┤
│ 10    │ 1           │ 25      │ "Ada"  │
│ 11    │ 3           │ 40      │ "Cai"  │
│ 12    │ 1           │ 10      │ "Ada"  │
│ 14    │ 4           │ 15      │ "Dee"  │
│ null  │ 2           │ null    │ "Ben"  │
└───────┴─────────────┴─────────┴────────┘
Example (JoinSemi)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// customers and orders are the joins chapter's frames: customer 2 has no order, and
// order 13's customer, 5, is no customer.
func customers() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("customer_id", []int64{1, 2, 3, 4}),
		ursus.Values("name", []string{"Ada", "Ben", "Cai", "Dee"}),
	)
}

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	on := ursus.JoinOn(ursus.Col("customer_id"))
	show(customers().Join(orders(), on, ursus.JoinHow(ursus.JoinSemi)))
}
Output:
shape: (3, 2)
┌─────────────┬────────┐
│ customer_id │ name   │
│ Int64       │ String │
├─────────────┼────────┤
│ 1           │ "Ada"  │
│ 3           │ "Cai"  │
│ 4           │ "Dee"  │
└─────────────┴────────┘
Example (JoinSuffix)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	shipped := ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12}),
		ursus.Values("amount", []float64{25, 38, 10}),
	)
	show(orders().
		Join(shipped,
			ursus.JoinOn(ursus.Col("order")),
			ursus.JoinSuffix("_shipped"),
			ursus.JoinMaintainOrder(ursus.JoinOrderLeft)).
		Select(ursus.Col("order"), ursus.Col("amount"), ursus.Col("amount_shipped")))
}
Output:
shape: (3, 3)
┌───────┬─────────┬────────────────┐
│ order │ amount  │ amount_shipped │
│ Int64 │ Float64 │ Float64        │
├───────┼─────────┼────────────────┤
│ 10    │ 25      │ 25             │
│ 11    │ 40      │ 38             │
│ 12    │ 10      │ 10             │
└───────┴─────────┴────────────────┘
Example (JoinUpdate)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	prices := ursus.Frame(
		ursus.Values("sku", []string{"a", "b", "c"}),
		ursus.ValuesNullable("price", []float64{9.5, 3, 0}, []bool{true, true, false}),
	)
	fixes := ursus.Frame(
		ursus.Values("sku", []string{"c", "a"}),
		ursus.Values("price", []float64{4.25, 9.75}),
	)
	show(prices.Update(fixes, ursus.UpdateOptions{On: []string{"sku"}}))
}
Output:
shape: (3, 2)
┌────────┬─────────┐
│ sku    │ price   │
│ String │ Float64 │
├────────┼─────────┤
│ "a"    │ 9.75    │
│ "b"    │ 3       │
│ "c"    │ 4.25    │
└────────┴─────────┘
Example (JoinValidate)
package main

import (
	"context"
	"fmt"

	"github.com/advenn/ursus"
)

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

func main() {
	twice := ursus.Frame(
		ursus.Values("customer_id", []int64{1, 1}),
		ursus.Values("name", []string{"Ada", "Ada Lovelace"}),
	)
	_, err := orders().
		Join(twice,
			ursus.JoinOn(ursus.Col("customer_id")),
			ursus.JoinValidate(ursus.ValidateManyToOne)).
		Collect(context.Background())
	fmt.Println(err)
}
Output:
ursus: join: validate=m:1 requires unique keys on the right side, but a key is duplicated
  first duplicate at right row 1
  key: col("customer_id")
  each duplicate multiplies every matching row on the other side
  de-duplicate with .Unique(...), or pass JoinValidate(ursus.ValidateManyToMany)
Example (JoinWhere)
promotions := ursus.Frame(
	ursus.Values("promotion", []string{"new year", "mid week"}),
	ursus.Values("from", []string{"2026-01-01", "2026-01-06"}),
	ursus.Values("to", []string{"2026-01-04", "2026-01-07"}),
).WithColumns(ursus.Col("from").Str().ToDate(), ursus.Col("to").Str().ToDate())
show(sales().
	Select(ursus.Col("order_id"), ursus.Col("date")).
	JoinWhere(promotions,
		ursus.Col("date").Ge(ursus.Col("from")),
		ursus.Col("date").Le(ursus.Col("to"))).
	Select(ursus.Col("order_id"), ursus.Col("date"), ursus.Col("promotion")).
	Sort(ursus.Asc(ursus.Col("order_id"))))
Output:
shape: (6, 3)
┌──────────┬────────────┬────────────┐
│ order_id │ date       │ promotion  │
│ Int64    │ Date       │ String     │
├──────────┼────────────┼────────────┤
│ 1        │ 2026-01-03 │ "new year" │
│ 2        │ 2026-01-03 │ "new year" │
│ 3        │ 2026-01-04 │ "new year" │
│ 6        │ 2026-01-06 │ "mid week" │
│ 7        │ 2026-01-07 │ "mid week" │
│ 8        │ 2026-01-07 │ "mid week" │
└──────────┴────────────┴────────────┘
Example (LazyBatches)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// sales is the CSV the concept pages read, its date column a Date.
func sales() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	ctx := context.Background()
	for batch, err := range sales().CollectBatches(ctx, ursus.WithBatchSize(3)) {
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("a batch of", batch.Height(), "rows")
	}
}
Output:
a batch of 3 rows
a batch of 3 rows
a batch of 2 rows
Example (LazyDeferredError)
package main

import (
	"context"
	"fmt"

	"github.com/advenn/ursus"
)

// sales is the CSV the concept pages read, its date column a Date.
func sales() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	lf := sales().
		Select(ursus.Col("prodcut")). // a typo: nothing has run, so nothing fails yet
		Filter(ursus.Col("qty").Gt(int64(3)))

	_, err := lf.Collect(context.Background())
	fmt.Println(err)
}
Output:
ursus: select: unknown column "prodcut"
  did you mean: "product"?
  available: order_id, date, region, product, qty, price
  at Project
Example (LazyExplainUnoptimized)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// sales is the CSV the concept pages read, its date column a Date.
func sales() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	q := sales().
		Filter(ursus.Col("qty").Gt(int64(3))).
		Select(ursus.Col("product"), ursus.Col("qty"))

	written, err := q.Explain(context.Background(), ursus.Optimized(false))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(written)
}
Output:
PROJECT [col("product"), col("qty")]
  FILTER [(col("qty") > lit(3))]
    CSV SCAN testdata/sales.csv
      projection: * (6 cols)
Example (ListBuild)
show(baskets())
show(sales().
	GroupBy(ursus.Col("region")).
	Agg(ursus.Col("qty").Implode().Alias("qtys")).
	Sort(ursus.Asc(ursus.Col("region"))))
Output:
shape: (3, 3)
┌───────┬──────────────────────────┬────────────────┐
│ order │ items                    │ prices         │
│ Int64 │ List(String)             │ List(Float64)  │
├───────┼──────────────────────────┼────────────────┤
│ 1     │ ["pear", "apple", "fig"] │ [2.25, 1.5, 3] │
│ 2     │ ["fig"]                  │ [3]            │
│ 3     │ ["apple", "pear"]        │ [1.5, 2.25]    │
└───────┴──────────────────────────┴────────────────┘

shape: (3, 2)
┌─────────┬─────────────┐
│ region  │ qtys        │
│ String  │ List(Int64) │
├─────────┼─────────────┤
│ "east"  │ [2, 12]     │
│ "north" │ [10, 7, 3]  │
│ "south" │ [4, 9, 6]   │
└─────────┴─────────────┘
Example (ListExplode)
show(baskets().Explode("items", "prices"))
Output:
shape: (6, 3)
┌───────┬─────────┬─────────┐
│ order │ items   │ prices  │
│ Int64 │ String  │ Float64 │
├───────┼─────────┼─────────┤
│ 1     │ "pear"  │ 2.25    │
│ 1     │ "apple" │ 1.5     │
│ 1     │ "fig"   │ 3       │
│ 2     │ "fig"   │ 3       │
│ 3     │ "apple" │ 1.5     │
│ 3     │ "pear"  │ 2.25    │
└───────┴─────────┴─────────┘
Example (ListFilterEval)
prices := ursus.Col("prices").List()
show(baskets().Select(
	ursus.Col("order"),
	prices.Filter(ursus.Element().Ge(2.0)).Alias("from_2"),
	prices.Eval(ursus.Element().Rank(ursus.RankDense, true)).Alias("rank"),
))
Output:
shape: (3, 3)
┌───────┬───────────────┬──────────────┐
│ order │ from_2        │ rank         │
│ Int64 │ List(Float64) │ List(Uint32) │
├───────┼───────────────┼──────────────┤
│ 1     │ [2.25, 3]     │ [2, 3, 1]    │
│ 2     │ [3]           │ [1]          │
│ 3     │ [2.25]        │ [2, 1]       │
└───────┴───────────────┴──────────────┘
Example (ListQuestions)
items, prices := ursus.Col("items").List(), ursus.Col("prices").List()
show(baskets().Select(
	ursus.Col("order"),
	items.Len().Alias("n"),
	items.Get(0).Alias("first"),
	items.Contains("fig").Alias("has_fig"),
	prices.Sum().Alias("total"),
	prices.Max().Alias("dearest"),
))
Output:
shape: (3, 6)
┌───────┬────────┬─────────┬─────────┬─────────┬─────────┐
│ order │ n      │ first   │ has_fig │ total   │ dearest │
│ Int64 │ Uint32 │ String  │ Bool    │ Float64 │ Float64 │
├───────┼────────┼─────────┼─────────┼─────────┼─────────┤
│ 1     │ 3      │ "pear"  │ true    │ 6.75    │ 3       │
│ 2     │ 1      │ "fig"   │ true    │ 3       │ 3       │
│ 3     │ 2      │ "apple" │ false   │ 3.75    │ 2.25    │
└───────┴────────┴─────────┴─────────┴─────────┴─────────┘
Example (ListSets)
items := ursus.Col("items")
fruit := ursus.Lit([]string{"apple", "pear"})
show(baskets().Select(
	ursus.Col("order"),
	items.List().SetIntersection(fruit).Alias("in_both"),
	items.List().SetDifference(fruit).Alias("only_here"),
))
Output:
shape: (3, 3)
┌───────┬───────────────────┬──────────────┐
│ order │ in_both           │ only_here    │
│ Int64 │ List(String)      │ List(String) │
├───────┼───────────────────┼──────────────┤
│ 1     │ ["pear", "apple"] │ ["fig"]      │
│ 2     │ []                │ ["fig"]      │
│ 3     │ ["apple", "pear"] │ []           │
└───────┴───────────────────┴──────────────┘
Example (ListTransform)
items := ursus.Col("items").List()
show(baskets().Select(
	ursus.Col("order"),
	items.Sort().Alias("sorted"),
	items.Join(" + ").Alias("joined"),
	ursus.Col("prices").List().Eval(ursus.Element().Mul(2)).Alias("doubled"),
))
Output:
shape: (3, 4)
┌───────┬──────────────────────────┬──────────────────────┬───────────────┐
│ order │ sorted                   │ joined               │ doubled       │
│ Int64 │ List(String)             │ String               │ List(Float64) │
├───────┼──────────────────────────┼──────────────────────┼───────────────┤
│ 1     │ ["apple", "fig", "pear"] │ "pear + apple + fig" │ [4.5, 3, 6]   │
│ 2     │ ["fig"]                  │ "fig"                │ [6]           │
│ 3     │ ["apple", "pear"]        │ "apple + pear"       │ [3, 4.5]      │
└───────┴──────────────────────────┴──────────────────────┴───────────────┘
Example (MemoryRefusal)
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/advenn/ursus"
)

func main() {
	keys := make([]int64, 100_000)
	_, err := ursus.Frame(ursus.Values("k", keys)).
		Select(ursus.Col("k").Implode()).
		Collect(context.Background(),
			ursus.WithMemoryLimit(64<<10),
			ursus.WithSpillDir(os.TempDir()))
	fmt.Println(err)
}
Output:
ursus: group_by: memory limit of 64.0KiB exceeded: the query is holding 105.2KiB, 105.2KiB of it in this operator
  group_by holds one entry per distinct key, and implode holds every VALUE as its answer — which partitioning to disk cannot divide, because it divides across keys (quantile, median, n_unique, mode and corr_spearman spill their values); raise the limit with WithMemoryLimit
  sort, group_by, join, unique, over, reverse and tail spill to disk; join_asof, merge_sorted, hstack, rolling and group_by_dynamic stream their input; an over computing a rolling window by time one of whose periods holds more than the limit fails rather than exceed it, and so does implode over one group larger than it
Example (MemorySink)

Compiled, not run: it reads files the tests do not have.

package main

import (
	"context"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	ctx := context.Background()
	err := ursus.ScanParquet("events/*.parquet").
		Sort(ursus.Desc(ursus.Col("ts"))).
		SinkParquet(ctx, "sorted.parquet",
			ursus.WithMemoryLimit(512<<20),
			ursus.WithSpillDir("/var/tmp/ursus"))
	if err != nil {
		log.Fatal(err)
	}
}
Example (MemorySpill)
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/advenn/ursus"
)

func main() {
	keys := make([]int64, 100_000)
	for i := range keys {
		keys[i] = int64(i * 7919 % 25_000)
	}
	var stats ursus.MemoryStats
	df, err := ursus.Frame(ursus.Values("k", keys)).
		GroupBy(ursus.Col("k")).Agg(ursus.Len().Alias("n")).
		Select(ursus.Len().Alias("groups"), ursus.Col("n").Max().Alias("largest")).
		Collect(context.Background(),
			ursus.WithMemoryLimit(256<<10),
			ursus.WithSpillDir(os.TempDir()),
			ursus.WithMemoryStats(&stats))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
	fmt.Println("spilled:", stats.Spills > 0)
}
Output:
shape: (1, 2)
┌────────┬─────────┐
│ groups │ largest │
│ Uint64 │ Uint64  │
├────────┼─────────┤
│ 25000  │ 4       │
└────────┴─────────┘

spilled: true
Example (MissingCount)
show(readings().Select(ursus.All().NullCount()))
Output:
shape: (1, 3)
┌────────┬────────┬────────┐
│ hour   │ temp   │ backup │
│ Uint64 │ Uint64 │ Uint64 │
├────────┼────────┼────────┤
│ 0      │ 3      │ 3      │
└────────┴────────┴────────┘
Example (MissingDrop)
show(readings().DropNulls("temp"))
show(readings().DropNulls())
Output:
shape: (3, 3)
┌───────┬─────────┬─────────┐
│ hour  │ temp    │ backup  │
│ Int64 │ Float64 │ Float64 │
├───────┼─────────┼─────────┤
│ 0     │ 10      │ 9       │
│ 3     │ 13      │ null    │
│ 4     │ 12      │ null    │
└───────┴─────────┴─────────┘

shape: (1, 3)
┌───────┬─────────┬─────────┐
│ hour  │ temp    │ backup  │
│ Int64 │ Float64 │ Float64 │
├───────┼─────────┼─────────┤
│ 0     │ 10      │ 9       │
└───────┴─────────┴─────────┘
Example (MissingDropNans)
nan := math.NaN()
present := []bool{true, true, false, true} // the third is null
ratios := ursus.Frame(
	ursus.Values("id", []int64{1, 2, 3, 4}),
	ursus.ValuesNullable("ratio", []float64{0.5, nan, 0, 2}, present),
)
show(ratios.DropNans())
Output:
shape: (3, 2)
┌───────┬─────────┐
│ id    │ ratio   │
│ Int64 │ Float64 │
├───────┼─────────┤
│ 1     │ 0.5     │
│ 3     │ null    │
│ 4     │ 2       │
└───────┴─────────┘
Example (MissingFillNeighbours)
temp := ursus.Col("temp")
show(readings().Select(
	ursus.Col("hour"),
	temp,
	temp.FillNull(ursus.FillForward).Alias("forward"),
	temp.ForwardFill(1).Alias("forward_1"),
	temp.FillNull(ursus.FillBackward).Alias("backward"),
	temp.Interpolate().Alias("interpolated"),
))
Output:
shape: (6, 6)
┌───────┬─────────┬─────────┬───────────┬──────────┬──────────────┐
│ hour  │ temp    │ forward │ forward_1 │ backward │ interpolated │
│ Int64 │ Float64 │ Float64 │ Float64   │ Float64  │ Float64      │
├───────┼─────────┼─────────┼───────────┼──────────┼──────────────┤
│ 0     │ 10      │ 10      │ 10        │ 10       │ 10           │
│ 1     │ null    │ 10      │ 10        │ 13       │ 11           │
│ 2     │ null    │ 10      │ null      │ 13       │ 12           │
│ 3     │ 13      │ 13      │ 13        │ 13       │ 13           │
│ 4     │ 12      │ 12      │ 12        │ 12       │ 12           │
│ 5     │ null    │ 12      │ 12        │ null     │ null         │
└───────┴─────────┴─────────┴───────────┴──────────┴──────────────┘
Example (MissingFillValue)
temp := ursus.Col("temp")
show(readings().Select(
	ursus.Col("hour"),
	temp,
	temp.FillNullWith(0.0).Alias("zero"),
	temp.FillNull(ursus.FillMean).Alias("mean"),
	ursus.Coalesce(temp, ursus.Col("backup"), ursus.Lit(-1.0)).Alias("coalesced"),
))
Output:
shape: (6, 5)
┌───────┬─────────┬─────────┬────────────────────┬───────────┐
│ hour  │ temp    │ zero    │ mean               │ coalesced │
│ Int64 │ Float64 │ Float64 │ Float64            │ Float64   │
├───────┼─────────┼─────────┼────────────────────┼───────────┤
│ 0     │ 10      │ 10      │ 10                 │ 10        │
│ 1     │ null    │ 0       │ 11.666666666666666 │ 11        │
│ 2     │ null    │ 0       │ 11.666666666666666 │ -1        │
│ 3     │ 13      │ 13      │ 13                 │ 13        │
│ 4     │ 12      │ 12      │ 12                 │ 12        │
│ 5     │ null    │ 0       │ 11.666666666666666 │ 14        │
└───────┴─────────┴─────────┴────────────────────┴───────────┘
Example (MissingFrameFill)
show(readings().FillNull(0.0))
Output:
shape: (6, 3)
┌───────┬─────────┬─────────┐
│ hour  │ temp    │ backup  │
│ Int64 │ Float64 │ Float64 │
├───────┼─────────┼─────────┤
│ 0     │ 10      │ 9       │
│ 1     │ 0       │ 11      │
│ 2     │ 0       │ 0       │
│ 3     │ 13      │ 0       │
│ 4     │ 12      │ 0       │
│ 5     │ 0       │ 14      │
└───────┴─────────┴─────────┘
Example (MissingInterpolateFrame)
day := func(d int) time.Time { return time.Date(2026, 1, d, 0, 0, 0, 0, time.UTC) }
ends := []bool{true, false, false, true} // two rows missing between
gappy := ursus.Frame(
	ursus.ValuesNullable("day", []time.Time{day(1), {}, {}, day(7)}, ends),
	ursus.ValuesNullable("level", []int64{10, 0, 0, 40}, ends),
	ursus.ValuesNullable("note", []string{"start", "", "", "end"}, ends),
).WithColumns(ursus.Col("day").Cast(ursus.Date))
show(gappy.Interpolate())
Output:
shape: (4, 3)
┌────────────┬─────────┬─────────┐
│ day        │ level   │ note    │
│ Date       │ Float64 │ String  │
├────────────┼─────────┼─────────┤
│ 2026-01-01 │ 10      │ "start" │
│ 2026-01-03 │ 20      │ null    │
│ 2026-01-05 │ 30      │ null    │
│ 2026-01-07 │ 40      │ "end"   │
└────────────┴─────────┴─────────┘
Example (MissingLimit)
show(readings().FillNull(ursus.FillForward.Limit(1)))
Output:
shape: (6, 3)
┌───────┬─────────┬─────────┐
│ hour  │ temp    │ backup  │
│ Int64 │ Float64 │ Float64 │
├───────┼─────────┼─────────┤
│ 0     │ 10      │ 9       │
│ 1     │ 10      │ 11      │
│ 2     │ null    │ 11      │
│ 3     │ 13      │ null    │
│ 4     │ 12      │ null    │
│ 5     │ 12      │ 14      │
└───────┴─────────┴─────────┘
Example (QuickstartExplain)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	plan, err := ursus.ScanCSV("testdata/sales.csv").
		Filter(ursus.Col("qty").Gt(int64(3))).
		Select(ursus.Col("product"), ursus.Col("qty")).
		Explain(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(plan)
}
Output:
FILTER [(col("qty") > lit(3))]
  CSV SCAN testdata/sales.csv
    projection: [product, qty] (2/6 cols)
Example (QuickstartFrame)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.Frame(
		ursus.Values("city", []string{"Oslo", "Lima", "Pune"}),
		ursus.Values("population", []int64{709_000, 10_151_000, 7_764_000}),
		ursus.Values("coastal", []bool{true, true, false}),
	).Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (3, 3)
┌────────┬────────────┬─────────┐
│ city   │ population │ coastal │
│ String │ Int64      │ Bool    │
├────────┼────────────┼─────────┤
│ "Oslo" │ 709000     │ true    │
│ "Lima" │ 10151000   │ true    │
│ "Pune" │ 7764000    │ false   │
└────────┴────────────┴─────────┘
Example (QuickstartGroupBy)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.ScanCSV("testdata/sales.csv").
		GroupBy(ursus.Col("product")).
		Agg(
			ursus.Col("qty").Sum().Alias("qty"),
			ursus.Col("price").Max().Alias("price"),
			ursus.Col("region").NUnique().Alias("regions"),
		).
		Sort(ursus.Asc(ursus.Col("product"))).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (3, 4)
┌─────────┬────────┬─────────┬─────────┐
│ product │ qty    │ price   │ regions │
│ String  │ Int128 │ Float64 │ Uint64  │
├─────────┼────────┼─────────┼─────────┤
│ "apple" │ 26     │ 1.5     │ 3       │
│ "fig"   │ 12     │ 3       │ 2       │
│ "pear"  │ 15     │ 2.25    │ 3       │
└─────────┴────────┴─────────┴─────────┘
Example (QuickstartJoin)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	managers := ursus.Frame(
		ursus.Values("region", []string{"north", "south"}),
		ursus.Values("manager", []string{"Ada", "Grace"}),
	)
	df, err := ursus.ScanCSV("testdata/sales.csv").
		Join(managers,
			ursus.JoinOn(ursus.Col("region")),
			ursus.JoinHow(ursus.JoinLeft)).
		Select(ursus.Col("order_id"), ursus.Col("region"), ursus.Col("manager")).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (8, 3)
┌──────────┬─────────┬─────────┐
│ order_id │ region  │ manager │
│ Int64    │ String  │ String  │
├──────────┼─────────┼─────────┤
│ 1        │ "north" │ "Ada"   │
│ 2        │ "south" │ "Grace" │
│ 3        │ "north" │ "Ada"   │
│ 4        │ "east"  │ null    │
│ 5        │ "south" │ "Grace" │
│ 6        │ "east"  │ null    │
│ 7        │ "north" │ "Ada"   │
│ 8        │ "south" │ "Grace" │
└──────────┴─────────┴─────────┘
Example (QuickstartRows)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	type sale struct {
		ID      int64  `ursus:"order_id"`
		Product string `ursus:"product"`
		Qty     int64  `ursus:"qty"`
	}
	df, err := ursus.ScanCSV("testdata/sales.csv").
		Filter(ursus.Col("qty").Ge(int64(9))).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	sales, err := df.Rows[sale]()
	if err != nil {
		log.Fatal(err)
	}
	for _, s := range sales {
		fmt.Printf("%+v\n", s)
	}
}
Output:
{ID:1 Product:apple Qty:10}
{ID:5 Product:fig Qty:9}
{ID:6 Product:apple Qty:12}
Example (QuickstartScan)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}),
	).Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (8, 6)
┌──────────┬────────────┬─────────┬─────────┬───────┬─────────┐
│ order_id │ date       │ region  │ product │ qty   │ price   │
│ Int64    │ Date       │ String  │ String  │ Int64 │ Float64 │
├──────────┼────────────┼─────────┼─────────┼───────┼─────────┤
│ 1        │ 2026-01-03 │ "north" │ "apple" │ 10    │ 1.5     │
│ 2        │ 2026-01-03 │ "south" │ "apple" │ 4     │ 1.5     │
│ 3        │ 2026-01-04 │ "north" │ "pear"  │ 7     │ 2.25    │
│ 4        │ 2026-01-05 │ "east"  │ "pear"  │ 2     │ 2.25    │
│ 5        │ 2026-01-05 │ "south" │ "fig"   │ 9     │ 3       │
│ 6        │ 2026-01-06 │ "east"  │ "apple" │ 12    │ 1.5     │
│ 7        │ 2026-01-07 │ "north" │ "fig"   │ 3     │ 3       │
│ 8        │ 2026-01-07 │ "south" │ "pear"  │ 6     │ 2.25    │
└──────────┴────────────┴─────────┴─────────┴───────┴─────────┘
Example (QuickstartSelect)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.ScanCSV("testdata/sales.csv").
		Filter(ursus.Col("region").Eq("north")).
		Select(ursus.Col("product"), ursus.Col("qty")).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (3, 2)
┌─────────┬───────┐
│ product │ qty   │
│ String  │ Int64 │
├─────────┼───────┤
│ "apple" │ 10    │
│ "pear"  │ 7     │
│ "fig"   │ 3     │
└─────────┴───────┘
Example (QuickstartWithColumns)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.ScanCSV("testdata/sales.csv").
		WithColumns(
			ursus.Col("qty").Mul(ursus.Col("price")).Alias("revenue"),
			ursus.When(ursus.Col("qty").Ge(int64(8))).
				Then("bulk").
				Otherwise("single").
				Alias("kind"),
		).
		Select(ursus.Col("order_id"), ursus.Col("revenue"), ursus.Col("kind")).
		Head(4).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (4, 3)
┌──────────┬─────────┬──────────┐
│ order_id │ revenue │ kind     │
│ Int64    │ Float64 │ String   │
├──────────┼─────────┼──────────┤
│ 1        │ 15      │ "bulk"   │
│ 2        │ 6       │ "single" │
│ 3        │ 15.75   │ "single" │
│ 4        │ 4.5     │ "single" │
└──────────┴─────────┴──────────┘
Example (ReshapeConcat)
january := ursus.Frame(
	ursus.Values("day", []int64{30, 31}),
	ursus.Values("qty", []int64{4, 6}),
)
february := ursus.Frame(
	ursus.Values("day", []int64{1, 2}),
	ursus.Values("qty", []int64{3, 8}),
)
show(ursus.Concat([]*ursus.LazyFrame{january, february}))
Output:
shape: (4, 2)
┌───────┬───────┐
│ day   │ qty   │
│ Int64 │ Int64 │
├───────┼───────┤
│ 30    │ 4     │
│ 31    │ 6     │
│ 1     │ 3     │
│ 2     │ 8     │
└───────┴───────┘
Example (ReshapeConcatAlign)
prices := ursus.Frame(
	ursus.Values("day", []int64{3, 1, 2}),
	ursus.Values("close", []float64{10.5, 10.0, 11.0}),
)
volumes := ursus.Frame(
	ursus.Values("day", []int64{2, 4, 1}),
	ursus.Values("volume", []int64{700, 900, 500}),
)
show(ursus.Concat([]*ursus.LazyFrame{prices, volumes},
	ursus.WithConcatMode(ursus.ConcatAlign)))
Output:
shape: (4, 3)
┌───────┬─────────┬────────┐
│ day   │ close   │ volume │
│ Int64 │ Float64 │ Int64  │
├───────┼─────────┼────────┤
│ 1     │ 10      │ 500    │
│ 2     │ 11      │ 700    │
│ 3     │ 10.5    │ null   │
│ 4     │ null    │ 900    │
└───────┴─────────┴────────┘
Example (ReshapeConcatDiagonal)
old := ursus.Frame(
	ursus.Values("id", []int64{1, 2}),
	ursus.Values("name", []string{"Ada", "Ben"}),
)
recent := ursus.Frame(
	ursus.Values("id", []int64{3}),
	ursus.Values("email", []string{"cai@example.com"}),
)
show(ursus.Concat([]*ursus.LazyFrame{old, recent},
	ursus.WithConcatMode(ursus.ConcatDiagonal)))
Output:
shape: (3, 3)
┌───────┬────────┬───────────────────┐
│ id    │ name   │ email             │
│ Int64 │ String │ String            │
├───────┼────────┼───────────────────┤
│ 1     │ "Ada"  │ null              │
│ 2     │ "Ben"  │ null              │
│ 3     │ null   │ "cai@example.com" │
└───────┴────────┴───────────────────┘
Example (ReshapeConcatRelaxed)
counted := ursus.Frame(ursus.Values("qty", []int64{4, 6}))
weighed := ursus.Frame(ursus.Values("qty", []float64{2.5}))
show(ursus.Concat([]*ursus.LazyFrame{counted, weighed},
	ursus.WithConcatMode(ursus.ConcatRelaxed)))
Output:
shape: (3, 1)
┌─────────┐
│ qty     │
│ Float64 │
├─────────┤
│ 4       │
│ 6       │
│ 2.5     │
└─────────┘
Example (ReshapeDummies)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	ctx := context.Background()
	df, err := ursus.Frame(
		ursus.Values("id", []int64{1, 2, 3, 4}),
		ursus.ValuesNullable("size", []int64{10, 2, 0, 10},
			[]bool{true, true, false, true}),
	).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	d, err := df.ToDummies(ctx, ursus.DummiesOptions{Columns: []string{"size"}})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(d)
}
Output:
shape: (4, 4)
┌───────┬─────────┬────────┬───────────┐
│ id    │ size_10 │ size_2 │ size_null │
│ Int64 │ Uint8   │ Uint8  │ Uint8     │
├───────┼─────────┼────────┼───────────┤
│ 1     │ 1       │ 0      │ 0         │
│ 2     │ 0       │ 1      │ 0         │
│ 3     │ 0       │ 0      │ 1         │
│ 4     │ 1       │ 0      │ 0         │
└───────┴─────────┴────────┴───────────┘
Example (ReshapeExplode)
posts := ursus.Frame(
	ursus.Values("post", []int64{1, 2, 3}),
	ursus.Values("tags", []string{"go,data", "go", ""}),
).WithColumns(ursus.Col("tags").Str().Split(","))
show(posts)
show(posts.Explode("tags"))
Output:
shape: (3, 2)
┌───────┬────────────────┐
│ post  │ tags           │
│ Int64 │ List(String)   │
├───────┼────────────────┤
│ 1     │ ["go", "data"] │
│ 2     │ ["go"]         │
│ 3     │ [""]           │
└───────┴────────────────┘

shape: (4, 2)
┌───────┬────────┐
│ post  │ tags   │
│ Int64 │ String │
├───────┼────────┤
│ 1     │ "go"   │
│ 1     │ "data" │
│ 2     │ "go"   │
│ 3     │ ""     │
└───────┴────────┘
Example (ReshapeHStack)
names := ursus.Frame(ursus.Values("name", []string{"Ada", "Ben"}))
scores := ursus.Frame(ursus.Values("score", []int64{90, 75}))
show(names.HStack(scores))
Output:
shape: (2, 2)
┌────────┬───────┐
│ name   │ score │
│ String │ Int64 │
├────────┼───────┤
│ "Ada"  │ 90    │
│ "Ben"  │ 75    │
└────────┴───────┘
Example (ReshapePartitionBy)
ctx := context.Background()
df, err := sales().Collect(ctx)
if err != nil {
	log.Fatal(err)
}
parts, err := df.PartitionBy(ctx, ursus.PartitionOptions{
	By:      []string{"region"},
	DropKey: true,
})
if err != nil {
	log.Fatal(err)
}
for _, p := range parts {
	region, _, _ := p.Key.At[string](0, "region")
	fmt.Println(region, p.Frame.Height())
}
fmt.Println(parts[0].Frame)
Output:
north 3
south 3
east 2
shape: (3, 5)
┌──────────┬────────────┬─────────┬───────┬─────────┐
│ order_id │ date       │ product │ qty   │ price   │
│ Int64    │ Date       │ String  │ Int64 │ Float64 │
├──────────┼────────────┼─────────┼───────┼─────────┤
│ 1        │ 2026-01-03 │ "apple" │ 10    │ 1.5     │
│ 3        │ 2026-01-04 │ "pear"  │ 7     │ 2.25    │
│ 7        │ 2026-01-07 │ "fig"   │ 3     │ 3       │
└──────────┴────────────┴─────────┴───────┴─────────┘
Example (ReshapePivot)
ctx := context.Background()
df, err := sales().Collect(ctx)
if err != nil {
	log.Fatal(err)
}
wide, err := df.Pivot(ctx, ursus.PivotOptions{
	On:          []string{"product"},
	Index:       []string{"region"},
	Values:      []string{"qty"},
	Agg:         ursus.Expr.Sum,
	SortColumns: true,
})
if err != nil {
	log.Fatal(err)
}
fmt.Println(wide)
Output:
shape: (3, 4)
┌─────────┬────────┬────────┬────────┐
│ region  │ apple  │ fig    │ pear   │
│ String  │ Int128 │ Int128 │ Int128 │
├─────────┼────────┼────────┼────────┤
│ "north" │ 10     │ 3      │ 7      │
│ "south" │ 4      │ 9      │ 6      │
│ "east"  │ 12     │ null   │ 2      │
└─────────┴────────┴────────┴────────┘
Example (ReshapePivotLazy)
products := ursus.Values("product", []string{"apple", "pear", "plum"})
wide := sales().Pivot(ursus.PivotOptions{
	On:        []string{"product"},
	OnColumns: []*ursus.Column{products},
	Index:     []string{"region"},
	Values:    []string{"qty"},
	Agg:       ursus.Expr.Sum,
})
show(wide)
Output:
shape: (3, 4)
┌─────────┬────────┬────────┬────────┐
│ region  │ apple  │ pear   │ plum   │
│ String  │ Int128 │ Int128 │ Int128 │
├─────────┼────────┼────────┼────────┤
│ "north" │ 10     │ 7      │ null   │
│ "south" │ 4      │ 6      │ null   │
│ "east"  │ 12     │ 2      │ null   │
└─────────┴────────┴────────┴────────┘
Example (ReshapePivotSeveral)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	ctx := context.Background()
	df, err := ursus.Frame(
		ursus.Values("shop", []string{"a", "a", "b", "b"}),
		ursus.Values("quarter", []string{"q1", "q2", "q1", "q1"}),
		ursus.Values("channel", []string{"web", "web", "store", "web"}),
		ursus.Values("sales", []int64{10, 12, 7, 3}),
	).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	wide, err := df.Pivot(ctx, ursus.PivotOptions{
		On:     []string{"quarter", "channel"},
		Index:  []string{"shop"},
		Values: []string{"sales"},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(wide)
}
Output:
shape: (2, 4)
┌────────┬──────────────┬──────────────┬────────────────┐
│ shop   │ {"q1","web"} │ {"q2","web"} │ {"q1","store"} │
│ String │ Int64        │ Int64        │ Int64          │
├────────┼──────────────┼──────────────┼────────────────┤
│ "a"    │ 10           │ 12           │ null           │
│ "b"    │ 3            │ null         │ 7              │
└────────┴──────────────┴──────────────┴────────────────┘
Example (ReshapeTranspose)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	ctx := context.Background()
	df, err := ursus.Frame(
		ursus.Values("metric", []string{"orders", "revenue"}),
		ursus.Values("north", []float64{3, 39.75}),
		ursus.Values("south", []float64{3, 46.5}),
	).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	t, err := df.Transpose(ctx, ursus.TransposeOptions{
		IncludeHeader:   true,
		HeaderName:      "region",
		ColumnNamesFrom: "metric",
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(t)
}
Output:
shape: (2, 3)
┌─────────┬─────────┬─────────┐
│ region  │ orders  │ revenue │
│ String  │ Float64 │ Float64 │
├─────────┼─────────┼─────────┤
│ "north" │ 3       │ 39.75   │
│ "south" │ 3       │ 46.5    │
└─────────┴─────────┴─────────┘
Example (ReshapeUnnest)
points := ursus.Frame(
	ursus.Values("id", []int64{1, 2}),
	ursus.Values("x", []float64{0.5, 2}),
	ursus.Values("y", []float64{1, -1}),
).Select(
	ursus.Col("id"),
	ursus.Struct(ursus.Col("x"), ursus.Col("y")).Alias("point"),
)
show(points)
show(points.Unnest("point"))
Output:
shape: (2, 2)
┌───────┬────────────────────────────────┐
│ id    │ point                          │
│ Int64 │ Struct(x: Float64, y: Float64) │
├───────┼────────────────────────────────┤
│ 1     │ {x: 0.5, y: 1}                 │
│ 2     │ {x: 2, y: -1}                  │
└───────┴────────────────────────────────┘

shape: (2, 3)
┌───────┬─────────┬─────────┐
│ id    │ x       │ y       │
│ Int64 │ Float64 │ Float64 │
├───────┼─────────┼─────────┤
│ 1     │ 0.5     │ 1       │
│ 2     │ 2       │ -1      │
└───────┴─────────┴─────────┘
Example (ReshapeUnpivot)
quarters := ursus.Frame(
	ursus.Values("region", []string{"north", "south"}),
	ursus.Values("q1", []int64{120, 80}),
	ursus.Values("q2", []int64{135, 95}),
)
show(quarters.Unpivot(ursus.UnpivotOptions{
	On:           []string{"q1", "q2"},
	Index:        []string{"region"},
	VariableName: "quarter",
	ValueName:    "sales",
}))
Output:
shape: (4, 3)
┌─────────┬─────────┬───────┐
│ region  │ quarter │ sales │
│ String  │ String  │ Int64 │
├─────────┼─────────┼───────┤
│ "north" │ "q1"    │ 120   │
│ "north" │ "q2"    │ 135   │
│ "south" │ "q1"    │ 80    │
│ "south" │ "q2"    │ 95    │
└─────────┴─────────┴───────┘
Example (ReshapeUnpivotRest)
quarters := ursus.Frame(
	ursus.Values("region", []string{"north", "south"}),
	ursus.Values("q1", []int64{120, 80}),
	ursus.Values("q2", []int64{135, 95}),
	ursus.Values("q3", []int32{150, 90}),
)
show(quarters.Unpivot(ursus.UnpivotOptions{Index: []string{"region"}}))
Output:
shape: (6, 3)
┌─────────┬──────────┬───────┐
│ region  │ variable │ value │
│ String  │ String   │ Int64 │
├─────────┼──────────┼───────┤
│ "north" │ "q1"     │ 120   │
│ "north" │ "q2"     │ 135   │
│ "north" │ "q3"     │ 150   │
│ "south" │ "q1"     │ 80    │
│ "south" │ "q2"     │ 95    │
│ "south" │ "q3"     │ 90    │
└─────────┴──────────┴───────┘
Example (ReshapeUnstack)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	ctx := context.Background()
	df, err := ursus.Frame(
		ursus.Values("reading", []float64{1.5, 2, 2.5, 3, 3.5, 4, 4.5}),
	).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	g, err := df.Unstack(ctx, ursus.UnstackOptions{Step: 3})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(g)
}
Output:
shape: (3, 3)
┌───────────┬───────────┬───────────┐
│ reading_0 │ reading_1 │ reading_2 │
│ Float64   │ Float64   │ Float64   │
├───────────┼───────────┼───────────┤
│ 1.5       │ 3         │ 4.5       │
│ 2         │ 3.5       │ null      │
│ 2.5       │ 4         │ null      │
└───────────┴───────────┴───────────┘
Example (SelectByName)
df, err := sales().
	Select(
		ursus.Col("order_id"),
		ursus.ColRegex("^(qty|price)$"),
		ursus.ColDType(ursus.String),
	).
	Head(3).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 5)
┌──────────┬───────┬─────────┬─────────┬─────────┐
│ order_id │ qty   │ price   │ region  │ product │
│ Int64    │ Int64 │ Float64 │ String  │ String  │
├──────────┼───────┼─────────┼─────────┼─────────┤
│ 1        │ 10    │ 1.5     │ "north" │ "apple" │
│ 2        │ 4     │ 1.5     │ "south" │ "apple" │
│ 3        │ 7     │ 2.25    │ "north" │ "pear"  │
└──────────┴───────┴─────────┴─────────┴─────────┘
Example (SelectDropRename)
df, err := sales().
	Drop("order_id", "date").
	Rename(map[string]string{"qty": "quantity", "price": "unit_price"}).
	Head(3).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 4)
┌─────────┬─────────┬──────────┬────────────┐
│ region  │ product │ quantity │ unit_price │
│ String  │ String  │ Int64    │ Float64    │
├─────────┼─────────┼──────────┼────────────┤
│ "north" │ "apple" │ 10       │ 1.5        │
│ "south" │ "apple" │ 4        │ 1.5        │
│ "north" │ "pear"  │ 7        │ 2.25       │
└─────────┴─────────┴──────────┴────────────┘
Example (SelectExclude)
df, err := sales().
	Select(ursus.Exclude("order_id", "date")).
	Head(3).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 4)
┌─────────┬─────────┬───────┬─────────┐
│ region  │ product │ qty   │ price   │
│ String  │ String  │ Int64 │ Float64 │
├─────────┼─────────┼───────┼─────────┤
│ "north" │ "apple" │ 10    │ 1.5     │
│ "south" │ "apple" │ 4     │ 1.5     │
│ "north" │ "pear"  │ 7     │ 2.25    │
└─────────┴─────────┴───────┴─────────┘
Example (SelectHashRows)
visits := ursus.Frame(
	ursus.Values("user", []string{"ada", "ben", "ada"}),
	ursus.Values("page", []string{"/home", "/home", "/home"}),
)
show(visits.WithColumns(ursus.HashRows(0).Alias("hash")))
Output:
shape: (3, 3)
┌────────┬─────────┬──────────────────────┐
│ user   │ page    │ hash                 │
│ String │ String  │ Uint64               │
├────────┼─────────┼──────────────────────┤
│ "ada"  │ "/home" │ 15332602214641343754 │
│ "ben"  │ "/home" │ 17635480945181825081 │
│ "ada"  │ "/home" │ 15332602214641343754 │
└────────┴─────────┴──────────────────────┘
Example (SelectInsertColumn)
lines := sales().
	Select(ursus.Col("order_id"), ursus.Col("qty"), ursus.Col("price")).
	Head(3)
revenue := ursus.Col("qty").Mul(ursus.Col("price")).Alias("revenue")
show(lines.InsertColumn(1, revenue))
show(lines.InsertColumn(-1, ursus.Values("note", []string{"gift", "", "rush"})))
Output:
shape: (3, 4)
┌──────────┬─────────┬───────┬─────────┐
│ order_id │ revenue │ qty   │ price   │
│ Int64    │ Float64 │ Int64 │ Float64 │
├──────────┼─────────┼───────┼─────────┤
│ 1        │ 15      │ 10    │ 1.5     │
│ 2        │ 6       │ 4     │ 1.5     │
│ 3        │ 15.75   │ 7     │ 2.25    │
└──────────┴─────────┴───────┴─────────┘

shape: (3, 4)
┌──────────┬───────┬────────┬─────────┐
│ order_id │ qty   │ note   │ price   │
│ Int64    │ Int64 │ String │ Float64 │
├──────────┼───────┼────────┼─────────┤
│ 1        │ 10    │ "gift" │ 1.5     │
│ 2        │ 4     │ ""     │ 1.5     │
│ 3        │ 7     │ "rush" │ 2.25    │
└──────────┴───────┴────────┴─────────┘
Example (SelectNames)
c := ursus.Col
df, err := sales().
	Select(
		c("qty", "price").Name().Suffix("_x"),
		c("region").Name().ToUpper(),
		c("qty").Mul(c("price")).Alias("total").Name().Keep(),
	).
	Head(3).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 4)
┌───────┬─────────┬─────────┬─────────┐
│ qty_x │ price_x │ REGION  │ qty     │
│ Int64 │ Float64 │ String  │ Float64 │
├───────┼─────────┼─────────┼─────────┤
│ 10    │ 1.5     │ "north" │ 15      │
│ 4     │ 1.5     │ "south" │ 6       │
│ 7     │ 2.25    │ "north" │ 15.75   │
└───────┴─────────┴─────────┴─────────┘
Example (SelectSelectors)
every := selector.Numeric().Difference(selector.ByName("order_id"))
df, err := sales().Select(every.Expr()).Head(3).Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 2)
┌───────┬─────────┐
│ qty   │ price   │
│ Int64 │ Float64 │
├───────┼─────────┤
│ 10    │ 1.5     │
│ 4     │ 1.5     │
│ 7     │ 2.25    │
└───────┴─────────┘
Example (Slices)
ctx := context.Background()
ids := func(lf *ursus.LazyFrame) []int64 {
	df, err := lf.Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	s, err := df.Column[int64]("order_id")
	if err != nil {
		log.Fatal(err)
	}
	return s.Values()
}
fmt.Println("head 3:     ", ids(sales().Head(3)))
fmt.Println("tail 2:     ", ids(sales().Tail(2)))
fmt.Println("slice 2, 3: ", ids(sales().Slice(2, 3)))
fmt.Println("slice -3, 2:", ids(sales().Slice(-3, 2)))
fmt.Println("reverse:    ", ids(sales().Reverse()))
Output:
head 3:      [1 2 3]
tail 2:      [7 8]
slice 2, 3:  [3 4 5]
slice -3, 2: [6 7]
reverse:     [8 7 6 5 4 3 2 1]
Example (SortKeys)
df, err := sales().
	Sort(ursus.Asc(ursus.Col("region")), ursus.Desc(ursus.Col("qty"))).
	Select(ursus.Col("region"), ursus.Col("product"), ursus.Col("qty")).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (8, 3)
┌─────────┬─────────┬───────┐
│ region  │ product │ qty   │
│ String  │ String  │ Int64 │
├─────────┼─────────┼───────┤
│ "east"  │ "apple" │ 12    │
│ "east"  │ "pear"  │ 2     │
│ "north" │ "apple" │ 10    │
│ "north" │ "pear"  │ 7     │
│ "north" │ "fig"   │ 3     │
│ "south" │ "fig"   │ 9     │
│ "south" │ "pear"  │ 6     │
│ "south" │ "apple" │ 4     │
└─────────┴─────────┴───────┘
Example (SortNulls)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.Frame(
		ursus.ValuesNullable("x", []int64{3, 0, 1, 2}, []bool{true, false, true, true}),
	).Sort(ursus.Desc(ursus.Col("x")).NullsLast()).Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (4, 1)
┌───────┐
│ x     │
│ Int64 │
├───────┤
│ 3     │
│ 2     │
│ 1     │
│ null  │
└───────┘
Example (StrClean)
name := ursus.Col("name").Str()
trimmed := name.StripChars(" ").Str() // a method gives an Expr: Str() again
show(people().Select(
	trimmed.ToTitlecase().Alias("name"),
	trimmed.ToLower().Alias("lower"),
	name.LenChars().Alias("chars"),
	name.LenBytes().Alias("bytes"),
))
Output:
shape: (3, 4)
┌────────────────┬────────────────┬────────┬────────┐
│ name           │ lower          │ chars  │ bytes  │
│ String         │ String         │ Uint32 │ Uint32 │
├────────────────┼────────────────┼────────┼────────┤
│ "Ada Lovelace" │ "ada lovelace" │ 14     │ 14     │
│ "Zoë Smith"    │ "zoë smith"    │ 10     │ 11     │
│ "Ben O'Brien"  │ "ben o'brien"  │ 11     │ 11     │
└────────────────┴────────────────┴────────┴────────┘
Example (StrCombine)
show(sales().Head(3).Select(
	ursus.ConcatStr("/", ursus.Col("region"), ursus.Col("product")).Alias("path"),
	ursus.Format("{} x {}", ursus.Col("qty"), ursus.Col("product")).Alias("line"),
))
show(sales().
	GroupBy(ursus.Col("region")).
	Agg(ursus.Col("product").Str().Join(", ").Alias("products")).
	Sort(ursus.Asc(ursus.Col("region"))))
Output:
shape: (3, 2)
┌───────────────┬──────────────┐
│ path          │ line         │
│ String        │ String       │
├───────────────┼──────────────┤
│ "north/apple" │ "10 x apple" │
│ "south/apple" │ "4 x apple"  │
│ "north/pear"  │ "7 x pear"   │
└───────────────┴──────────────┘

shape: (3, 2)
┌─────────┬────────────────────┐
│ region  │ products           │
│ String  │ String             │
├─────────┼────────────────────┤
│ "east"  │ "pear, apple"      │
│ "north" │ "apple, pear, fig" │
│ "south" │ "apple, fig, pear" │
└─────────┴────────────────────┘
Example (StrExtract)
contact := ursus.Col("contact").Str()
show(people().Select(
	contact.Extract(`([\w.]+)@([\w.]+)`, 2).Alias("domain"),
	contact.ExtractAll(`\d{3}-\d{4}`).Alias("phones"),
))
Output:
shape: (3, 2)
┌────────────────────┬──────────────────────────┐
│ domain             │ phones                   │
│ String             │ List(String)             │
├────────────────────┼──────────────────────────┤
│ "example.com"      │ []                       │
│ "mail.example.org" │ []                       │
│ null               │ ["555-0100", "555-0199"] │
└────────────────────┴──────────────────────────┘
Example (StrMatch)
contact := ursus.Col("contact").Str()
show(people().Select(
	ursus.Col("contact"),
	contact.Contains("@", true).Alias("email"),
	contact.Contains(`\d{3}-\d{4}`, false).Alias("us_phone"),
	contact.EndsWith(".org").Alias("org"),
	contact.CountMatches(`\d`, false).Alias("digits"),
))
Output:
shape: (3, 5)
┌─────────────────────────────────────┬───────┬──────────┬───────┬────────┐
│ contact                             │ email │ us_phone │ org   │ digits │
│ String                              │ Bool  │ Bool     │ Bool  │ Uint32 │
├─────────────────────────────────────┼───────┼──────────┼───────┼────────┤
│ "ada@example.com, +44 20 7946 0958" │ true  │ false    │ false │ 12     │
│ "zoe.smith@mail.example.org"        │ true  │ false    │ true  │ 0      │
│ "call 555-0100 or 555-0199"         │ false │ true     │ false │ 14     │
└─────────────────────────────────────┴───────┴──────────┴───────┴────────┘
Example (StrParse)
joined := ursus.Col("joined").Str()
show(people().Select(
	ursus.Col("joined"),
	joined.Strptime(ursus.Date, "%Y-%m-%d", false).Alias("date"),
	joined.Head(4).Str().ToInteger().Alias("year"),
))
Output:
shape: (3, 3)
┌──────────────┬────────────┬───────┐
│ joined       │ date       │ year  │
│ String       │ Date       │ Int64 │
├──────────────┼────────────┼───────┤
│ "2024-03-01" │ 2024-03-01 │ 2024  │
│ "2025-11-17" │ 2025-11-17 │ 2025  │
│ "bad date"   │ null       │ null  │
└──────────────┴────────────┴───────┘
Example (StrReplace)
contact := ursus.Col("contact").Str()
show(people().Select(
	contact.Replace(`\d`, "#", false).Alias("first_digit"),
	contact.ReplaceAll(`\d`, "#", false).Alias("every_digit"),
))
Output:
shape: (3, 2)
┌─────────────────────────────────────┬─────────────────────────────────────┐
│ first_digit                         │ every_digit                         │
│ String                              │ String                              │
├─────────────────────────────────────┼─────────────────────────────────────┤
│ "ada@example.com, +#4 20 7946 0958" │ "ada@example.com, +## ## #### ####" │
│ "zoe.smith@mail.example.org"        │ "zoe.smith@mail.example.org"        │
│ "call #55-0100 or 555-0199"         │ "call ###-#### or ###-####"         │
└─────────────────────────────────────┴─────────────────────────────────────┘
Example (StrSlice)
code := ursus.Frame(ursus.Values("code", []string{"7", "42", "1234"}))
c := ursus.Col("code").Str()
show(code.Select(
	ursus.Col("code"),
	c.ZFill(5).Alias("zfill"),
	c.PadEnd(5, ".").Alias("pad_end"),
	c.Head(2).Alias("head"),
	c.Slice(1, 2).Alias("slice"),
	c.Reverse().Alias("reverse"),
))
Output:
shape: (3, 6)
┌────────┬─────────┬─────────┬────────┬────────┬─────────┐
│ code   │ zfill   │ pad_end │ head   │ slice  │ reverse │
│ String │ String  │ String  │ String │ String │ String  │
├────────┼─────────┼─────────┼────────┼────────┼─────────┤
│ "7"    │ "00007" │ "7...." │ "7"    │ ""     │ "7"     │
│ "42"   │ "00042" │ "42..." │ "42"   │ "2"    │ "24"    │
│ "1234" │ "01234" │ "1234." │ "12"   │ "23"   │ "4321"  │
└────────┴─────────┴─────────┴────────┴────────┴─────────┘
Example (StrSplit)
show(people().Select(
	ursus.Col("name").Str().StripChars(" ").Str().Split(" ").Alias("words"),
))
show(people().
	Select(ursus.Col("joined").Str().SplitExact("-", 2, false).Alias("parts")).
	Unnest("parts"))
Output:
shape: (3, 1)
┌─────────────────────┐
│ words               │
│ List(String)        │
├─────────────────────┤
│ ["ada", "LOVELACE"] │
│ ["Zoë", "Smith"]    │
│ ["ben", "o'brien"]  │
└─────────────────────┘

shape: (3, 3)
┌────────────┬─────────┬─────────┐
│ field_0    │ field_1 │ field_2 │
│ String     │ String  │ String  │
├────────────┼─────────┼─────────┤
│ "2024"     │ "03"    │ "01"    │
│ "2025"     │ "11"    │ "17"    │
│ "bad date" │ null    │ null    │
└────────────┴─────────┴─────────┘
Example (StructBuild)
show(sales().Head(3).Select(
	ursus.Col("order_id"),
	ursus.Struct(ursus.Col("product"), ursus.Col("qty")).Alias("line"),
))
Output:
shape: (3, 2)
┌──────────┬─────────────────────────────────────┐
│ order_id │ line                                │
│ Int64    │ Struct(product: String, qty: Int64) │
├──────────┼─────────────────────────────────────┤
│ 1        │ {product: "apple", qty: 10}         │
│ 2        │ {product: "apple", qty: 4}          │
│ 3        │ {product: "pear", qty: 7}           │
└──────────┴─────────────────────────────────────┘
Example (StructFields)
line := ursus.Struct(ursus.Col("product"), ursus.Col("qty")).Alias("line")
l := ursus.Col("line").Struct()
lines := sales().Head(2).Select(line)
show(lines.Select(
	l.Field("product").Alias("product"),
	l.WithFields(l.Field("qty").Mul(10).Alias("qty")).Alias("scaled"),
))
show(lines.Select(l.JsonEncode().Alias("json")))
Output:
shape: (2, 2)
┌─────────┬─────────────────────────────────────┐
│ product │ scaled                              │
│ String  │ Struct(product: String, qty: Int64) │
├─────────┼─────────────────────────────────────┤
│ "apple" │ {product: "apple", qty: 100}        │
│ "apple" │ {product: "apple", qty: 40}         │
└─────────┴─────────────────────────────────────┘

shape: (2, 1)
┌──────────────────────────────────────┐
│ json                                 │
│ String                               │
├──────────────────────────────────────┤
│ "{\"product\":\"apple\",\"qty\":10}" │
│ "{\"product\":\"apple\",\"qty\":4}"  │
└──────────────────────────────────────┘
Example (TimeArithmetic)
invoices := ursus.Frame(
	ursus.Values("issued", []string{"2026-01-31", "2026-02-10"}),
	ursus.Values("paid", []string{"2026-02-14", "2026-02-11"}),
).Select(ursus.Col("issued").Str().ToDate(), ursus.Col("paid").Str().ToDate())
issued := ursus.Col("issued").Dt()
show(invoices.Select(
	ursus.Col("issued"),
	ursus.Col("paid").Sub(ursus.Col("issued")).Dt().TotalDays().
		Alias("days_to_pay"),
	issued.OffsetBy(ursus.Every("1mo")).Alias("plus_a_month"),
	issued.MonthEnd().Alias("month_end"),
	issued.AddBusinessDays(5, ursus.RollForward).Alias("plus_5_business"),
))
Output:
shape: (2, 5)
┌────────────┬─────────────┬──────────────┬────────────┬─────────────────┐
│ issued     │ days_to_pay │ plus_a_month │ month_end  │ plus_5_business │
│ Date       │ Int64       │ Date         │ Date       │ Date            │
├────────────┼─────────────┼──────────────┼────────────┼─────────────────┤
│ 2026-01-31 │ 14          │ 2026-02-28   │ 2026-01-31 │ 2026-02-09      │
│ 2026-02-10 │ 1           │ 2026-03-10   │ 2026-02-28 │ 2026-02-17      │
└────────────┴─────────────┴──────────────┴────────────┴─────────────────┘
Example (TimeDynamic)
show(prices().
	Filter(ursus.Col("ticker").Eq("ACME")).
	GroupByDynamic(ursus.Col("date"),
		ursus.DynamicOptions{Every: ursus.Every("1d")}).
	Agg(ursus.Len().Alias("rows"), ursus.Col("close").Mean().Alias("close")))
Output:
shape: (8, 3)
┌────────────┬────────┬─────────┐
│ date       │ rows   │ close   │
│ Date       │ Uint64 │ Float64 │
├────────────┼────────┼─────────┤
│ 2026-01-05 │ 1      │ 10      │
│ 2026-01-06 │ 1      │ 11      │
│ 2026-01-07 │ 1      │ 10.5    │
│ 2026-01-08 │ 1      │ 12      │
│ 2026-01-09 │ 1      │ 12      │
│ 2026-01-10 │ 0      │ null    │
│ 2026-01-11 │ 0      │ null    │
│ 2026-01-12 │ 1      │ 13.5    │
└────────────┴────────┴─────────┘
Example (TimeDynamicWeeks)
show(sales().
	GroupByDynamic(ursus.Col("date"), ursus.DynamicOptions{
		Every:   ursus.Every("1w"),
		GroupBy: []ursus.Expr{ursus.Col("region")},
	}).
	Agg(ursus.Col("qty").Sum().Alias("qty")).
	Sort(ursus.Asc(ursus.Col("region")), ursus.Asc(ursus.Col("date"))))
Output:
shape: (5, 3)
┌────────────┬─────────┬────────┐
│ date       │ region  │ qty    │
│ Date       │ String  │ Int128 │
├────────────┼─────────┼────────┤
│ 2026-01-05 │ "east"  │ 14     │
│ 2025-12-29 │ "north" │ 17     │
│ 2026-01-05 │ "north" │ 3      │
│ 2025-12-29 │ "south" │ 4      │
│ 2026-01-05 │ "south" │ 15     │
└────────────┴─────────┴────────┘
Example (TimeIntegerIndex)
readings := ursus.Frame(
	ursus.Values("step", []int64{1, 2, 4, 7, 8, 12}),
	ursus.Values("load", []float64{3, 5, 2, 6, 4, 7}),
)
fives := ursus.DynamicOptions{Every: ursus.Every("5i")}
show(readings.GroupByDynamic(ursus.Col("step"), fives).
	Agg(ursus.Len().Alias("rows"), ursus.Col("load").Sum()))
Output:
shape: (3, 3)
┌───────┬────────┬─────────┐
│ step  │ rows   │ load    │
│ Int64 │ Uint64 │ Float64 │
├───────┼────────┼─────────┤
│ 0     │ 3      │ 10      │
│ 5     │ 2      │ 10      │
│ 10    │ 1      │ 7       │
└───────┴────────┴─────────┘
Example (TimeParse)
logs := ursus.Frame(
	ursus.Values("at", []string{"2026-03-08 06:30", "2026-03-08 07:45", "oops"}),
)
utc := ursus.Datetime(ursus.Micro, "UTC")
show(logs.Select(
	ursus.Col("at"),
	ursus.Col("at").Str().Strptime(utc, "%Y-%m-%d %H:%M", false).Alias("instant"),
))
Output:
shape: (3, 2)
┌────────────────────┬─────────────────────────────┐
│ at                 │ instant                     │
│ String             │ Datetime(us, UTC)           │
├────────────────────┼─────────────────────────────┤
│ "2026-03-08 06:30" │ 2026-03-08T06:30:00.000000Z │
│ "2026-03-08 07:45" │ 2026-03-08T07:45:00.000000Z │
│ "oops"             │ null                        │
└────────────────────┴─────────────────────────────┘
Example (TimeParts)
d := ursus.Col("day").Dt()
show(days().Select(
	ursus.Col("day"),
	d.Year().Alias("year"),
	d.Month().Alias("month"),
	d.Weekday().Alias("weekday"),
	d.Week().Alias("week"),
	d.Quarter().Alias("quarter"),
	d.OrdinalDay().Alias("day_of_year"),
	d.IsLeapYear().Alias("leap"),
))
Output:
shape: (3, 8)
┌────────────┬───────┬───────┬─────────┬───────┬─────────┬─────────────┬───────┐
│ day        │ year  │ month │ weekday │ week  │ quarter │ day_of_year │ leap  │
│ Date       │ Int32 │ Int32 │ Int32   │ Int32 │ Int32   │ Int32       │ Bool  │
├────────────┼───────┼───────┼─────────┼───────┼─────────┼─────────────┼───────┤
│ 2024-02-29 │ 2024  │ 2     │ 4       │ 9     │ 1       │ 60          │ true  │
│ 2026-01-03 │ 2026  │ 1     │ 6       │ 1     │ 1       │ 3           │ false │
│ 2026-12-31 │ 2026  │ 12    │ 4       │ 53    │ 4       │ 365         │ false │
└────────────┴───────┴───────┴─────────┴───────┴─────────┴─────────────┴───────┘
Example (TimeRanges)
from := ursus.Lit(time.Date(2026, 1, 30, 0, 0, 0, 0, time.UTC)).Cast(ursus.Date)
to := ursus.Lit(time.Date(2026, 2, 2, 0, 0, 0, 0, time.UTC)).Cast(ursus.Date)
span := ursus.DateRange(from, to, ursus.Every("1d")).Alias("day")
show(ursus.Frame().Select(span))
Output:
shape: (4, 1)
┌────────────┐
│ day        │
│ Date       │
├────────────┤
│ 2026-01-30 │
│ 2026-01-31 │
│ 2026-02-01 │
│ 2026-02-02 │
└────────────┘
Example (TimeRolling)
show(prices().
	Filter(ursus.Col("ticker").Eq("ACME")).
	Rolling(ursus.Col("date"), ursus.RollingOptions{Period: ursus.Every("3d")}).
	Agg(ursus.Len().Alias("rows"), ursus.Col("close").Max().Alias("high_3d")))
Output:
shape: (6, 3)
┌────────────┬────────┬─────────┐
│ date       │ rows   │ high_3d │
│ Date       │ Uint64 │ Float64 │
├────────────┼────────┼─────────┤
│ 2026-01-05 │ 1      │ 10      │
│ 2026-01-06 │ 2      │ 11      │
│ 2026-01-07 │ 3      │ 11      │
│ 2026-01-08 │ 3      │ 12      │
│ 2026-01-09 │ 3      │ 12      │
│ 2026-01-12 │ 1      │ 13.5    │
└────────────┴────────┴─────────┘
Example (TimeTruncate)
date := ursus.Col("date").Dt()
show(sales().Select(
	ursus.Col("date"),
	date.Truncate(ursus.Every("1w")).Alias("week"),
	date.Truncate(ursus.Every("1mo")).Alias("month"),
	date.Strftime("%a %d %b").Alias("label"),
).Unique())
Output:
shape: (5, 4)
┌────────────┬────────────┬────────────┬──────────────┐
│ date       │ week       │ month      │ label        │
│ Date       │ Date       │ Date       │ String       │
├────────────┼────────────┼────────────┼──────────────┤
│ 2026-01-03 │ 2025-12-29 │ 2026-01-01 │ "Sat 03 Jan" │
│ 2026-01-04 │ 2025-12-29 │ 2026-01-01 │ "Sun 04 Jan" │
│ 2026-01-05 │ 2026-01-05 │ 2026-01-01 │ "Mon 05 Jan" │
│ 2026-01-06 │ 2026-01-05 │ 2026-01-01 │ "Tue 06 Jan" │
│ 2026-01-07 │ 2026-01-05 │ 2026-01-01 │ "Wed 07 Jan" │
└────────────┴────────────┴────────────┴──────────────┘
Example (TimeUpsample)
show(prices().
	Filter(ursus.Col("ticker").Eq("ACME")).
	Upsample("date", ursus.Every("1d"), "ticker").
	WithColumns(ursus.Col("close").FillNull(ursus.FillForward).Alias("filled")))
Output:
shape: (8, 4)
┌────────────┬────────┬─────────┬─────────┐
│ date       │ ticker │ close   │ filled  │
│ Date       │ String │ Float64 │ Float64 │
├────────────┼────────┼─────────┼─────────┤
│ 2026-01-05 │ "ACME" │ 10      │ 10      │
│ 2026-01-06 │ "ACME" │ 11      │ 11      │
│ 2026-01-07 │ "ACME" │ 10.5    │ 10.5    │
│ 2026-01-08 │ "ACME" │ 12      │ 12      │
│ 2026-01-09 │ "ACME" │ 12      │ 12      │
│ 2026-01-10 │ "ACME" │ null    │ 12      │
│ 2026-01-11 │ "ACME" │ null    │ 12      │
│ 2026-01-12 │ "ACME" │ 13.5    │ 13.5    │
└────────────┴────────┴─────────┴─────────┘
Example (TimeZones)
at := []time.Time{
	time.Date(2026, 3, 8, 6, 30, 0, 0, time.UTC),
	time.Date(2026, 3, 8, 7, 30, 0, 0, time.UTC),
}
instants := ursus.Frame(ursus.Values("utc", at)).
	Select(ursus.Col("utc").Cast(ursus.Datetime(ursus.Second, "UTC")))
show(instants.Select(
	ursus.Col("utc"),
	ursus.Col("utc").Dt().ConvertTimeZone("America/New_York").Alias("new_york"),
	ursus.Col("utc").Dt().ReplaceTimeZone("Asia/Tokyo").Alias("same_wall_in_tokyo"),
))
Output:
shape: (2, 3)
┌──────────────────────┬───────────────────────────────┬───────────────────────────┐
│ utc                  │ new_york                      │ same_wall_in_tokyo        │
│ Datetime(s, UTC)     │ Datetime(s, America/New_York) │ Datetime(s, Asia/Tokyo)   │
├──────────────────────┼───────────────────────────────┼───────────────────────────┤
│ 2026-03-08T06:30:00Z │ 2026-03-08T01:30:00-05:00     │ 2026-03-08T06:30:00+09:00 │
│ 2026-03-08T07:30:00Z │ 2026-03-08T03:30:00-04:00     │ 2026-03-08T07:30:00+09:00 │
└──────────────────────┴───────────────────────────────┴───────────────────────────┘
Example (TopK)
df, err := sales().
	TopK(3, ursus.Asc(ursus.Col("qty"))).
	Select(ursus.Col("order_id"), ursus.Col("product"), ursus.Col("qty")).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 3)
┌──────────┬─────────┬───────┐
│ order_id │ product │ qty   │
│ Int64    │ String  │ Int64 │
├──────────┼─────────┼───────┤
│ 6        │ "apple" │ 12    │
│ 1        │ "apple" │ 10    │
│ 5        │ "fig"   │ 9     │
└──────────┴─────────┴───────┘
Example (TypesCast)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	ctx := context.Background()
	text := ursus.Frame(ursus.Values("n", []string{"12", "7", "seven"}))

	_, err := text.Select(ursus.Col("n").Cast(ursus.Int64)).Collect(ctx)
	fmt.Println(err)

	df, err := text.Select(ursus.Col("n").CastLossy(ursus.Int64)).Collect(ctx)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
ursus: cast: cannot parse "seven" as Int64 at row 2
  use CastLossy to turn unparseable values into nulls instead
shape: (3, 1)
┌───────┐
│ n     │
│ Int64 │
├───────┤
│ 12    │
│ 7     │
│ null  │
└───────┘
Example (TypesFrameCast)
lines := sales().
	Select(ursus.Col("order_id"), ursus.Col("qty"), ursus.Col("price")).
	Head(2)
show(lines.Cast(map[string]ursus.DataType{
	"qty":   ursus.Int32,
	"price": ursus.Float32,
}))
show(lines.CastTypes(map[ursus.DataType]ursus.DataType{ursus.Int64: ursus.Int16}))
show(lines.Select(ursus.Col("price").Truncate(0).Cast(ursus.Int64)))
Output:
shape: (2, 3)
┌──────────┬───────┬─────────┐
│ order_id │ qty   │ price   │
│ Int64    │ Int32 │ Float32 │
├──────────┼───────┼─────────┤
│ 1        │ 10    │ 1.5     │
│ 2        │ 4     │ 1.5     │
└──────────┴───────┴─────────┘

shape: (2, 3)
┌──────────┬───────┬─────────┐
│ order_id │ qty   │ price   │
│ Int16    │ Int16 │ Float64 │
├──────────┼───────┼─────────┤
│ 1        │ 10    │ 1.5     │
│ 2        │ 4     │ 1.5     │
└──────────┴───────┴─────────┘

shape: (2, 1)
┌───────┐
│ price │
│ Int64 │
├───────┤
│ 1     │
│ 1     │
└───────┘
Example (TypesMatchToSchema)
want := ursus.MustSchema(
	ursus.Of("order_id", ursus.Int64),
	ursus.Of("qty", ursus.Int64),
	ursus.Of("discount", ursus.Float64),
)
drifted := sales().Head(2).Select(
	ursus.Col("qty").Cast(ursus.Int32), ursus.Col("order_id"), ursus.Col("product"))

_, err := drifted.
	MatchToSchema(want, ursus.MatchOptions{}).
	Collect(context.Background())
fmt.Println(err)

show(drifted.MatchToSchema(want, ursus.MatchOptions{
	MissingColumns: ursus.MatchInsert,
	ExtraColumns:   ursus.MatchIgnore,
	IntegerCast:    ursus.MatchUpcast,
}))
Output:
ursus: match_to_schema: qty is Int32, and the schema's is Int64
  MatchOptions{IntegerCast: ursus.MatchUpcast} widens it
shape: (2, 3)
┌──────────┬───────┬──────────┐
│ order_id │ qty   │ discount │
│ Int64    │ Int64 │ Float64  │
├──────────┼───────┼──────────┤
│ 1        │ 10    │ null     │
│ 2        │ 4     │ null     │
└──────────┴───────┴──────────┘
Example (TypesNaN)
package main

import (
	"context"
	"fmt"
	"log"
	"math"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.Frame(
		ursus.ValuesNullable("x",
			[]float64{1, math.NaN(), 0},
			[]bool{true, true, false}),
	).Select(
		ursus.Col("x"),
		ursus.Col("x").IsNull().Alias("is_null"),
		ursus.Col("x").IsNan().Alias("is_nan"),
	).Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (3, 3)
┌─────────┬─────────┬────────┐
│ x       │ is_null │ is_nan │
│ Float64 │ Bool    │ Bool   │
├─────────┼─────────┼────────┤
│ 1       │ false   │ false  │
│ NaN     │ false   │ true   │
│ null    │ true    │ null   │
└─────────┴─────────┴────────┘
Example (TypesNulls)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	df, err := ursus.Frame(
		ursus.ValuesNullable("score", []float64{4, 0, 8}, []bool{true, false, true}),
	).Select(
		ursus.Len().Alias("rows"),
		ursus.Col("score").Count().Alias("count"),
		ursus.Col("score").NullCount().Alias("nulls"),
		ursus.Col("score").Mean().Alias("mean"),
		ursus.Col("score").FillNullWith(0.0).Mean().Alias("mean_as_zero"),
	).Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (1, 5)
┌────────┬────────┬────────┬─────────┬──────────────┐
│ rows   │ count  │ nulls  │ mean    │ mean_as_zero │
│ Uint64 │ Uint64 │ Uint64 │ Float64 │ Float64      │
├────────┼────────┼────────┼─────────┼──────────────┤
│ 3      │ 2      │ 1      │ 6       │ 4            │
└────────┴────────┴────────┴─────────┴──────────────┘
Example (TypesSchema)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// sales is the CSV the concept pages read, its date column a Date.
func sales() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/sales.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	df, err := sales().Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df.Schema())
}
Output:
{order_id: Int64, date: Date, region: String, product: String, qty: Int64, price: Float64}
Example (UdfBatches)
orZero := ursus.Col("temp").MapBatches("or_zero", ursus.Float64,
	func(vals []float64, valid []bool) ([]float64, []bool, error) {
		out, ok := make([]float64, len(vals)), make([]bool, len(vals))
		for i := range vals {
			if valid[i] {
				out[i] = vals[i]
			}
			ok[i] = true // a null becomes 0, a value of its own
		}
		return out, ok, nil
	})
show(readings().Select(ursus.Col("temp"), orZero.Alias("or_zero")))
Output:
shape: (6, 2)
┌─────────┬─────────┐
│ temp    │ or_zero │
│ Float64 │ Float64 │
├─────────┼─────────┤
│ 10      │ 10      │
│ null    │ 0       │
│ null    │ 0       │
│ 13      │ 13      │
│ 12      │ 12      │
│ null    │ 0       │
└─────────┴─────────┘
Example (UdfElements)
slug := ursus.Col("product").MapElements("slug", ursus.String,
	func(s string) (string, error) {
		return strings.ToUpper(s[:1]) + "-" + fmt.Sprint(len(s)), nil
	})
show(sales().Head(3).Select(ursus.Col("product"), slug.Alias("slug")))
Output:
shape: (3, 2)
┌─────────┬────────┐
│ product │ slug   │
│ String  │ String │
├─────────┼────────┤
│ "apple" │ "A-5"  │
│ "apple" │ "A-5"  │
│ "pear"  │ "P-4"  │
└─────────┴────────┘
Example (UdfError)
dozens := ursus.Col("qty").MapElements("dozens", ursus.Float64,
	func(q int64) (float64, error) {
		if q > 10 {
			return 0, errors.New("more than ten is a wholesale order")
		}
		return float64(q) / 12, nil
	})
_, err := sales().Select(dozens).Collect(context.Background())
fmt.Println(err)
Output:
ursus: map_elements: udf "dozens" failed on 12, row 5 of its batch of column "qty"
  caused by: more than ten is a wholesale order
Example (UdfExplain)
slug := ursus.Col("product").MapElements("slug", ursus.String,
	func(s string) (string, error) { return strings.ToUpper(s), nil })
plan, err := sales().
	Filter(ursus.Col("qty").Gt(5)).
	Select(slug).
	Explain(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(plan)
Output:
PROJECT [col("product").map_elements("slug" -> String)]
  FILTER [(col("qty") > lit(5))]
    CSV SCAN testdata/sales.csv
      projection: [product, qty] (2/6 cols)
Example (UdfNulls)
var calls atomic.Int64
double := ursus.Col("temp").MapElements("double", ursus.Float64,
	func(t float64) (float64, error) {
		calls.Add(1)
		return 2 * t, nil
	})
show(readings().Select(ursus.Col("temp"), double.Alias("doubled")))
fmt.Println("calls:", calls.Load())
Output:
shape: (6, 2)
┌─────────┬─────────┐
│ temp    │ doubled │
│ Float64 │ Float64 │
├─────────┼─────────┤
│ 10      │ 20      │
│ null    │ null    │
│ null    │ null    │
│ 13      │ 26      │
│ 12      │ 24      │
│ null    │ null    │
└─────────┴─────────┘

calls: 3
Example (UdfTypes)
band := ursus.Col("qty").MapElements("band", ursus.String,
	func(q int64) (string, error) {
		switch {
		case q >= 10:
			return "large", nil
		case q >= 5:
			return "medium", nil
		}
		return "small", nil
	})
show(sales().
	GroupBy(band.Alias("band")).
	Agg(ursus.Len().Alias("orders")).
	Sort(ursus.Asc(ursus.Col("band"))))
Output:
shape: (3, 2)
┌──────────┬────────┐
│ band     │ orders │
│ String   │ Uint64 │
├──────────┼────────┤
│ "large"  │ 2      │
│ "medium" │ 3      │
│ "small"  │ 3      │
└──────────┴────────┘
Example (Unique)
df, err := sales().
	Unique("region").
	Select(ursus.Col("region"), ursus.Col("order_id")).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 2)
┌─────────┬──────────┐
│ region  │ order_id │
│ String  │ Int64    │
├─────────┼──────────┤
│ "north" │ 1        │
│ "south" │ 2        │
│ "east"  │ 4        │
└─────────┴──────────┘
Example (UniqueKeepLast)
df, err := sales().
	UniqueKeep(ursus.KeepLast, "region").
	Select(ursus.Col("region"), ursus.Col("order_id")).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 2)
┌─────────┬──────────┐
│ region  │ order_id │
│ String  │ Int64    │
├─────────┼──────────┤
│ "east"  │ 6        │
│ "north" │ 7        │
│ "south" │ 8        │
└─────────┴──────────┘
Example (WhereExists)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// customers and orders are the joins chapter's frames: customer 2 has no order, and
// order 13's customer, 5, is no customer.
func customers() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("customer_id", []int64{1, 2, 3, 4}),
		ursus.Values("name", []string{"Ada", "Ben", "Cai", "Dee"}),
	)
}

func orders() *ursus.LazyFrame {
	return ursus.Frame(
		ursus.Values("order", []int64{10, 11, 12, 13, 14}),
		ursus.Values("customer_id", []int64{1, 3, 1, 5, 4}),
		ursus.Values("amount", []float64{25, 40, 10, 99, 15}),
	)
}

// show collects a query and prints it.
func show(lf *ursus.LazyFrame) {
	df, err := lf.Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}

func main() {
	show(customers().WhereExists(orders(),
		ursus.Col("customer_id").Eq(ursus.Col("customer_id_right")),
		ursus.Col("amount").Gt(20.0)))
}
Output:
shape: (2, 2)
┌─────────────┬────────┐
│ customer_id │ name   │
│ Int64       │ String │
├─────────────┼────────┤
│ 1           │ "Ada"  │
│ 3           │ "Cai"  │
└─────────────┴────────┘
Example (WindowCumulative)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// prices is the windows chapter's series: two tickers' closes over six trading days,
// a weekend between the fifth and the sixth.
func prices() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/prices.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	byTicker := ursus.WindowSpec{
		PartitionBy: []ursus.Expr{ursus.Col("ticker")},
		OrderBy:     []ursus.SortKey{ursus.Asc(ursus.Col("date"))},
	}
	df, err := prices().
		WithColumns(
			ursus.Col("close").CumMax(false).OverWith(byTicker).Alias("high"),
			ursus.Col("close").CumCount(false).OverWith(byTicker).Alias("day"),
		).
		Filter(ursus.Col("ticker").Eq("ACME")).
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (6, 5)
┌────────────┬────────┬─────────┬─────────┬────────┐
│ date       │ ticker │ close   │ high    │ day    │
│ Date       │ String │ Float64 │ Float64 │ Uint32 │
├────────────┼────────┼─────────┼─────────┼────────┤
│ 2026-01-05 │ "ACME" │ 10      │ 10      │ 1      │
│ 2026-01-06 │ "ACME" │ 11      │ 11      │ 2      │
│ 2026-01-07 │ "ACME" │ 10.5    │ 11      │ 3      │
│ 2026-01-08 │ "ACME" │ 12      │ 12      │ 4      │
│ 2026-01-09 │ "ACME" │ 12      │ 12      │ 5      │
│ 2026-01-12 │ "ACME" │ 13.5    │ 13.5    │ 6      │
└────────────┴────────┴─────────┴─────────┴────────┘
Example (WindowEwm)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// prices is the windows chapter's series: two tickers' closes over six trading days,
// a weekend between the fifth and the sixth.
func prices() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/prices.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	t, c := ursus.Col("ticker"), ursus.Col("close")
	df, err := prices().
		WithColumns(
			c.EwmMean(ursus.EwmAlpha(0.5)).Over(t).Round(4).Alias("ewm"),
			c.EwmMean(ursus.EwmAlpha(0.5), ursus.EwmAdjust(false)).Over(t).Round(4).
				Alias("ewm_unadjusted"),
		).
		Filter(t.Eq("ACME")).
		Drop("ticker").
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (6, 4)
┌────────────┬─────────┬─────────┬────────────────┐
│ date       │ close   │ ewm     │ ewm_unadjusted │
│ Date       │ Float64 │ Float64 │ Float64        │
├────────────┼─────────┼─────────┼────────────────┤
│ 2026-01-05 │ 10      │ 10      │ 10             │
│ 2026-01-06 │ 11      │ 10.6667 │ 10.5           │
│ 2026-01-07 │ 10.5    │ 10.5714 │ 10.5           │
│ 2026-01-08 │ 12      │ 11.3333 │ 11.25          │
│ 2026-01-09 │ 12      │ 11.6774 │ 11.625         │
│ 2026-01-12 │ 13.5    │ 12.6032 │ 12.5625        │
└────────────┴─────────┴─────────┴────────────────┘
Example (WindowOver)
qty, region := ursus.Col("qty"), ursus.Col("region")
df, err := sales().
	Select(
		ursus.Col("order_id"),
		region,
		qty,
		qty.Sum().Over(region).Alias("region_qty"),
		qty.Div(qty.Sum().Over(region)).Round(2).Alias("share"),
		qty.Sum().Over().Alias("all_qty"),
	).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (8, 6)
┌──────────┬─────────┬───────┬────────────┬─────────┬─────────┐
│ order_id │ region  │ qty   │ region_qty │ share   │ all_qty │
│ Int64    │ String  │ Int64 │ Int128     │ Float64 │ Int128  │
├──────────┼─────────┼───────┼────────────┼─────────┼─────────┤
│ 1        │ "north" │ 10    │ 20         │ 0.5     │ 53      │
│ 2        │ "south" │ 4     │ 19         │ 0.21    │ 53      │
│ 3        │ "north" │ 7     │ 20         │ 0.35    │ 53      │
│ 4        │ "east"  │ 2     │ 14         │ 0.14    │ 53      │
│ 5        │ "south" │ 9     │ 19         │ 0.47    │ 53      │
│ 6        │ "east"  │ 12    │ 14         │ 0.86    │ 53      │
│ 7        │ "north" │ 3     │ 20         │ 0.15    │ 53      │
│ 8        │ "south" │ 6     │ 19         │ 0.32    │ 53      │
└──────────┴─────────┴───────┴────────────┴─────────┴─────────┘
Example (WindowRank)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

func main() {
	x := ursus.Col("points")
	df, err := ursus.Frame(
		ursus.Values("team", []string{"a", "b", "c", "d", "e"}),
		ursus.Values("points", []int64{7, 9, 7, 3, 9}),
	).WithColumns(
		x.Rank(ursus.RankOrdinal, true).Alias("ordinal"),
		x.Rank(ursus.RankMin, true).Alias("min"),
		x.Rank(ursus.RankMax, true).Alias("max"),
		x.Rank(ursus.RankDense, true).Alias("dense"),
		x.Rank(ursus.RankAverage, true).Alias("average"),
	).Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (5, 7)
┌────────┬────────┬─────────┬────────┬────────┬────────┬─────────┐
│ team   │ points │ ordinal │ min    │ max    │ dense  │ average │
│ String │ Int64  │ Uint32  │ Uint32 │ Uint32 │ Uint32 │ Float64 │
├────────┼────────┼─────────┼────────┼────────┼────────┼─────────┤
│ "a"    │ 7      │ 3       │ 3      │ 4      │ 2      │ 3.5     │
│ "b"    │ 9      │ 1       │ 1      │ 2      │ 1      │ 1.5     │
│ "c"    │ 7      │ 4       │ 3      │ 4      │ 2      │ 3.5     │
│ "d"    │ 3      │ 5       │ 5      │ 5      │ 3      │ 5       │
│ "e"    │ 9      │ 2       │ 1      │ 2      │ 1      │ 1.5     │
└────────┴────────┴─────────┴────────┴────────┴────────┴─────────┘
Example (WindowRolling)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// prices is the windows chapter's series: two tickers' closes over six trading days,
// a weekend between the fifth and the sixth.
func prices() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/prices.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	t, c := ursus.Col("ticker"), ursus.Col("close")
	df, err := prices().
		WithColumns(
			c.RollingMean(3).Over(t).Alias("mean3"),
			c.RollingMean(3, ursus.MinSamples(1)).Over(t).Alias("mean3_min1"),
			c.RollingMax(3, ursus.RollingCenter()).Over(t).Alias("max3_centred"),
		).
		Filter(t.Eq("ACME")).
		Drop("ticker").
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (6, 5)
┌────────────┬─────────┬────────────────────┬────────────────────┬──────────────┐
│ date       │ close   │ mean3              │ mean3_min1         │ max3_centred │
│ Date       │ Float64 │ Float64            │ Float64            │ Float64      │
├────────────┼─────────┼────────────────────┼────────────────────┼──────────────┤
│ 2026-01-05 │ 10      │ null               │ 10                 │ null         │
│ 2026-01-06 │ 11      │ null               │ 10.5               │ 11           │
│ 2026-01-07 │ 10.5    │ 10.5               │ 10.5               │ 12           │
│ 2026-01-08 │ 12      │ 11.166666666666666 │ 11.166666666666666 │ 12           │
│ 2026-01-09 │ 12      │ 11.5               │ 11.5               │ 13.5         │
│ 2026-01-12 │ 13.5    │ 12.5               │ 12.5               │ null         │
└────────────┴─────────┴────────────────────┴────────────────────┴──────────────┘
Example (WindowRollingBy)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// prices is the windows chapter's series: two tickers' closes over six trading days,
// a weekend between the fifth and the sixth.
func prices() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/prices.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	t, c := ursus.Col("ticker"), ursus.Col("close")
	df, err := prices().
		WithColumns(
			c.RollingSum(3, ursus.MinSamples(1)).Over(t).Alias("last_3_rows"),
			c.RollingSumBy(ursus.Col("date"), ursus.Every("3d")).Over(t).
				Alias("last_3_days"),
		).
		Filter(t.Eq("ACME")).
		Drop("ticker").
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (6, 4)
┌────────────┬─────────┬─────────────┬─────────────┐
│ date       │ close   │ last_3_rows │ last_3_days │
│ Date       │ Float64 │ Float64     │ Float64     │
├────────────┼─────────┼─────────────┼─────────────┤
│ 2026-01-05 │ 10      │ 10          │ 10          │
│ 2026-01-06 │ 11      │ 21          │ 21          │
│ 2026-01-07 │ 10.5    │ 31.5        │ 31.5        │
│ 2026-01-08 │ 12      │ 33.5        │ 33.5        │
│ 2026-01-09 │ 12      │ 34.5        │ 34.5        │
│ 2026-01-12 │ 13.5    │ 37.5        │ 13.5        │
└────────────┴─────────┴─────────────┴─────────────┘
Example (WindowShift)
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/advenn/ursus"
)

// prices is the windows chapter's series: two tickers' closes over six trading days,
// a weekend between the fifth and the sixth.
func prices() *ursus.LazyFrame {
	return ursus.ScanCSV("testdata/prices.csv",
		ursus.WithSchemaOverrides(map[string]ursus.DataType{"date": ursus.Date}))
}

func main() {
	t := ursus.Col("ticker")
	df, err := prices().
		WithColumns(
			ursus.Col("close").Shift(1).Over(t).Alias("previous"),
			ursus.Col("close").Diff(1).Over(t).Alias("change"),
			ursus.Col("close").PctChange(1).Over(t).Round(3).Alias("pct"),
		).
		Filter(t.Eq("BOLT")).
		Drop("ticker").
		Collect(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(df)
}
Output:
shape: (6, 5)
┌────────────┬─────────┬──────────┬─────────┬─────────┐
│ date       │ close   │ previous │ change  │ pct     │
│ Date       │ Float64 │ Float64  │ Float64 │ Float64 │
├────────────┼─────────┼──────────┼─────────┼─────────┤
│ 2026-01-05 │ 20      │ null     │ null    │ null    │
│ 2026-01-06 │ 19      │ 20       │ -1      │ -0.05   │
│ 2026-01-07 │ 21      │ 19       │ 2       │ 0.105   │
│ 2026-01-08 │ 21      │ 21       │ 0       │ 0       │
│ 2026-01-09 │ 22      │ 21       │ 1       │ 0.048   │
│ 2026-01-12 │ 20      │ 22       │ -2      │ -0.091  │
└────────────┴─────────┴──────────┴─────────┴─────────┘
Example (WithRowIndex)
df, err := sales().
	Filter(ursus.Col("region").Eq("south")).
	WithRowIndex("n", 1).
	Select(ursus.Col("n"), ursus.Col("order_id"), ursus.Col("product")).
	Collect(context.Background())
if err != nil {
	log.Fatal(err)
}
fmt.Println(df)
Output:
shape: (3, 3)
┌────────┬──────────┬─────────┐
│ n      │ order_id │ product │
│ Uint32 │ Int64    │ String  │
├────────┼──────────┼─────────┤
│ 1      │ 2        │ "apple" │
│ 2      │ 5        │ "fig"   │
│ 3      │ 8        │ "pear"  │
└────────┴──────────┴─────────┘

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL