Skip to content

Fix Series.isin dtype and value semantics for masked (nullable) dtypes - #23057

Merged
rapids-bot[bot] merged 11 commits into
NVIDIA:mainfrom
galipremsagar:isin_masked
Jul 14, 2026
Merged

Fix Series.isin dtype and value semantics for masked (nullable) dtypes#23057
rapids-bot[bot] merged 11 commits into
NVIDIA:mainfrom
galipremsagar:isin_masked

Conversation

@galipremsagar

Copy link
Copy Markdown
Contributor

Description

Fixes the Series.isin failures in tests/series/methods/test_isin.py under cudf.pandas (test_isin_masked_types and test_isin_large_series_and_pdNA, 12 cases). Two root causes, both fixed in cudf-classic:

1. Result dtype for masked (nullable) inputs

Series.isin on a pandas masked dtype (Int64/Float64/boolean) returned a numpy bool, but pandas returns a nullable boolean. Series.isin now mirrors pandas' BaseMaskedArray.isin for masked inputs:

  • matching is done on the underlying numpy values (so a boolean element equals the integer 1),
  • an NA element is considered present only when pd.NA itself is one of values (a plain NaN/None/NaT does not match), and
  • the result is a nullable BooleanDtype.

Non-masked dtypes (numpy, arrow, nullable-string, categorical) keep the numpy bool result, matching pandas.

2. Boolean-vs-numeric comparison bailed out

A boolean column compared as all-False against numeric needles because can_cast_safely reports bool↔numeric as unsafe, so ColumnBase.isin returned an all-False result. NumericalColumn._process_values_for_isin now promotes the boolean side to the numeric dtype (a bool always fits), so True == 1 compares by value like numpy/pandas. This also fixes plain cudf.Series([True, False]).isin([1]) in cudf-classic.

The now-passing entries are removed from the cudf.pandas xfail list.

Tests

Added cudf-classic coverage in tests/series/methods/test_isin.py:

  • test_isin_masked_types — mirrors pandas' cases across boolean/Int64/Float64, asserting the BooleanDtype result and NA semantics,
  • test_isin_bool_against_numeric — boolean Series vs numeric values,
  • test_isin_non_masked_extension_returns_numpy_bool — arrow / categorical inputs yield numpy bool.

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 1, 2026 00:52
@github-actions github-actions Bot added Python Affects Python cuDF API. cudf.pandas Issues specific to cudf.pandas labels Jul 1, 2026
@GPUtester GPUtester moved this to In Progress in cuDF Python Jul 1, 2026
@galipremsagar galipremsagar added bug Something isn't working 3 - Ready for Review Ready for review by team non-breaking Non-breaking change labels Jul 1, 2026
@coderabbitai

coderabbitai Bot commented Jul 1, 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: 432f0ca8-5ed1-4ab4-acce-6017c8e1cdd6

📥 Commits

Reviewing files that changed from the base of the PR and between 230446e and 8b22815.

📒 Files selected for processing (3)
  • python/cudf/cudf/core/column/numerical.py
  • python/cudf/cudf/core/series.py
  • python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
💤 Files with no reviewable changes (1)
  • python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
✅ Files skipped from review due to trivial changes (1)
  • python/cudf/cudf/core/series.py

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • Improved isin behavior for nullable numeric and boolean columns to better match pandas, including correct handling of missing values and mixed boolean/numeric comparisons.
    • Fixed Series.isin to return more consistent result types across masked and non-masked inputs.
  • Tests

    • Added broader coverage for nullable dtypes, missing-value handling, iterator inputs, and pandas-compatible behavior to prevent regressions.

Walkthrough

Updates isin to handle masked NumPy-backed dtypes with pandas-compatible null semantics, promotes boolean operands during numeric dtype alignment, removes stale xfail mappings, and expands regression coverage for nullable, masked, and extension dtype cases.

Changes

isin dtype handling fix

Layer / File(s) Summary
Masked isin handling and dtype promotion
python/cudf/cudf/core/column/numerical.py
Adds masked nullable NumPy-dtype handling in NumericalColumn.isin, normalizes host-side needles, combines data matches with a null mask, and promotes NumPy bool operands to numeric dtypes during dtype mismatch handling.
Series return path and xfail cleanup
python/cudf/cudf/core/series.py, python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
Stores the column result before rebuilding Series.isin output and removes pandas-testing-plugin mappings for the masked isin cases.
Series.isin regression coverage
python/cudf/cudf/tests/series/methods/test_isin.py
Updates the test header and imports, then adds coverage for masked nullable semantics, bool-vs-numeric matching, mixed container and iterator needles, masked float NaN versus pd.NA behavior, pandas-compatible mode, all-NA inputs, dtype stability, and extension dtype results.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • rapidsai/cudf#22904: Related removal/adjustment of stale xfail mappings in the same pandas testing plugin file.

Suggested labels: improvement

Suggested reviewers: mroeschke, TomAugspurger, bdice, wence-, KyleFromNVIDIA

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 change: fixing Series.isin behavior for masked nullable dtypes and related value semantics.
Description check ✅ Passed The description matches the changeset, describing the masked-dtype semantics fix, boolean-vs-numeric comparison fix, and added tests.
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.

@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.

Actionable comments posted: 1

🤖 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.

Inline comments:
In `@python/cudf/cudf/core/series.py`:
- Around line 3163-3168: The nullable-dtype path in `Series.isin` is iterating
over `values` twice in Python (`cleaned_values` and the later `any(...)`), which
can exhaust one-shot iterables and cause repeated GPU-to-host reads. Update the
`Series.isin` logic to normalize and inspect `values` once before building
`cleaned_values`, ideally by materializing or otherwise reusing a single pass
result, and then derive both the NA check and the filtered lookup list from that
same cached representation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7657cffc-4425-4de0-825b-ad35066f3e9d

📥 Commits

Reviewing files that changed from the base of the PR and between bd7bb74 and 213e3fb.

📒 Files selected for processing (4)
  • python/cudf/cudf/core/column/numerical.py
  • python/cudf/cudf/core/series.py
  • python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
  • python/cudf/cudf/tests/series/methods/test_isin.py
💤 Files with no reviewable changes (1)
  • python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py

Comment thread python/cudf/cudf/core/series.py Outdated
@galipremsagar

Copy link
Copy Markdown
Contributor Author

/okay to test 6c4b51b

@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/cudf/tests/series/methods/test_isin.py (1)

278-299: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Inconsistent dtype assertions across sub-blocks.

The Int64 block asserts got.dtype == pd.BooleanDtype() (Line 287), but the boolean block (Lines 290-293) and Float64 block (Lines 295-298) omit the equivalent check on got.dtype, only verifying gsr.dtype. Since this test specifically exists to guard nullable-dtype isin behavior in pandas-compatible mode, all three cases should assert the output dtype consistently.

🧪 Proposed fix to add missing dtype assertions
         gsr = cudf.Series([True, pd.NA], dtype="boolean")
         got = gsr.isin([True])
         assert gsr.dtype == pd.BooleanDtype()
+        assert got.dtype == pd.BooleanDtype()
         assert got.to_pandas().tolist() == [True, False]

         gsr = cudf.Series([1.5, 2.5], dtype="Float64")
         got = gsr.isin([1.5])
         assert gsr.dtype == pd.Float64Dtype()
+        assert got.dtype == pd.BooleanDtype()
         assert got.to_pandas().tolist() == [True, False]

As per coding guidelines, python/**/test_*.py should "Ensure test files provide comprehensive edge case coverage."

🤖 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/series/methods/test_isin.py` around lines 278 - 299,
The nullable-dtype isin test is inconsistent because only the Int64 case checks
the result dtype, while the boolean and Float64 cases in
test_isin_masked_pandas_compatible_mode omit the same assertion. Update that
test so each sub-block verifies both the input Series dtype and the returned
mask dtype, using the existing got variable and pd.BooleanDtype() consistently
across all three cases.

Source: Coding guidelines

🤖 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/tests/series/methods/test_isin.py`:
- Around line 278-299: The nullable-dtype isin test is inconsistent because only
the Int64 case checks the result dtype, while the boolean and Float64 cases in
test_isin_masked_pandas_compatible_mode omit the same assertion. Update that
test so each sub-block verifies both the input Series dtype and the returned
mask dtype, using the existing got variable and pd.BooleanDtype() consistently
across all three cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7d90a29e-628f-4a1c-99f4-5c6109f1b2d4

📥 Commits

Reviewing files that changed from the base of the PR and between 213e3fb and 6c4b51b.

📒 Files selected for processing (3)
  • python/cudf/cudf/core/series.py
  • python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
  • python/cudf/cudf/tests/series/methods/test_isin.py
💤 Files with no reviewable changes (1)
  • python/cudf/cudf/pandas/scripts/pandas-testing-plugin.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • python/cudf/cudf/core/series.py

@galipremsagar

Copy link
Copy Markdown
Contributor Author

/okay to test 230446e

Comment thread python/cudf/cudf/core/series.py Outdated
@galipremsagar

Copy link
Copy Markdown
Contributor Author

/okay to test 8b22815

@galipremsagar
galipremsagar requested a review from mroeschke July 3, 2026 13:12
# Conflicts:
#	python/cudf/cudf/tests/series/methods/test_isin.py
@copy-pr-bot

copy-pr-bot Bot commented Jul 13, 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 4c80565

Comment thread python/cudf/cudf/core/column/numerical.py Outdated
Comment thread python/cudf/cudf/core/series.py Outdated
* Simplify the masked-isin dispatch to an isinstance check on
  np.dtype/pd.ArrowDtype (equivalent for NumericalColumn dtypes) and
  drop the now-unused import.
* Inline the isin result column into Series._from_column.
@galipremsagar

Copy link
Copy Markdown
Contributor Author

/okay to test 9cf3a34

@galipremsagar galipremsagar added 5 - Ready to Merge Testing and reviews complete, ready to merge and removed 3 - Ready for Review Ready for review by team labels Jul 14, 2026
@galipremsagar

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit e09f480 into NVIDIA:main Jul 14, 2026
128 checks passed
@github-project-automation github-project-automation Bot moved this from In Progress to Done in cuDF Python Jul 14, 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