Skip to content

Fix cum_sum window functions with null filling in cudf-polars - #23206

Merged
rapids-bot[bot] merged 9 commits into
NVIDIA:mainfrom
Matt711:bug/polars/cum-sum-null-filling
Jul 20, 2026
Merged

Fix cum_sum window functions with null filling in cudf-polars#23206
rapids-bot[bot] merged 9 commits into
NVIDIA:mainfrom
Matt711:bug/polars/cum-sum-null-filling

Conversation

@Matt711

@Matt711 Matt711 commented Jul 9, 2026

Copy link
Copy Markdown
Member

Description

Makes expressions like this work correctly

pl.col("a")
    .cum_sum()
    .fill_null(...)
    .over(...)

Closes #23189

This PR also closes #23205 by falling back to a single partition when executing cum_sum window functions without an order_by set in over with multiple ranks.

Needed for #22868 because of the change to Q51.

Checklist

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

@Matt711 Matt711 added bug Something isn't working non-breaking Non-breaking change labels Jul 9, 2026
@copy-pr-bot

copy-pr-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@github-actions github-actions Bot added Python Affects Python cuDF API. cudf-polars Issues specific to cudf-polars labels Jul 9, 2026
@Matt711

Matt711 commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

/ok to test d72e3cb

@GPUtester GPUtester moved this to In Progress in cuDF Python Jul 9, 2026
@Matt711
Matt711 marked this pull request as ready for review July 10, 2026 15:08
@Matt711
Matt711 requested a review from a team as a code owner July 10, 2026 15:08
@Matt711
Matt711 requested a review from mroeschke July 10, 2026 15:08
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Improved support for cumulative sums with forward or backward null filling in windowed operations.
    • Added correct handling for grouped cumulative sums without an explicit ordering column.
  • Bug Fixes

    • Improved multi-partition processing for cumulative sum windows.
    • Added clear errors for unsupported null-filling combinations with window functions.
  • Tests

    • Added coverage for supported cumulative sum and null-filling strategies.
    • Expanded validation of window-function behavior and streaming execution.

Walkthrough

Changes

Cumulative window support

Layer / File(s) Summary
Per-partition cumulative fill execution
python/cudf_polars/cudf_polars/dsl/expressions/rolling.py
Cumulative sums separate wrapped fill strategies, enforce sorted groups when needed, and apply forward or backward null replacement after grouped aggregation.
Window translation validation
python/cudf_polars/cudf_polars/dsl/translate.py, python/cudf_polars/tests/streaming/test_rolling.py, python/cudf_polars/cudf_polars/testing/inject_gpu_engine.py
Window translation handles wrapped cumulative sums, rejects filled rank expressions, and adds corresponding tests and expected-failure updates.
Distributed cumulative-sum fallback
python/cudf_polars/cudf_polars/streaming/utils.py, python/cudf_polars/cudf_polars/streaming/select.py
Multi-rank lowering detects cumulative sums without order_by and falls back to the existing single-partition-style path.

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

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: cum_sum window behavior with null filling in cudf-polars.
Description check ✅ Passed The description is directly related to the changes and correctly describes the cum_sum window fixes and fallback behavior.
Linked Issues check ✅ Passed The changes address both linked bugs by fixing cum_sum null-filling per partition and adding a multi-rank no-order_by fallback.
Out of Scope Changes check ✅ Passed No clear out-of-scope changes are present; the test expectation updates and minor header tweak support the same fixes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

and isinstance(v.children[0], UnaryFunction)
):
v = v.children[0]
if isinstance(v, UnaryFunction) and v.name == "cum_sum":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

question: Does this bug apply to any other of the cumulative functions besides cum_sum?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes but cum_sum is the only one we support for window functions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Gotcha, I would learn towards checking v.name.startswith("cum_") for a little more future-proofing but not a blocker.

Comment on lines +1111 to +1112
and isinstance(v.children[0], expr.UnaryFunction)
and v.children[0].name == "rank"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

