From b7b2be6b704db35f4ebe0416ebb396bd428d5a03 Mon Sep 17 00:00:00 2001 From: phofl Date: Sat, 28 Nov 2020 18:50:14 +0100 Subject: [PATCH 1/3] Fix bug with listlike slice indexers for datetime indexes --- pandas/core/indexes/datetimes.py | 18 ++++++++++++++++++ pandas/tests/indexing/test_datetime.py | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index f6eeb121b1ac0..15558088b51b7 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -818,6 +818,24 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): # -------------------------------------------------------------------- + def _convert_listlike_indexer(self, key): + if not isinstance(key, list): + # There are no slices for arrays or indexes, so we can dispatch back + return super()._convert_listlike_indexer(key) + + new_indexer = [] + indexes = list(range(len(self))) + try: + for k in key: + indexer = indexes[self.get_loc(k)] + if not isinstance(indexer, list): + indexer = [indexer] + new_indexer.extend(indexer) + except KeyError: + # Dispatch to base method for handling of KeyErrors + return super()._convert_listlike_indexer(key) + return np.array(new_indexer), key + @property def inferred_type(self) -> str: # b/c datetime is represented as microseconds since the epoch, make diff --git a/pandas/tests/indexing/test_datetime.py b/pandas/tests/indexing/test_datetime.py index d00fe58265a2e..37a3bcce38430 100644 --- a/pandas/tests/indexing/test_datetime.py +++ b/pandas/tests/indexing/test_datetime.py @@ -231,3 +231,11 @@ def test_loc_setitem_with_existing_dst(self): dtype=object, ) tm.assert_frame_equal(result, expected) + + @pytest.mark.parametrize("indexer", ["2001-01", ["2001-01"]]) + def test_loc_getitem_partial_strings_in_list(self, indexer): + # GH#27180 + ser = Series(1, index=date_range('2001-01-01', periods=60)) + result = ser.loc[indexer] + expected = Series(1, index=date_range('2001-01-01', periods=31)) + tm.assert_series_equal(result, expected) \ No newline at end of file From db09651dfed04cd00a615e8f2387f47f603979c6 Mon Sep 17 00:00:00 2001 From: phofl Date: Sun, 29 Nov 2020 17:50:30 +0100 Subject: [PATCH 2/3] Improve test and add note --- pandas/core/indexes/datetimes.py | 7 ++++--- pandas/tests/indexing/test_datetime.py | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 15558088b51b7..6398185c0081f 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -820,14 +820,15 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): def _convert_listlike_indexer(self, key): if not isinstance(key, list): - # There are no slices for arrays or indexes, so we can dispatch back + # There are no slices, so we can dispatch back return super()._convert_listlike_indexer(key) new_indexer = [] - indexes = list(range(len(self))) + positions = list(range(len(self))) try: for k in key: - indexer = indexes[self.get_loc(k)] + # Convert slices to list of integers + indexer = positions[self.get_loc(k)] if not isinstance(indexer, list): indexer = [indexer] new_indexer.extend(indexer) diff --git a/pandas/tests/indexing/test_datetime.py b/pandas/tests/indexing/test_datetime.py index 37a3bcce38430..9bf66cfd02942 100644 --- a/pandas/tests/indexing/test_datetime.py +++ b/pandas/tests/indexing/test_datetime.py @@ -232,10 +232,20 @@ def test_loc_setitem_with_existing_dst(self): ) tm.assert_frame_equal(result, expected) - @pytest.mark.parametrize("indexer", ["2001-01", ["2001-01"]]) + @pytest.mark.parametrize( + "indexer", [["2001-01", "2001-01-30"], ["2001-01", Timestamp("2001-01-30")]] + ) def test_loc_getitem_partial_strings_in_list(self, indexer): # GH#27180 - ser = Series(1, index=date_range('2001-01-01', periods=60)) + ser = Series(1, index=date_range("2001-01-29", periods=60)) result = ser.loc[indexer] - expected = Series(1, index=date_range('2001-01-01', periods=31)) - tm.assert_series_equal(result, expected) \ No newline at end of file + expected = Series( + 1, + index=[ + Timestamp("2001-01-29"), + Timestamp("2001-01-30"), + Timestamp("2001-01-31"), + Timestamp("2001-01-30"), + ], + ) + tm.assert_series_equal(result, expected) From d5e463094cb5fe63a9b95a8e9d5a234892b73656 Mon Sep 17 00:00:00 2001 From: phofl Date: Sun, 29 Nov 2020 17:52:24 +0100 Subject: [PATCH 3/3] Add whatsnew --- doc/source/whatsnew/v1.2.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 5d36c52da9f0d..64c3287f40fc1 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -634,6 +634,7 @@ Indexing - Bug in :meth:`DataFrame.loc` returning and assigning elements in wrong order when indexer is differently ordered than the :class:`MultiIndex` to filter (:issue:`31330`, :issue:`34603`) - Bug in :meth:`DataFrame.loc` and :meth:`DataFrame.__getitem__` raising ``KeyError`` when columns were :class:`MultiIndex` with only one level (:issue:`29749`) - Bug in :meth:`Series.__getitem__` and :meth:`DataFrame.__getitem__` raising blank ``KeyError`` without missing keys for :class:`IntervalIndex` (:issue:`27365`) +- Bug in :meth:`DataFrame.loc` raising ``KeyError`` for list-like indexers with string slices for :class:`DatetimeIndex` (:issue:`27180`) Missing ^^^^^^^