Skip to content

Match pandas semantics in groupby.rolling and groupby.apply result construction - #23122

Merged
rapids-bot[bot] merged 5 commits into
NVIDIA:mainfrom
galipremsagar:as_index_fix
Jul 6, 2026
Merged

Match pandas semantics in groupby.rolling and groupby.apply result construction#23122
rapids-bot[bot] merged 5 commits into
NVIDIA:mainfrom
galipremsagar:as_index_fix

Conversation

@galipremsagar

Copy link
Copy Markdown
Contributor

Description

Fixes 12 of the 14 tests/window/test_groupby.py failures under cudf.pandas: RollingGroupby now honors as_index=False (group keys as leading columns) and sort=False (first-appearance group order), raises for BaseIndexer windows whose bounds were silently computed across group boundaries, and groupby.apply preserves the UDF result's index name in the concatenated MultiIndex. The two rolling.corr tuple-index tests are inherent (tuple values are stored as list rows and do not round-trip), so their xfail entries get a real reason; the 13 fixed entries are removed.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@galipremsagar
galipremsagar requested a review from a team as a code owner July 6, 2026 16:45
@github-actions github-actions Bot added Python Affects Python cuDF API. cudf.pandas Issues specific to cudf.pandas labels Jul 6, 2026
@GPUtester GPUtester moved this to In Progress in cuDF Python Jul 6, 2026
@galipremsagar galipremsagar added bug Something isn't working non-breaking Non-breaking change labels Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b7a49601-bbf7-4f2e-909b-bcf875ff712c

📥 Commits

Reviewing files that changed from the base of the PR and between aaaa9ec and 45e04eb.

📒 Files selected for processing (1)
  • python/cudf/cudf/core/window/rolling.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • python/cudf/cudf/core/window/rolling.py

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Improved groupby().apply() output so inner index level names are preserved in more cases.
    • Enhanced grouped rolling behavior to better match expected ordering and as_index=False results.
  • Bug Fixes

    • Fixed cases where result index names could be lost after grouped apply operations.
    • Corrected grouped rolling output to keep group appearance order when sorting is disabled.
    • Added clearer handling for unsupported rolling indexers in grouped rolling.
  • Tests

    • Added regression coverage for grouped apply index-name preservation and grouped rolling behavior.

Walkthrough

This PR preserves inner MultiIndex names in GroupBy.apply, and updates RollingGroupby to reject BaseIndexer windows, keep first-appearance ordering for sort=False, and prepend group keys for as_index=False results. Tests and pandas-testing-plugin expectations are updated.

Changes

GroupBy.apply inner index name preservation

Layer / File(s) Summary
Preserve inner MultiIndex level name in GroupBy.apply
python/cudf/cudf/core/groupby/groupby.py, python/cudf/cudf/tests/groupby/test_apply.py, python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
Both MultiIndex reconstruction branches in _post_process_chunk_results capture and restore the inner index level name via mi.names; a regression test checks include_groups=False, and the matching test expectation entry is removed.

Rolling groupby enhancements

Layer / File(s) Summary
RollingGroupby init and ordering
python/cudf/cudf/core/window/rolling.py, python/cudf/cudf/tests/window/test_rolling.py
RollingGroupby.__init__ rejects BaseIndexer windows, stores _as_index, and uses cupy-based reordering for sort=False; tests cover as_index=False, sort=False, and the BaseIndexer rejection.
RollingGroupby result construction
python/cudf/cudf/core/window/rolling.py, python/cudf/cudf/tests/window/test_rolling.py, python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
__getitem__ preserves _as_index, and _apply_agg now prepends group key columns for as_index=False DataFrame results while removing the old MultiIndex path; rolling-correlation expectation text is updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • rapidsai/cudf#22783: Both PRs modify GroupBy.apply post-processing to reconstruct the output MultiIndex inner level and preserve level naming.
  • rapidsai/cudf#22809: Both PRs change GroupBy._post_process_chunk_results to preserve pandas-assigned index metadata for row-like UDF outputs.
  • rapidsai/cudf#22904: Both PRs modify the pandas-testing-plugin xfail reason mappings for specific test node IDs.

Suggested labels: improvement

Suggested reviewers: vyasr, mroeschke, bdice

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: matching pandas semantics for groupby.rolling and groupby.apply result construction.
Description check ✅ Passed The description is directly related to the changeset and accurately describes the rolling and groupby.apply fixes and tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@galipremsagar

Copy link
Copy Markdown
Contributor Author

/merge

@galipremsagar galipremsagar added the 5 - Ready to Merge Testing and reviews complete, ready to merge label Jul 6, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
python/cudf/cudf/core/window/rolling.py (1)

676-702: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid computing _get_sorted_inds() when it will be discarded.

