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 }.
Language guide
Detailed usage notes for the operations listed in the language reference. For exact grammar and edge-case semantics, use the specification.
Pipelines
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.
Combining tables
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 }.
Append compatible tables with row binding, or combine columns with column binding when row alignment is intentional.
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 }
];
Analytics
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];
Values and expressions
Ibex has static scalar, Series, Table, and TimeFrame types. Use explicit constructors such as Int64(x) and Float64(x) where conversion is required.
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.
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.
More detail
Built-in signatures and examples.
CSV, Parquet, databases, and streams.
A first end-to-end pipeline.