diff --git a/doc/source/whatsnew/v1.4.0.rst b/doc/source/whatsnew/v1.4.0.rst index 00471a0ef893e..5a5afb0823b75 100644 --- a/doc/source/whatsnew/v1.4.0.rst +++ b/doc/source/whatsnew/v1.4.0.rst @@ -289,6 +289,7 @@ Indexing - Bug in :meth:`Index.get_indexer_non_unique` when index contains multiple ``np.nan`` (:issue:`35392`) - Bug in :meth:`DataFrame.query` did not handle the degree sign in a backticked column name, such as \`Temp(°C)\`, used in an expression to query a dataframe (:issue:`42826`) - Bug in :meth:`DataFrame.drop` where the error message did not show missing labels with commas when raising ``KeyError`` (:issue:`42881`) +- Bug in :meth:`PeriodIndex.get_loc` raised ``KeyError`` with string key corresponding to higher resolution than the index (:issue:`40145`) - Missing diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 4e47a59740212..99e5f46a98b02 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -427,15 +427,14 @@ def get_loc(self, key, method=None, tolerance=None): raise KeyError(key) from err if reso == self.dtype.resolution: - # the reso < self.dtype.resolution case goes through _get_string_slice + # the reso > self.dtype.resolution case goes through _partial_date_slice key = Period(parsed, freq=self.freq) loc = self.get_loc(key, method=method, tolerance=tolerance) # Recursing instead of falling through matters for the exception # message in test_get_loc3 (though not clear if that really matters) return loc - elif method is None: - raise KeyError(key) - else: + elif reso < self.dtype.resolution: + # GH 40145 Fall through key = Period(parsed, freq=self.freq) elif isinstance(key, Period): diff --git a/pandas/tests/indexes/period/test_indexing.py b/pandas/tests/indexes/period/test_indexing.py index 3b7b738bec410..f31b4f217bc9b 100644 --- a/pandas/tests/indexes/period/test_indexing.py +++ b/pandas/tests/indexes/period/test_indexing.py @@ -213,37 +213,29 @@ def test_getitem_seconds(self): def test_getitem_day(self): # GH#6716 - # Confirm DatetimeIndex and PeriodIndex works identically didx = date_range(start="2013/01/01", freq="D", periods=400) pidx = period_range(start="2013/01/01", freq="D", periods=400) + # Identitcal for DatetimeIndex and PeriodIndex for idx in [didx, pidx]: - # getitem against index should raise ValueError - values = [ - "2014", - "2013/02", - "2013/01/02", - "2013/02/01 9H", - "2013/02/01 09:00", - ] - for v in values: - - # GH7116 - # these show deprecations as we are trying - # to slice with non-integer indexers - # with pytest.raises(IndexError): - # idx[v] - continue - s = Series(np.random.rand(len(idx)), index=idx) tm.assert_series_equal(s["2013/01"], s[0:31]) tm.assert_series_equal(s["2013/02"], s[31:59]) tm.assert_series_equal(s["2014"], s[365:]) - invalid = ["2013/02/01 9H", "2013/02/01 09:00"] - for v in invalid: - with pytest.raises(KeyError, match=v): - s[v] + higher_reso = ["2013/02/01 9H", "2013/02/01 09:00"] + + # DatetimeIndex raises + s = Series(np.random.rand(len(didx)), index=didx) + for v in higher_reso: + with pytest.raises(KeyError, match=v): + s[v] + + # GH 40145 PeriodIndex works + s = Series(np.random.rand(len(pidx)), index=pidx) + for v in higher_reso: + assert v in pidx + assert s[v] == s[31] class TestGetLoc: @@ -883,15 +875,10 @@ def test_contains(self): assert p in idx0 assert str(p) in idx0 - # GH#31172 - # Higher-resolution period-like are _not_ considered as contained key = "2017-09-01 00:00:01" - assert key not in idx0 - with pytest.raises(KeyError, match=key): - idx0.get_loc(key) - with pytest.raises(KeyError, match=key): - with tm.assert_produces_warning(FutureWarning): - idx0.get_value(ser, key) + assert key in idx0 + with tm.assert_produces_warning(FutureWarning): + idx0.get_value(ser, key) assert "2017-09" in idx0