Language guide

Table expressions and analysis operations

Detailed usage notes for the operations listed in the language reference. For exact grammar and edge-case semantics, use the specification.

Transform tables with ordered clauses

Attach a comma-separated clause list to a table. Each clause receives the result of the previous clause. select projects fields or computes aggregations; update adds or replaces fields; filter retains rows; by changes the aggregation context; and order sorts the result.

let totals = trades[
  update { notional = price * size },
  filter notional > 1000,
  select { total = sum(notional), n = count() },
  by symbol,
  order { total desc }
];

Use rename, distinct, head, and tail for common schema and row-shape operations. A conditional where predicate update { ... } replaces values only on matching rows.

When an operation is inherently row-wise, map { name = expr, ... } evaluates scalar fields in input order and returns exactly those fields. It may contain effectful extern calls, such as reading and converting each path returned by fs::list. Bind the map result before applying another clause.

Joins, binding, and reshaping

Joins

Use inner, left, right, outer, semi, anti, cross, and as-of joins. Join on a shared key or map different key names with on { left_key = right_key }.

Binding

Append compatible tables with row binding, or combine columns with column binding when row alignment is intentional.

Reshaping

Use melt to convert wide data to long form and dcast to pivot long data back to a wide table.

let enriched = (trades left join symbols on symbol)[
  select { symbol, sector, price, size }
];

Aggregates, windows, and ordered calculations

Aggregate expressions include count, sum, mean, min, max, first, last, median, quantiles, standard deviation, and correlation. With by, they run per group; without it, they reduce the whole table. Grouped update broadcasts a group result back to each row.

For ordered data, use lag, lead, rank functions, cumulative sums and products. Promote an ordered table with as_timeframe, then use window duration for rolling aggregates or resample duration for fixed time buckets.

let tf = as_timeframe(ticks, "ts");
tf[window 5m, update {
  vwap = sum(price * size) / sum(size),
  volume_5m = rolling_sum(size)
}, by symbol];

Types, nulls, and reusable logic

Types

Ibex has static scalar, Series, Table, and TimeFrame types. Use explicit constructors such as Int64(x) and Float64(x) where conversion is required.

Nulls

Columns and scalar bindings can be null, and predicates use three-valued logic. A null or empty optional lookup stays null through casts and expressions; use coalesce, is_null, fill_null, fill_forward, and fill_backward to inspect or recover it.

Functions

Declare typed fn functions for reusable expressions. case chooses the first matching branch. Row-wise map { ... } performs ordered scalar work; map x in names => ... separately expands fields at compile time inside select and update.

Related guides

I/O guide

CSV, Parquet, databases, and streams.