Understanding the Table API
Deephaven's Table API lets you filter, transform, join, and aggregate data using a consistent set of operations. This guide explains the core concepts behind the API, helping you write more effective queries and avoid common pitfalls.
Note
New to Deephaven? Start with How Deephaven works: A mental model for the conceptual foundation this guide builds on. This page assumes you already know why tables don't copy data and why formulas run in the engine, and focuses on the API surface and tradeoffs you'll actually choose between.
For a quick-reference index of available operations and where to find full guides for each, see Table operations overview.
Tables are immutable
Table operations never modify their source — the original is always safe to keep using. Most operations return a distinct new table; a no-op transformation may occasionally hand back the same object as an optimization, but either way the source is left untouched:
This differs from libraries like pandas, where operations often modify data in place. In Deephaven, you build up results by chaining operations, and each step leaves its input untouched. That result usually isn't a full copy, though — see Tables are recipes, not data for how the engine shares unchanged columns instead of duplicating them.
Why immutability matters:
- Debugging: Intermediate tables remain available for inspection.
- Reuse: The same source can feed multiple downstream operations without interference.
- Live updates: The engine can safely propagate changes through the dependency graph.
Formulas are strings
Column expressions are written as strings, not native Python code:
These strings are parsed and executed by Deephaven's Java engine, not Python. This has several implications:
Syntax is Java-like, not Python:
- Use
Math.sqrt, notmath.sqrt. - String literals use backticks:
`hello`, not"hello". - Ternary expressions:
X > 0 ? X : -X.
You can call Python functions, but there's a cost:
Crossing from Java to Python adds overhead — see Formulas run in the engine, not in Python for when the engine can batch that crossing per chunk versus falling back to once per row. Either way, for performance-critical code, prefer built-in functions or a Java function.
Query scope makes variables available:
Python variables in the local or global scope are automatically available in formula strings through Deephaven's query scope.
What's available inside formulas
Inside a formula string, you have access to:
Column values — Reference by name:
Special variables:
i— Row position asint(0, 1, 2, ...)ii— Row position aslong(for tables with more than 2 billion rows)k— Internal row key (use cautiously; not the same as row position)
i/ii are valid on static, append-only, or blink tables; k is valid on a slightly broader set — static, add-only (which includes append-only), or blink tables (see Table types for what these mean). A general refreshing table rejects whichever of these it doesn't satisfy, because positions and keys can shift. See special variables for the full compatibility matrix.
Built-in functions — Math, string manipulation, time operations:
Query scope variables and your own Python functions are also available inside formulas — see Formulas are strings above for how those work and their tradeoffs.
Operations build a dependency graph
When you chain operations, you create a directed acyclic graph (DAG) of table dependencies:
For static tables, this is just a convenient way to structure code.
For live (refreshing) tables, the graph becomes active:
- When source data changes, updates propagate automatically through all dependent tables.
- You don't re-run your code — the engine handles incremental updates.
- Each downstream table sees a consistent view of the data.
This is why Deephaven can efficiently process real-time data: it typically recomputes only what changed, not the entire result.
Memory vs computation tradeoffs
The Table API offers several ways to add columns, each with different performance characteristics:
| Operation | Stores values | Recomputes on access | Best for |
|---|---|---|---|
update | Yes | No | Expensive formulas, values accessed repeatedly |
view | No | Yes | Simple formulas, memory-constrained environments |
select | Yes | No | Creating a new table with only specific columns |
update_view | No | Yes | Same as view, but keeping all original columns |
lazy_update | Cached | When cache misses | Few unique input values, expensive computation |
update computes values once and stores them:
view computes on demand:
select is like update but only includes specified columns:
For refreshing tables, this choice also affects update performance. view recomputes on every access, while update recomputes only when source data changes.
Same API, different behavior
The same operations work on both static and live tables — see Static vs. live: understanding mutability for the underlying concept. In practice, this means:
The code is identical. The difference:
static_resultis computed once and never changes.live_resultautomatically updates as new rows arrive inlive_table.
You can check whether a table is live with is_refreshing:
Related documentation
- How Deephaven works: A mental model — The conceptual foundation this guide builds on
- Table operations overview — Quick-reference index of available operations
- Deephaven's design — Architecture and update model
- Table types — Static, streaming, blink, and ring tables
- Pydoc: Table — Complete Python API reference
- Javadoc: TableOperations — Operation contracts
How table operations work in Deephaven