UI Pivot Tables
Warning
The Deephaven Enterprise Engine supports Pivot Tables, but the UI Widget is not yet available. To display pivots, you must develop a custom UI with the JavaScript or Python clients.
UI Pivot Tables are designed for displaying information that is aggregated in two or more dimensions, like a rollup across both rows and columns. Pivot tables transform your data to show relationships between different dimensions, making complex data easier to analyze and interpret.
Pivot tables are particularly useful when:
- You need to analyze data across multiple dimensions simultaneously.
- You want to summarize large datasets into manageable insights.
- You need to perform comparative analysis between different categories.
- You want to create cross-tabulations for reporting purposes.
To create a Pivot Table, you must specify three core components:
- Row by-columns: Defines the grouping for rows (vertical organization).
- Column by-columns: Defines the grouping for columns (horizontal organization).
- Aggregations: Defines the calculations performed on data points.
Create pivot tables
To create a Pivot Table, use these API methods:
- Python:
make_pivot
. - Java/Groovy:
PivotTable.make
.
Example: Log message analysis
In this example, we retrieve the ProcessEventLog
table and create a Pivot showing the number of log messages for each Process. The rows are aggregated by Date
, EffectiveUser
, and ProcessInfoId
. The columns are aggregated by log levels (e.g., ERROR
or INFO
).
import io.deephaven.api.ColumnName
import io.deephaven.pivot.PivotTable
import static io.deephaven.api.agg.Aggregation.AggCount
import io.deephaven.enterprise.acl.EdgeAclProvider
import io.deephaven.enterprise.acl.AclFilterGenerator
processEventLog=db.liveTable("DbInternal", "ProcessEventLog").where("Date >= io.deephaven.time.calendar.Calendars.calendar().minusDays(today(), 5)")
pivot=PivotTable.make(processEventLog,
[AggCount("Count")],
[ColumnName.of("Date"), ColumnName.of("EffectiveUser"), ColumnName.of("ProcessInfoId")],
[ColumnName.of("Level")])
from deephaven_enterprise.pivot.pivot_table import (
PivotTable,
make_pivot,
)
from deephaven.agg import count_
from deephaven_enterprise.edge_acl import EdgeAclProvider
import deephaven_enterprise.acl_generator as acl_generator
process_event_log = db.live_table("DbInternal", "ProcessEventLog").where(
"Date >= io.deephaven.time.calendar.Calendars.calendar().minusDays(today(), 5)"
)
pivot = make_pivot(
process_event_log,
[count_("Count")],
["Date", "EffectiveUser", "ProcessInfoId"],
["Level"],
)
Data visualization
The following table is one possible visual representation of this pivot. You can see there are a total of 33,012 log messages, of which 19,638 are INFO
messages. On 2025-06-30
there were 13,619 messages, of which 5,946 were for Bob's processes and 7,673 for Alice's processes. We can see that Bob's process with the ID 658337fc-4eb4-4d3f-aef6-3c39066a59fe
had 2,491 messages, of which 2,331 were INFO
; 1 WARN
; 158 STDOUT
; and 1 STDERR
.
Date | EffectiveUser | ProcessInfoId | Total | INFO | WARN | ERROR | STDOUT | STDERR |
---|---|---|---|---|---|---|---|---|
33012 | 19638 | 499 | 19 | 10524 | 2332 | |||
2025-06-30 | 13619 | 9747 | 82 | 3781 | 9 | |||
2025-06-30 | bob | 5946 | 5291 | 2 | 649 | 4 | ||
2025-06-30 | bob | 123a2bde-8540-4523-b834-01d9d4f18fb3 | 672 | 503 | 1 | 167 | 1 | |
2025-06-30 | bob | 8bc6df68-7eef-423a-80e1-f917d59b781e | 599 | 431 | 167 | 1 | ||
2025-06-30 | bob | 006bf090-9fd2-421a-85c8-919e5d0653e5 | 2184 | 2026 | 157 | 1 | ||
2025-06-30 | bob | 658337fc-4eb4-4d3f-aef6-3c39066a59fe | 2491 | 2331 | 1 | 158 | 1 | |
2025-06-30 | alice | 7673 | 4456 | 80 | 3132 | 5 | ||
2025-06-30 | alice | 7fc0d1af-01c8-48e4-8381-22525a9fcfe3 | 1487 | 126 | 26 | 1334 | 1 | |
2025-06-30 | alice | 687f56b8-dbec-4763-9e60-bcd86302891f | 752 | 589 | 1 | 161 | 1 | |
2025-06-30 | alice | ff49a554-3bff-4551-9018-fc14c14a1bbe | 3659 | 3498 | 3 | 157 | 1 | |
2025-06-30 | alice | 70ce127e-b5b0-4584-93e1-cb7d0eabd94c | 1468 | 122 | 25 | 1320 | 1 | |
2025-06-30 | alice | 04a282a6-a784-409c-a4e0-f450bdee737f | 307 | 121 | 25 | 160 | 1 | |
2025-07-01 | 19393 | 9891 | 417 | 19 | 6743 | 2323 | ||
2025-07-01 | alice | 14035 | 5515 | 414 | 19 | 5770 | 2317 | |
2025-07-01 | alice | e3346e22-9f96-40a6-a466-26df3cfd542e | 331 | 167 | 1 | 162 | 1 | |
2025-07-01 | alice | a030c4d1-e470-4c08-bcf5-4b59f02bf2d1 | 1474 | 123 | 19 | 1331 | 1 | |
2025-07-01 | alice | c13e0754-0ca3-4885-9023-8ed97f465020 | 600 | 436 | 1 | 162 | 1 | |
2025-07-01 | alice | bd109e38-d551-434b-a661-8fdc8c78f8c6 | 1486 | 120 | 41 | 1324 | 1 | |
2025-07-01 | alice | 428f705d-8484-40ab-87dc-b4bba32b9a2e | 445 | 223 | 47 | 8 | 166 | 1 |
2025-07-01 | alice | 5c70ef7b-2423-4e01-99e5-d0c8af0ad5bc | 415 | 212 | 44 | 158 | 1 | |
2025-07-01 | alice | 64205453-facf-4476-bca7-d2b3b50cc3a2 | 1475 | 119 | 27 | 1328 | 1 | |
2025-07-01 | alice | d7ba5baa-9f0b-475d-8e44-db22581717d3 | 3250 | 3091 | 1 | 157 | 1 | |
2025-07-01 | alice | 5554cf08-d93a-4413-a62b-f2f9d6c251f1 | 383 | 176 | 48 | 158 | 1 | |
2025-07-01 | alice | 9991450b-66e5-4104-9266-2bee3357afd5 | 1554 | 155 | 43 | 1 | 166 | 1189 |
2025-07-01 | alice | a0cc164c-092f-4d5f-b319-7010ea4dc0ca | 433 | 217 | 43 | 6 | 166 | 1 |
2025-07-01 | alice | 3f933375-29ac-44da-8d2f-b3109b4dee3f | 355 | 148 | 40 | 166 | 1 | |
2025-07-01 | alice | 81c415b9-e6c3-41df-9968-170a0aceb4ac | 1521 | 204 | 31 | 4 | 166 | 1116 |
2025-07-01 | alice | c986fc3c-01f3-4434-837c-45867f0eb0fe | 313 | 124 | 28 | 160 | 1 | |
2025-07-01 | bob | 5358 | 4376 | 3 | 973 | 6 | ||
2025-07-01 | bob | c9ba1205-54ca-4aaa-a1a1-79aa472e098e | 369 | 201 | 167 | 1 | ||
2025-07-01 | bob | 84eeebb7-c6a0-4f24-a0f7-c1f15ae2cf8e | 408 | 242 | 1 | 164 | 1 | |
2025-07-01 | bob | 07c534cb-e416-49c4-b837-20ef85b94b37 | 420 | 258 | 161 | 1 | ||
2025-07-01 | bob | 172340a6-6575-4859-93b4-42b61a505f0a | 472 | 303 | 1 | 167 | 1 | |
2025-07-01 | bob | d30849fd-5c25-473e-aa8e-1010c8bc9afd | 1962 | 1803 | 1 | 157 | 1 | |
2025-07-01 | bob | 860273d2-7834-405b-9e76-de084709472d | 1727 | 1569 | 157 | 1 |
Filters and sorts
A Pivot Table can be sorted along either the rows or columns dimension using either the by-columns or the total aggregations for that dimension. Pivot sorts work identically to rollup sorts in each dimension—each node is sorted independently, maintaining the pivot's hierarchical structure.
A Pivot Table may be filtered by row or column keys, but cannot be filtered by aggregated values. After filtering, aggregations are recomputed so that totals only include the values accepted by the filter.
If you want to filter by input columns other than the keys, you must filter the source table and re-apply the pivot.
In Groovy, sorts and filters are added to the Pivot with the apply
method. The method takes two collections of SortColumns, one for the rows and another for the columns, and a list of Filters.
import io.deephaven.api.SortColumn
import io.deephaven.api.ColumnName
import io.deephaven.engine.table.impl.select.WhereFilterFactory
// Sort the rows by keys
by_key = pivot.apply([SortColumn.asc(ColumnName.of("Date")), SortColumn.asc(ColumnName.of("EffectiveUser")), SortColumn.asc(ColumnName.of("ProcessInfoId"))], [], [])
// Sort the rows and columns by count, displaying the most common values on top and to the left
by_count = pivot.apply([SortColumn.desc(ColumnName.of("Count"))], [SortColumn.desc(ColumnName.of("Count"))], [])
// Only include "ERROR" and "STDERR"
some_levels = pivot.apply([], [], [WhereFilterFactory.getExpression("Level.contains(`ERR`)")])
In Python, use
sort_rows
and
sort_cols
to sort the rows and columns of the pivot, respectively.
from deephaven.table import SortDirection
# Sort the rows by keys
by_key = pivot.sort_rows(["Date", "EffectiveUser", "ProcessInfoId"])
# Sort the rows and columns by count, displaying the most common values on top and to the left
by_count = pivot.sort_rows("Count", order=SortDirection.DESCENDING).sort_cols(
"Count", order=SortDirection.DESCENDING
)
Use the filter
method to filter on key columns.
# Only include "ERROR" and "STDERR"
some_levels = pivot_table.filter("Level.contains(`ERR`)")
Security considerations
Access Control Lists
When an ACL (Access Control List) is applied to a pivot table, it is applied when a user fetches the pivot table from the server. The ACL is applied to the source of the pivot, thus allowing the user to see the aggregated values across the permitted rows.
Example: User-specific data filtering
Extending the previous example, we add a group filter to the EffectiveUser
column. This filter permits users to view only rows that have an EffectiveUser value that matches one of the groups they are a member of. Because each user is a member of a group that matches their user name, and the EffectiveUser
column contains the username that a worker is running as, the effect is to display a pivot that includes only that user's processes.
def ACL = EdgeAclProvider.builder()
.rowAcl("allusers", AclFilterGenerator.group("EffectiveUser"))
.build()
pivot = ACL.applyTo(pivot)
ACL = (
EdgeAclProvider.builder()
.row_acl("allusers", acl_generator.group("EffectiveUser"))
.build()
)
pivot = ACL.apply_to(pivot)