Line 683 always computes groupby.grouping.keys._get_sorted_inds() (a full sort), but when not groupby._sort, the result is immediately overwritten at line 700 by the cupy-based reordering. This wastes a sort pass on every sort=False call.

♻️ Proposed fix to skip the unused sort
         self._as_index = groupby._as_index
-        sort_inds = groupby.grouping.keys._get_sorted_inds()
-        if not groupby._sort:
+        if groupby._sort:
+            sort_inds = groupby.grouping.keys._get_sorted_inds()
+        else:
             # With sort=False pandas keeps groups in order of first
             # appearance; reorder the key-sorted blocks accordingly while
             # keeping the original row order within each block.
             offsets, _, (positions,) = groupby._groups(
                 [groupby._range_column_from_obj]
             )
             pos = cupy.asarray(positions.values)
             off = cupy.asarray(offsets)
             sizes = off[1:] - off[:-1]
             row_first_pos = cupy.repeat(pos[off[:-1]], sizes)
             order = cupy.lexsort(
                 cupy.stack([cupy.arange(len(pos)), row_first_pos])
             )
             sort_inds = as_column(pos[order])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudf/cudf/core/window/rolling.py` around lines 676 - 702, Avoid
computing groupby.grouping.keys._get_sorted_inds() when sort=False in the
rolling groupby path. In the groupby.rolling logic, move the initial sort_inds
assignment so it only runs when groupby._sort is true, and let the existing
cupy-based reorder path provide sort_inds for the false case. This removes the
wasted full sort while preserving the current behavior in the BaseIndexer and
GatherMap.from_column_unchecked flow.
python/cudf/cudf/tests/window/test_rolling.py (1)

550-591: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Good coverage for the three new behaviors (as_index=False, sort=False ordering, BaseIndexer rejection).

Consider also adding a case with multiple groups per first-appearance test (e.g., 3+ distinct groups interleaved) to more thoroughly exercise the cupy lexsort reordering logic beyond the 2-group case, but this isn't blocking.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@python/cudf/cudf/tests/window/test_rolling.py` around lines 550 - 591, The
new sort=False coverage in test_groupby_rolling_no_sort_first_appearance_order
only exercises two groups, so extend that test to use 3+ distinct interleaved
groups to better cover first-appearance ordering and the cupy lexsort reordering
path. Update the existing pandas/cudf comparison in
test_groupby_rolling_no_sort_first_appearance_order so it still validates the
same rolling().min() behavior with a richer group pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@python/cudf/cudf/core/window/rolling.py`:
- Around line 676-702: Avoid computing groupby.grouping.keys._get_sorted_inds()
when sort=False in the rolling groupby path. In the groupby.rolling logic, move
the initial sort_inds assignment so it only runs when groupby._sort is true, and
let the existing cupy-based reorder path provide sort_inds for the false case.
This removes the wasted full sort while preserving the current behavior in the
BaseIndexer and GatherMap.from_column_unchecked flow.

In `@python/cudf/cudf/tests/window/test_rolling.py`:
- Around line 550-591: The new sort=False coverage in
test_groupby_rolling_no_sort_first_appearance_order only exercises two groups,
so extend that test to use 3+ distinct interleaved groups to better cover
first-appearance ordering and the cupy lexsort reordering path. Update the
existing pandas/cudf comparison in
test_groupby_rolling_no_sort_first_appearance_order so it still validates the
same rolling().min() behavior with a richer group pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 4fb647a0-fbab-4ed7-a4dd-7e5b93e8b180

📥 Commits

Reviewing files that changed from the base of the PR and between 6fc6473 and aaaa9ec.

📒 Files selected for processing (5)
  • python/cudf/cudf/core/groupby/groupby.py
  • python/cudf/cudf/core/window/rolling.py
  • python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
  • python/cudf/cudf/tests/groupby/test_apply.py
  • python/cudf/cudf/tests/window/test_rolling.py

Older cupy (oldest-deps CI) does not accept an ndarray as the repeats
argument; broadcast each group's first-appearance position via
searchsorted instead.
@copy-pr-bot

copy-pr-bot Bot commented Jul 6, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@galipremsagar

Copy link
Copy Markdown
Contributor Author

/okay to test 45e04eb

@galipremsagar

Copy link
Copy Markdown
Contributor Author

/okay to test 3ae476c

@rapids-bot
rapids-bot Bot merged commit a942e5a into NVIDIA:main Jul 6, 2026
126 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in cuDF Python Jul 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

5 - Ready to Merge Testing and reviews complete, ready to merge bug Something isn't working cudf.pandas Issues specific to cudf.pandas non-breaking Non-breaking change Python Affects Python cuDF API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants