Skip to navigation

Tables

View as Markdown

Create a Table to describe the source columns Kumo should use and their roles in a graph. The table API is the same on Linux and in Snowflake or Databricks notebooks. Complete the setup for your environment and connect your data using Linux, Snowflake, or Databricks before continuing. A source table identifies external data. A Kumo Table adds modeling metadata; it does not copy the complete source into a graph until you create a snapshot.

Create Tables from a Source

The following examples use the AdventureWorks customer, product, and order sources.

from kumo.engine import Table
customer = Table.from_source_table(
connector["Customer"],
["CustomerID", "StoreID", "TerritoryID"],
primary_key="CustomerID",
)
product = Table.from_source_table(
connector["Product"],
["ProductID", "Name", "Color", "ProductLine", "Class", "StandardCost", "ListPrice"],
primary_key="ProductID",
)
sales_order_header = Table.from_source_table(
connector["SalesOrderHeader"],
["SalesOrderID", "CustomerID", "OrderDate", "OnlineOrderFlag", "TerritoryID", "SubTotal", "TotalDue"],
primary_key="SalesOrderID",
time_column="OrderDate",
)

from_source_table() resolves column types and may sample the source when its schema is ambiguous. Specify key and time columns explicitly; Kumo does not infer their roles from their names. A primary key identifies an entity. A time column tells Kumo when an event or record occurred.

Table.from_source_table(
source_table,
column_names=None,
*,
primary_key=None,
time_column=None,
end_time_column=None,
) -> Table
ArgumentPurpose
column_namesSelect source columns. Omit it to include all columns. Include every key, time column, target, and feature your workflow needs.
primary_keyName the column identifying each entity in this table.
time_columnName the timestamp used for temporal modeling.
end_time_columnName the end timestamp for records with a validity interval.

Create Tables from SQL

Filter rows, join source tables, or derive columns in your warehouse before adding them to a graph. Use a configured Snowflake, Databricks, or BigQuery connector with its matching Kumo integration and permission to query every referenced table. File and object-storage connectors do not support custom SQL.

The example below uses the bigquery connector from Connect Your Data, with an AdventureWorks Customer table in its configured dataset. It keeps customers with a territory and projects the columns the graph needs. Use the identifier spelling and quoting required by your warehouse.

from kumo.engine import Graph, Table
customer_sql = """
SELECT CustomerID, StoreID, TerritoryID
FROM Customer
WHERE TerritoryID IS NOT NULL
"""
customer_from_sql = Table.from_sql(
bigquery,
customer_sql,
primary_key="CustomerID",
)
print([column.name for column in customer_from_sql.definition.cols])

The resolved columns should match the query’s output: CustomerID, StoreID, and TerritoryID, with the warehouse’s identifier casing. Use those returned names for primary_key, time_column, end_time_column, and any optional column_names selection. Include every key, timestamp, target, and feature needed by your workflow in the projection. If a key is renamed with AS, use its new name in the table definition.

The equivalent source-object form is useful when you want to keep the SQL source for reuse:

customer_source = bigquery.sql(customer_sql)
customer_from_sql = Table.from_source_table(
customer_source,
primary_key="CustomerID",
)

Pass this source object to from_source_table(), not a raw SQL string. Each construction resolves metadata from the query result and can execute warehouse SQL. Choose one form rather than running both for the same table.

ConnectorNamespace for query references
SnowflakeConfigured database and schema_name
DatabricksConfigured catalog and schema_name
BigQueryConfigured project_id and dataset_id

Unqualified table names resolve inside that namespace. Qualified names must stay inside it, including references in joins and common table expressions (CTEs). Use a single read-only SELECT, optionally preceded by CTEs; writes, schema changes, and multiple statements are rejected. Supply a fully bound query without unresolved parameter placeholders. Return flat, named output columns and give every computed expression an explicit alias, for example TotalDue - SubTotal AS additional_charges. If Kumo cannot verify the query’s dependencies, simplify it so its source references can be resolved within the configured namespace.

SQL sources receive generated source names. Give the table a readable graph alias so predictive queries and relationships have a stable name:

customer_graph = Graph(tables={"customers": customer_from_sql}, edges=[])
customer_graph.validate()
customer_snapshot = customer_graph.snapshot()

