Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ jobs:
environment: numpy-nightly
pytest_marker_expression: "not slow and not network and not single_cpu"
platform: ubuntu-24.04
- name: "Pyarrow Nightly"
environment: pyarrow-nightly
pytest_marker_expression: "not slow and not network and not single_cpu"
platform: ubuntu-24.04
# - name: "Pyarrow Nightly"
# environment: pyarrow-nightly
# pytest_marker_expression: "not slow and not network and not single_cpu"
# platform: ubuntu-24.04
fail-fast: false
name: ${{ matrix.name || format('{0} {1}', matrix.platform, matrix.environment) }}
env:
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v3.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ Indexing
- Bug in :meth:`DataFrame.loc` raising ``ValueError`` when setting a row on a :class:`DataFrame` with no columns and the label is not in the index (:issue:`17895`)
- Bug in :meth:`DataFrame.loc` returning incorrect dtype when the column key is a ``slice`` (:issue:`63071`)
- Bug in :meth:`Index.get_indexer` where ``method="pad"``, ``"backfill"``, or ``"nearest"`` returned incorrect results when the target contained ``NaT`` or ``NaN`` instead of ``-1`` (:issue:`32572`)
- Bug in :meth:`Index.get_indexer` where ``pd.NA`` was not matched against ``NaN`` labels for object-dtype indexes when the input was an ``ndarray`` or ``Index`` (:issue:`65419`)
- Bugs in setitem-with-expansion when adding new rows failing to keep the original dtype in some cases (:issue:`32346`, :issue:`15231`, :issue:`47503`, :issue:`6485`, :issue:`25383`, :issue:`52235`, :issue:`17026`, :issue:`56010`)
- Bug in :meth:`DataFrame.__getitem__` raising ``InvalidIndexError`` when indexing with a tuple containing a ``slice`` on a :class:`DataFrame` with :class:`MultiIndex` columns (e.g., ``df[:, "t1"]``) (:issue:`26511`)
- Bug in :meth:`DataFrame.at` raising ``TypeError`` when accessing a :class:`MultiIndex` with a partial date string on a :class:`DatetimeIndex` level (:issue:`43395`)
Expand Down
29 changes: 25 additions & 4 deletions pandas/core/indexes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
is_datetime_array,
no_default,
)
from pandas._libs.missing import is_matching_na
from pandas._libs.missing import (
NA,
is_matching_na,
)
from pandas._libs.tslibs import (
OutOfBoundsDatetime,
Timestamp,
Expand Down Expand Up @@ -6433,13 +6436,13 @@ def get_indexer_for(self, target: Axes) -> npt.NDArray[np.intp]:
--------
Index.get_indexer : Computes indexer and mask for new index given
the current index.
Index.get_non_unique : Returns indexer and masks for new index given
Index.get_indexer_non_unique : Returns indexer and masks for new index given
the current index.

Examples
--------
>>> idx = pd.Index([np.nan, "var1", np.nan])
>>> idx.get_indexer_for([np.nan])
>>> idx = pd.Index(["a", "b", "a"])
>>> idx.get_indexer_for(["a"])
array([0, 2])
"""
if self._index_as_unique:
Expand Down Expand Up @@ -7083,6 +7086,24 @@ def _maybe_cast_listlike_indexer(self, target: Axes) -> Index:
):
# Fill missing values to ensure consistent missing value representation
target_index = target_index.fillna(np.nan)

# GH#65419: Normalize pd.NA/None to np.nan for object-dtype Index matching
if (
self.dtype == object
and target_index.dtype == object
and not target_index._is_multi
and target_index.hasnans
):
# Only normalize if we have pd.NA or None. These are the ones
# that cause the mismatch and have non-float types. This specific
# check avoids infinite recursion and crashes with non-scalars.
values = target_index.values
mask = isna(values)
if any(v is NA or v is None for v in values[mask]):
target_index = target_index.fillna(np.nan)
if target_index.dtype != object:
target_index = target_index.astype(object)

return target_index

@final
Expand Down
28 changes: 28 additions & 0 deletions pandas/tests/indexes/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,34 @@ def test_get_indexer_for_mixed_tuples(self):
expected = np.array([-1, -1, -1, 2], dtype=np.intp)
tm.assert_numpy_array_equal(result, expected)

def test_get_indexer_pd_na_matches_nan_object_index(self):
# GH#65419: pd.NA should match NaN labels in object-dtype Index
# for ndarray and Index inputs (consistent with list input and get_loc)
idx = Index([np.nan, "b"])

# Scalar lookup - already worked
assert idx.get_loc(NA) == 0

# List lookup - already worked
res_list = idx.get_indexer([NA])
tm.assert_numpy_array_equal(res_list, np.array([0], dtype=np.intp))

# ndarray lookup - was broken, now fixed
res_ndarray = idx.get_indexer(np.array([NA], dtype=object))
tm.assert_numpy_array_equal(res_ndarray, np.array([0], dtype=np.intp))

# Index lookup - was broken, now fixed
res_index = idx.get_indexer(Index([NA]))
tm.assert_numpy_array_equal(res_index, np.array([0], dtype=np.intp))

def test_get_indexer_multiindex_target_no_crash(self):
# GH#65419: MultiIndex target should not crash in _maybe_cast_listlike_indexer
idx = Index([np.nan, "b"])
mi = MultiIndex.from_tuples([("a", 1), ("b", 2)])
# Should not crash - MultiIndex is excluded from the pd.NA normalization
res = idx.get_indexer(mi)
assert res.shape == (2,)


class TestConvertSliceIndexer:
def test_convert_almost_null_slice(self, index):
Expand Down
Loading