Skip to content

Tables ​

The Table class provides a tabular data structure for structured data processing. Tables are the primary data type returned by read() when loading CSV, JSON arrays, and other tabular formats.

For reading and writing table data, see the I/O guide. For data pipelines and the data {} block, see Tables & Data.

Standalone Functions

Table operations are standalone, pipe-friendly functions: tableWhere(), tableSelect(), tableDerive(), etc. Use the pipe operator |> to chain them, or call them directly with the table as the first argument. See the Data Tutorials for hands-on walkthroughs.


Constructor ​

Table ​

tova
Table(rows, columns?) -> Table

Creates a table from an array of row objects. Optionally specify column names.

tova
t = Table([
  { name: "Alice", age: 30, city: "NYC" },
  { name: "Bob", age: 25, city: "LA" },
  { name: "Carol", age: 35, city: "NYC" }
])

Properties ​

rows ​

tova
t.rows -> Int

The number of rows in the table.

length ​

tova
t.length -> Int

Alias for rows. The number of rows in the table.

columns ​

tova
t.columns -> [String]

The column names in order.

shape ​

tova
t.shape -> [Int, Int]

Returns [row_count, column_count].

tova
t.shape    // [3, 3]

Access ​

toArray ​

tova
t.toArray() -> [Object]

Returns the underlying rows as an array of objects.

at ​

tova
t.at(index) -> Object | Nil

Returns the row at the given index. Supports negative indices.

tova
first = t.at(0)     // first row
last = t.at(-1)     // last row

slice ​

tova
t.slice(start, end) -> Table

Returns a new table with rows from start to end.

getColumn ​

tova
t.getColumn(name) -> [Any]

Returns all values of a column as an array.

tova
names = t.getColumn("name")   // ["Alice", "Bob", "Carol"]

Query ​

where ​

tova
t.where(predicate) -> Table

Filters rows matching a predicate. Use .column shorthand for column access.

tova
adults = t.where(.age >= 18)
nyc = t.where(.city == "NYC")

select ​

tova
t.select(...columns) -> Table

Returns a table with only the specified columns.

tova
names = t.select("name", "age")

derive ​

tova
t.derive(column = expr) -> Table

Adds or replaces a column with a computed value.

tova
t.derive(.name_upper = .name |> upper())
t.derive(.age_group = if .age >= 30 { "senior" } else { "junior" })

sort_by ​

tova
t.sortBy(column, desc?) -> Table

Sorts the table by a column. Optionally in descending order.

tova
t.sortBy(.age)
t.sortBy(.age, desc: true)

limit ​

tova
t.limit(n) -> Table

Returns the first n rows.

tova
top_5 = t.sortBy(.age, desc: true).limit(5)

Grouping and Aggregation ​

group_by ​

tova
t.groupBy(column) -> GroupedTable

Groups rows by a column value. Must be followed by agg().

tova
t.groupBy(.city)

agg ​

tova
grouped.agg(name: agg_fn, ...) -> Table

Aggregates grouped data. Inside agg(), use these aggregation functions (the compiler automatically transforms them to their agg_* runtime equivalents):

FunctionDescription
sum(.column)Sum of column values
count()Number of rows in each group
mean(.column)Mean (average) of column values
median(.column)Median of column values
min(.column)Minimum column value
max(.column)Maximum column value
tova
summary = t
  |> groupBy(.city)
  |> agg(
    count: count(),
    avg_age: mean(.age),
    oldest: max(.age)
  )

Joins ​

join ​

tova
t.join(other, opts) -> Table

Joins two tables on matching columns.

Options:

  • left / right -- column names or accessors to join on
  • how -- join type: "inner" (default), "left", "right", "outer", "cross", "anti", "semi"
tova
users = Table([
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" }
])

orders = Table([
  { user_id: 1, amount: 50 },
  { user_id: 1, amount: 30 },
  { user_id: 2, amount: 70 }
])

// Inner join (default)
result = users |> join(orders, left: "id", right: "user_id")

