Skip to content

Commit 0b9c558

Browse files
jbrockmendelTLouf
authored andcommitted
BUG: PeriodIndex.get_loc with mismatched freq (pandas-dev#41670)
1 parent ab48402 commit 0b9c558

File tree

3 files changed

+32
-29
lines changed

3 files changed

+32
-29
lines changed

doc/source/whatsnew/v1.3.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,7 @@ Indexing
938938
- Bug in :meth:`Series.__delitem__` with ``ExtensionDtype`` incorrectly casting to ``ndarray`` (:issue:`40386`)
939939
- Bug in :meth:`DataFrame.loc` returning :class:`MultiIndex` in wrong order if indexer has duplicates (:issue:`40978`)
940940
- Bug in :meth:`DataFrame.__setitem__` raising ``TypeError`` when using a str subclass as the column name with a :class:`DatetimeIndex` (:issue:`37366`)
941+
- Bug in :meth:`PeriodIndex.get_loc` failing to raise ``KeyError`` when given a :class:`Period` with a mismatched ``freq`` (:issue:`41670`)
941942

942943
Missing
943944
^^^^^^^

pandas/core/indexes/period.py

+3-22
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44
datetime,
55
timedelta,
66
)
7-
from typing import (
8-
Any,
9-
Hashable,
10-
)
7+
from typing import Hashable
118
import warnings
129

1310
import numpy as np
@@ -318,24 +315,6 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
318315
return False
319316
return dtype.freq == self.freq
320317

321-
# ------------------------------------------------------------------------
322-
# Indexing
323-
324-
@doc(Index.__contains__)
325-
def __contains__(self, key: Any) -> bool:
326-
if isinstance(key, Period):
327-
if key.freq != self.freq:
328-
return False
329-
else:
330-
return key.ordinal in self._engine
331-
else:
332-
hash(key)
333-
try:
334-
self.get_loc(key)
335-
return True
336-
except KeyError:
337-
return False
338-
339318
# ------------------------------------------------------------------------
340319
# Index Methods
341320

@@ -472,6 +451,8 @@ def get_loc(self, key, method=None, tolerance=None):
472451
elif is_integer(key):
473452
# Period constructor will cast to string, which we dont want
474453
raise KeyError(key)
454+
elif isinstance(key, Period) and key.freq != self.freq:
455+
raise KeyError(key)
475456

476457
try:
477458
key = Period(key, freq=self.freq)

pandas/tests/indexes/period/test_indexing.py

+28-7
Original file line numberDiff line numberDiff line change
@@ -338,15 +338,21 @@ def test_get_loc_integer(self):
338338
pi2.get_loc(46)
339339

340340
# TODO: This method came from test_period; de-dup with version above
341-
def test_get_loc2(self):
341+
@pytest.mark.parametrize("method", [None, "pad", "backfill", "nearest"])
342+
def test_get_loc_method(self, method):
342343
idx = period_range("2000-01-01", periods=3)
343344

344-
for method in [None, "pad", "backfill", "nearest"]:
345-
assert idx.get_loc(idx[1], method) == 1
346-
assert idx.get_loc(idx[1].asfreq("H", how="start"), method) == 1
347-
assert idx.get_loc(idx[1].to_timestamp(), method) == 1
348-
assert idx.get_loc(idx[1].to_timestamp().to_pydatetime(), method) == 1
349-
assert idx.get_loc(str(idx[1]), method) == 1
345+
assert idx.get_loc(idx[1], method) == 1
346+
assert idx.get_loc(idx[1].to_timestamp(), method) == 1
347+
assert idx.get_loc(idx[1].to_timestamp().to_pydatetime(), method) == 1
348+
assert idx.get_loc(str(idx[1]), method) == 1
349+
350+
key = idx[1].asfreq("H", how="start")
351+
with pytest.raises(KeyError, match=str(key)):
352+
idx.get_loc(key, method=method)
353+
354+
# TODO: This method came from test_period; de-dup with version above
355+
def test_get_loc3(self):
350356

351357
idx = period_range("2000-01-01", periods=5)[::2]
352358
assert idx.get_loc("2000-01-02T12", method="nearest", tolerance="1 day") == 1
@@ -401,6 +407,21 @@ def test_get_loc_invalid_string_raises_keyerror(self):
401407
assert "A" not in ser
402408
assert "A" not in pi
403409

410+
def test_get_loc_mismatched_freq(self):
411+
# see also test_get_indexer_mismatched_dtype testing we get analogous
412+
# behavior for get_loc
413+
dti = date_range("2016-01-01", periods=3)
414+
pi = dti.to_period("D")
415+
pi2 = dti.to_period("W")
416+
pi3 = pi.view(pi2.dtype) # i.e. matching i8 representations
417+
418+
with pytest.raises(KeyError, match="W-SUN"):
419+
pi.get_loc(pi2[0])
420+
421+
with pytest.raises(KeyError, match="W-SUN"):
422+
# even though we have matching i8 values
423+
pi.get_loc(pi3[0])
424+
404425

405426
class TestGetIndexer:
406427
def test_get_indexer(self):

0 commit comments

Comments
 (0)