This creates a graph snapshot containing the filtered customer rows. Use customers as the table name in predictive queries and graph edges. When adding relationships, their foreign keys must name columns projected by the SQL query; see Create a Graph. Snapshot creation runs the query again, so changes to source data since metadata inference can change the rows ingested. Keep the output schema stable between these steps, ensure declared primary keys remain unique after joins, and check that the query returns rows: an empty result cannot be ingested. Local snapshot creation still requires the configured execution storage from your environment setup.

Inspect and Adjust Column Types

Inspect the resolved definition before building the graph. Check that all requested keys, time columns, targets, and features appear in the resolved column list:

for column in customer.definition.cols:
print(column.name, column.dtype, column.stype)
print("Primary key:", customer.definition.pkey)
print("Time column:", customer.definition.time_col)

A column’s dtype describes its representation, such as an integer or timestamp. Its stype describes how the model should interpret it, such as an identifier, category, or numerical feature.

For a tabular view, project the resolved column names, types, and roles into a pandas DataFrame. This example uses the customer table created above:

import pandas as pd
definition = customer.definition
metadata = pd.DataFrame([
{
"name": column.name,
"dtype": column.dtype.value,
"stype": column.stype.value,
"primary_key": column.name == definition.pkey,
"time_column": column.name == definition.time_col,
"end_time_column": column.name == definition.end_time_col,
}
for column in definition.cols
])
print(metadata.to_string(index=False))

The result has one row per selected column. For this customer table, CustomerID has primary_key=True; both time-role columns are False for every row because no time columns were assigned. The projection reads the resolved metadata in memory and includes no source row values or connector settings. If you constructed a bare Table with definition=None, first resolve it with table = table.infer_metadata() as described in Refresh Metadata. Inference can read the source; displaying an already resolved definition does not refresh it. See Inspect a Graph Definition to list graph table names and relationships.

See Encoder Selection for compatible encoders and configuration options for each semantic type. Use with_column() to return a new table with an explicit override:

from kumoapi.typing import Dtype, Stype
sales_order_header = sales_order_header.with_column("CustomerID", stype=Stype.ID)
sales_order_header = sales_order_header.with_column("OrderDate", dtype=Dtype.date)

For a CSV timestamp stored as text, supply its format. This example expects values such as 2026-01-15 14:30:00:

sales_order_header = sales_order_header.with_column(
"OrderDate",
dtype=Dtype.date,
timestamp_format="%Y-%m-%d %H:%M:%S",
)
table.with_column(
name,
*,
stype=None,
dtype=None,
timestamp_format=None,
multicategorical_delimiter=None,
) -> Table

Assign the returned value; the original table is unchanged. Unspecified semantic type, data type, and timestamp format preserve their current values. An unknown column raises ValueError. For a scalar string multicategorical column, an omitted delimiter preserves an existing delimiter or samples the source to infer a missing one. If the resulting column is not scalar string multicategorical, an omitted delimiter clears the previous delimiter. For Unix timestamps, timestamp_format can instead use a TimestampUnit from kumoapi.table. For a delimited collection of categories, use the appropriate MulticategoricalDelimiter from kumoapi.typing.

Validate a Table Schema

Check a table’s resolved schema before adding relationships or creating a snapshot. Use the customer table created above, including any type overrides you want to check:

from kumo.engine import Graph
Graph(tables=[customer]).validate()

The call returns None when the schema is valid. If it raises a validation error, correct the named column or type and validate the revised table again. For example, a floating-point primary key is rejected. Use an integer or string key in your source and ensure the table’s declared type matches it.

This checks the resolved schema. It does not ingest the table or verify row constraints such as primary-key uniqueness. Creating the table with from_source_table() still reads source metadata and may sample data to resolve types. To check relationships between tables, create a graph with edges and validate it. That section also explains the limitation on self-relations.

Refresh Metadata

table.infer_metadata() returns a new table inferred from the current source, preserving the selected columns and named keys and time columns. It is not necessary immediately after from_source_table(). If you rerun inference, reapply any explicit type overrides before building the graph. To change selected columns or key roles, construct a new table with the desired arguments.

Continue to Graphs to connect these tables and materialize a snapshot.