// Left join — keeps all rows from the left table
left_result = users |> join(orders, left: "id", right: "user_id", how: "left")
tova
// All join types
a |> join(b, left: .id, right: .uid, how: "right")    // all right rows
a |> join(b, left: .id, right: .uid, how: "outer")    // all rows from both
a |> join(b, how: "cross")                              // cartesian product
a |> join(b, left: .id, right: .uid, how: "anti")     // left rows with no match
a |> join(b, left: .id, right: .uid, how: "semi")     // left rows with a match

Reshaping ​

pivot ​

tova
t |> pivot(index: column, columns: column, values: column) -> Table

Pivots rows into columns. Takes named arguments index, columns, and values.

tova
sales = Table([
  { region: "East", quarter: "Q1", revenue: 100 },
  { region: "East", quarter: "Q2", revenue: 150 },
  { region: "West", quarter: "Q1", revenue: 200 }
])

sales |> pivot(index: "region", columns: "quarter", values: "revenue")
// { _index: "East", Q1: 100, Q2: 150 }
// { _index: "West", Q1: 200, Q2: nil }

Note: The index column is renamed to _index in the output.

unpivot ​

tova
t |> unpivot(id: column, columns: [String]) -> Table

Converts columns into rows (the inverse of pivot). Takes named arguments id (the identifier column) and columns (column names to unpivot). Returns a table with id, variable, and value columns.

explode ​

tova
t.explode(column) -> Table

Expands array values in a column into separate rows.

tova
t = Table([
  { name: "Alice", tags: ["admin", "user"] },
  { name: "Bob", tags: ["user"] }
])

t.explode("tags")
// { name: "Alice", tags: "admin" }
// { name: "Alice", tags: "user" }
// { name: "Bob", tags: "user" }

Cleaning ​

drop_duplicates ​

tova
t |> dropDuplicates(by?: column) -> Table

Removes duplicate rows. Optionally specify a column to check for uniqueness via the by option. Without by, compares entire rows.

tova
t |> dropDuplicates()
t |> dropDuplicates(by: .email)

rename ​

tova
t.rename(old, new) -> Table

Renames a column.

tova
t.rename("name", "full_name")

cast ​

tova
t.cast(column, type) -> Table

Converts a column to a different type.

tova
t.cast("age", "Int")
t.cast("price", "Float")

drop_nil ​

tova
t |> dropNil(column) -> Table

Removes rows where the specified column is nil.

tova
t |> dropNil(.email)

fill_nil ​

tova
t.fillNil(column, value) -> Table

Replaces nil values in a column with a default value.

tova
t.fillNil(.score, 0)
t.fillNil(.status, "unknown")

Inspection ​

peek ​

tova
t |> peek(n?: Int, title?: String) -> Table

Prints a preview of the table and returns it (passthrough for pipelines). Shows the first n rows (default 10).

tova
data |> peek()
data |> peek(n: 5, title: "Sample data")

describe ​

tova
t.describe() -> Table

Returns summary statistics for numeric columns (count, mean, std, min, max, quartiles).

tova
t.describe() |> peek()

schema_of ​

tova
schemaOf(table) -> Object

Returns the inferred schema (column names and types) of a table.

tova
schemaOf(users)
// { name: "String", age: "Int", email: "String" }

Window Functions ​

window ​

tova
t |> window(partition_by?: column, order_by?: column, desc?: Bool, ...fns) -> Table

Computes values across partitions of rows without collapsing them. Returns a new Table with all original columns plus new window columns.

Options:

  • partition_by -- column to partition rows by (optional; without it, entire table is one partition)
  • order_by -- column to sort rows within each partition (optional)
  • desc -- sort descending within partitions (default false)

All other named arguments define new columns using window functions.

tova
result = employees |> window(
  partition_by: .dept,
  order_by: .salary,
  rn: row_number(),
  rnk: rank(),
  prev_salary: lag(.salary, 1),
  running_total: running_sum(.salary)
)