question (not sure if possible): But can v.children[0] be an expression who's child is also a expr.UnaryFunction? i.e do we need to traverse potentially nested children to find a rank expr.UnaryFunction?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good question, we're pretty limited on the window functions we allow. But I'll double-check

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks, this is a problem and I switched to falling back on the nested unsupported cases in 9f4a5c6

Comment thread python/cudf_polars/cudf_polars/dsl/translate.py
@Matt711
Matt711 force-pushed the bug/polars/cum-sum-null-filling branch from d72e3cb to 9f4a5c6 Compare July 10, 2026 18:07

@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 (1)
python/cudf_polars/tests/streaming/test_rolling.py (1)

97-108: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider adding all-null/single-element partition cases.

The new test only covers partitions with partial nulls; per-partition edge cases like an all-null partition or a single-row partition for cum_sum().fill_null() aren't exercised here.

As per path instructions, "Ensure test files provide comprehensive edge case coverage (empty, all-null, single-element, mixed types) and do not depend on external datasets."

🤖 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_polars/tests/streaming/test_rolling.py` around lines 97 - 108,
The test_over_cum_sum_fill_null_per_partition parametrized case only covers
partially null partitions; extend its test data or add focused cases to cover an
all-null partition and a single-row partition while retaining both forward and
backward strategies. Keep the cases self-contained and validate them through
assert_gpu_result_equal.

Source: Path instructions

🤖 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_polars/tests/streaming/test_rolling.py`:
- Around line 97-108: The test_over_cum_sum_fill_null_per_partition parametrized
case only covers partially null partitions; extend its test data or add focused
cases to cover an all-null partition and a single-row partition while retaining
both forward and backward strategies. Keep the cases self-contained and validate
them through assert_gpu_result_equal.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8a83cc01-6ab9-4a11-99d8-21da428ceea0

📥 Commits

Reviewing files that changed from the base of the PR and between d72e3cb and 2081139.

📒 Files selected for processing (6)
  • python/cudf_polars/cudf_polars/dsl/expressions/rolling.py
  • python/cudf_polars/cudf_polars/dsl/translate.py
  • python/cudf_polars/cudf_polars/streaming/select.py
  • python/cudf_polars/cudf_polars/streaming/utils.py
  • python/cudf_polars/cudf_polars/testing/inject_gpu_engine.py
  • python/cudf_polars/tests/streaming/test_rolling.py
💤 Files with no reviewable changes (1)
  • python/cudf_polars/cudf_polars/testing/inject_gpu_engine.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • python/cudf_polars/cudf_polars/streaming/utils.py
  • python/cudf_polars/cudf_polars/streaming/select.py
  • python/cudf_polars/cudf_polars/dsl/translate.py
  • python/cudf_polars/cudf_polars/dsl/expressions/rolling.py

windowed = [
node
for node in traversal([value])
if isinstance(node, expr.UnaryFunction) and node.name in {"rank", "cum_sum"}

@mroeschke mroeschke Jul 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you remind me why we need to check rank but not in the last child.name check below?

Also a small docstring just describing what we're disallowing would be helpful

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's because the only unary functions we actually care about / support (other than fill null itself) are rank and cum_sum. This first is used to filter for both. And the second check only allows cum_sum because we only support fill_null(cum_sum(...)) not fill_null(rank(...)).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added docs in 215bc79

But you're right this isn't easy to reason about since you have to know which window functions are supported. In a follow-up, I'll do some refactoring to make this easier to understand.

@Matt711

Matt711 commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

/merge

@rapids-bot
rapids-bot Bot merged commit 69abe4c into NVIDIA:main Jul 20, 2026
107 of 108 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in cuDF Python Jul 20, 2026
@Matt711
Matt711 deleted the bug/polars/cum-sum-null-filling branch July 20, 2026 23:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working cudf-polars Issues specific to cudf-polars non-breaking Non-breaking change Python Affects Python cuDF API.

Projects

Status: Done

4 participants