Add multi-index lookup and GroupedMatrix for structured groupBy results#205
Merged
Add multi-index lookup and GroupedMatrix for structured groupBy results#205
Conversation
Member
perNyfelt
commented
Mar 9, 2026
- Add createIndex/resetIndex/lookup/lookupIndices to Matrix with lazy-invalidated internal index (database-style indexing over data columns)
- Add GroupedMatrix class with get(), keys(), level(), agg(), each()
- Change Stat.groupBy to return GroupedMatrix with compound List keys
- Add Matrix.groupBy() convenience method
- Propagate index through clone/subset/selectColumns, clear on drop
- Emit #index: metadata in toCsvString
- Update GgStat callers to use toStringKeyMap() for compat
- Add 32 new tests (17 index + 16 GroupedMatrix)
- Update docs and cookbook
- Add createIndex/resetIndex/lookup/lookupIndices to Matrix with lazy-invalidated internal index (database-style indexing over data columns) - Add GroupedMatrix class with get(), keys(), level(), agg(), each() - Change Stat.groupBy to return GroupedMatrix with compound List keys - Add Matrix.groupBy() convenience method - Propagate index through clone/subset/selectColumns, clear on drop - Emit #index: metadata in toCsvString - Update GgStat callers to use toStringKeyMap() for compat - Add 32 new tests (17 index + 16 GroupedMatrix) - Update docs and cookbook Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds database-style indexing to Matrix and introduces GroupedMatrix as a structured return type for groupBy, updating callers, tests, and docs to match.
Changes:
- Add
Matrixindex API (createIndex/resetIndex/lookup/lookupIndices) with lazy invalidation and CSV metadata emission. - Introduce
GroupedMatrixand changeStat.groupBy/Matrix.groupByto return it (with legacytoStringKeyMap()compatibility). - Update ggplot stats, tests, and documentation to use the new grouping/indexing behavior.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| matrix-ggplot/src/main/groovy/se/alipsa/matrix/gg/stat/GgStat.groovy | Updates Stat.groupBy(...) call sites to use GroupedMatrix.toStringKeyMap() for legacy string-key behavior. |
| matrix-core/v3.7.0-roadmap.md | Documents the new index + GroupedMatrix design and implementation checklist. |
| matrix-core/src/test/groovy/StatTest.groovy | Updates groupBy assertions to use GroupedMatrix.get(...) and adds a legacy toStringKeyMap() check. |
| matrix-core/src/test/groovy/MatrixTest.groovy | Adds index API tests plus Matrix.groupBy() convenience method tests. |
| matrix-core/src/test/groovy/GroupedMatrixTest.groovy | Adds a new test suite covering GroupedMatrix accessors and aggregation APIs. |
| matrix-core/src/main/groovy/se/alipsa/matrix/core/Stat.groovy | Changes groupBy implementation/return type to GroupedMatrix. |
| matrix-core/src/main/groovy/se/alipsa/matrix/core/Matrix.groovy | Implements index fields, invalidation hooks, CSV #index: emission, lookup APIs, and groupBy convenience methods. |
| matrix-core/src/main/groovy/se/alipsa/matrix/core/GroupedMatrix.groovy | Adds the new GroupedMatrix type with structured keys + aggregation helpers + legacy map conversions. |
| docs/python-comparison.md | Updates feature comparison to reflect rolling/cumulative ops and new indexing/grouping support. |
| docs/cookbook/matrix-core.md | Adds cookbook sections/examples for indexing/lookup and GroupedMatrix usage. |
matrix-core/src/main/groovy/se/alipsa/matrix/core/Matrix.groovy
Outdated
Show resolved
Hide resolved
- Remove dead branch in GroupedMatrix.agg, clarify keys are source column names - Add key count validation to lookupIndices (0 keys, too many keys) - Preserve index on empty lookup results for consistency - Clear indexMap in copyIndexTo to prevent stale state - Use immutable keys in Stat.groupBy to protect map integrity - Defensive-copy groups map in GroupedMatrix constructor Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
matrix-core/src/main/groovy/se/alipsa/matrix/core/Matrix.groovy
Outdated
Show resolved
Hide resolved
- Sort partial-key lookupIndices results to preserve original row order - Document #index: comment header in toCsvString Javadoc - Infer types in GroupedMatrix.agg (group column types from source, aggregation types from first result) - Update python-comparison.md groupBy section for GroupedMatrix API - Add invalidateIndex to all remaining mutating methods: apply(rows), apply(criteria), replace(col,from,to), replace(col,type,values), upsertColumn - Clear index on rename if indexed column is renamed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
matrix-core/src/main/groovy/se/alipsa/matrix/core/GroupedMatrix.groovy
Outdated
Show resolved
Hide resolved
…kupIndices - Fix agg type inference to use per-column slots, preventing misalignment when some aggregations return null for the first group - Add #index: parsing to MatrixBuilder for full CSV round-trip support - Return unmodifiable list from lookupIndices full-key path - Extend testIndexCsvRoundTrip to verify actual round-trip restore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove outdated reference to underscore-separated string keys and document the structured List<?> compound keys and toStringKeyMap() for backward compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
matrix-core/src/main/groovy/se/alipsa/matrix/core/Matrix.groovy
Outdated
Show resolved
Hide resolved
Return Collections.emptyList() for missing full keys and Collections.unmodifiableList() for partial-key results, matching the full-key behavior for a consistent API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
matrix-core/src/main/groovy/se/alipsa/matrix/core/Matrix.groovy
Outdated
Show resolved
Hide resolved
…cates - Add public markIndexDirty() for direct column mutation scenarios - Wrap index map keys with Collections.unmodifiableList() in buildIndex() - Reject duplicate column names in createIndex() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- MatrixBuilder: filter out empty strings from #index: spec parsing to avoid confusing errors from trailing commas or empty entries - GroupedMatrix.agg: infer aggregated column types as the common supertype across all groups instead of using the first non-null type, ensuring stable types regardless of group iteration order Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
matrix-core/src/main/groovy/se/alipsa/matrix/core/Matrix.groovy
Outdated
Show resolved
Hide resolved
…der leak - GroupedMatrix constructor: defensively copy and wrap all key lists as immutable to protect map integrity from external callers - Matrix.rename: update indexedColumnNames in-place instead of dropping the index, so callers don't lose their index on column rename - MatrixBuilder: clear indexColumns at parse start to prevent index leaking between reuses of the same builder instance - Fix lookupIndices comment: "unmodifiable view" instead of "defensive copy" - Roadmap: clarify toStringKeyMap() is the backward-compat method Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove clearPendingIndexColumns() from types() methods so that overriding types after csvString() parsing preserves the #index: metadata (index is still cleared when data/columns/rows change) - Update toMap() Javadoc to clarify it is not the legacy format; point to toStringKeyMap() for backward compatibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.