Ranking Functions ​

FunctionDescription
row_number()Sequential number in partition (1, 2, 3, ...)
rank()Rank with gaps for ties (1, 2, 2, 4)
dense_rank()Rank without gaps (1, 2, 2, 3)
percent_rank()Relative rank as fraction (0.0 to 1.0)
ntile(n)Divide into n equal-sized buckets

Offset Functions ​

FunctionDescription
lag(.col, offset?, default?)Value from a previous row (default offset=1)
lead(.col, offset?, default?)Value from a following row (default offset=1)
first_value(.col)First value in the partition
last_value(.col)Last value in the partition

Running Aggregates ​

FunctionDescription
running_sum(.col)Cumulative sum
running_count()Cumulative count
running_avg(.col)Cumulative average
running_min(.col)Running minimum
running_max(.col)Running maximum
moving_avg(.col, n)Moving average over last n rows
tova
// Ranking within departments
employees |> window(
  partition_by: .dept,
  order_by: .salary,
  desc: true,
  salary_rank: row_number()
)

// Running totals and moving averages
sales |> window(
  order_by: .date,
  cumulative: running_sum(.revenue),
  trend: moving_avg(.revenue, 7)
)

// Previous/next row comparison
prices |> window(
  order_by: .date,
  prev_price: lag(.price),
  next_price: lead(.price)
)

Sampling ​

sample ​

tova
t |> sample(n, seed?) -> Table

Returns a random subset of rows. If n < 1, treats it as a fraction. Optional seed for reproducibility.

tova
var subset = t |> sample(100)              // 100 random rows
subset = t |> sample(0.1)             // 10% of rows
subset = t |> sample(1000, seed: 42)  // reproducible

stratified_sample ​

tova
t |> stratified_sample(key, n, seed?) -> Table

Samples n rows (or fraction) from each group defined by key.

tova
var balanced = t |> stratified_sample(.category, 50)
balanced = t |> stratified_sample(.region, 0.1, seed: 42)

Visualization ​

All chart functions take a Table (or array of objects) and return an SVG string.

bar_chart ​

tova
barChart(data, x:, y:, title?, width?, height?, color?, labels?, sort?) -> String

Vertical bar chart. Options: color (hex), labels: true (show values), sort: "desc".

line_chart ​

tova
lineChart(data, x:, y:, title?, width?, height?, color?, points?) -> String

Line chart. Supports multi-series via array of y columns: y: [.revenue, .cost].

scatter_chart ​

tova
scatterChart(data, x:, y:, title?, width?, height?, color?) -> String

Scatter plot with <circle> elements.

histogram ​

tova
histogram(data, col:, bins?, title?, width?, height?, color?) -> String

Histogram with automatic binning. Default 20 bins.

pie_chart ​

tova
pieChart(data, label:, value:, title?, width?, height?) -> String

Pie chart with percentage labels.

heatmap ​

tova
heatmap(data, x:, y:, value:, title?, width?, height?) -> String

Grid of colored cells. Requires categorical x/y and numeric value.

tova
// Example: generate and save a chart
sales
  |> groupBy(.region)
  |> agg(revenue: sum(.amount))
  |> barChart(x: .region, y: .revenue, title: "Revenue")
  |> writeText("chart.svg")

Combination ​

union ​

tova
union(a, b) -> Table

Combines two tables with the same columns, appending all rows.

tova
all_users = union(active_users, inactive_users)

Pipeline Example ​

tova
result = read("sales.csv")
  |> dropNil(.amount)
  |> fillNil(.region, "Unknown")
  |> where(.amount > 0)
  |> derive(.quarter = dateFormat(.date, "QQ YYYY"))
  |> groupBy(.region)
  |> agg(
    total: sum(.amount),
    count: count(),
    avg: mean(.amount)
  )
  |> sortBy(.total, desc: true)
  |> peek(title: "Revenue by Region")

result |> write("revenue_summary.csv")

Released under the MIT License.