Skip to content

Commit

Permalink
BUG: PeriodIndex.get_loc with mismatched freq (pandas-dev#41670)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbrockmendel authored and TLouf committed Jun 1, 2021
1 parent ab48402 commit 0b9c558
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 29 deletions.
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.3.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,7 @@ Indexing
- Bug in :meth:`Series.__delitem__` with ``ExtensionDtype`` incorrectly casting to ``ndarray`` (:issue:`40386`)
- Bug in :meth:`DataFrame.loc` returning :class:`MultiIndex` in wrong order if indexer has duplicates (:issue:`40978`)
- Bug in :meth:`DataFrame.__setitem__` raising ``TypeError`` when using a str subclass as the column name with a :class:`DatetimeIndex` (:issue:`37366`)
- Bug in :meth:`PeriodIndex.get_loc` failing to raise ``KeyError`` when given a :class:`Period` with a mismatched ``freq`` (:issue:`41670`)

Missing
^^^^^^^
Expand Down
25 changes: 3 additions & 22 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
datetime,
timedelta,
)
from typing import (
Any,
Hashable,
)
from typing import Hashable
import warnings

import numpy as np
Expand Down Expand Up @@ -318,24 +315,6 @@ def _is_comparable_dtype(self, dtype: DtypeObj) -> bool:
return False
return dtype.freq == self.freq

# ------------------------------------------------------------------------
# Indexing

@doc(Index.__contains__)
def __contains__(self, key: Any) -> bool:
if isinstance(key, Period):
if key.freq != self.freq:
return False
else:
return key.ordinal in self._engine
else:
hash(key)
try:
self.get_loc(key)
return True
except KeyError:
return False

# ------------------------------------------------------------------------
# Index Methods

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

try:
key = Period(key, freq=self.freq)
Expand Down
35 changes: 28 additions & 7 deletions pandas/tests/indexes/period/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,15 +338,21 @@ def test_get_loc_integer(self):
pi2.get_loc(46)

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

for method in [None, "pad", "backfill", "nearest"]:
assert idx.get_loc(idx[1], method) == 1
assert idx.get_loc(idx[1].asfreq("H", how="start"), method) == 1
assert idx.get_loc(idx[1].to_timestamp(), method) == 1
assert idx.get_loc(idx[1].to_timestamp().to_pydatetime(), method) == 1
assert idx.get_loc(str(idx[1]), method) == 1
assert idx.get_loc(idx[1], method) == 1
assert idx.get_loc(idx[1].to_timestamp(), method) == 1
assert idx.get_loc(idx[1].to_timestamp().to_pydatetime(), method) == 1
assert idx.get_loc(str(idx[1]), method) == 1

key = idx[1].asfreq("H", how="start")
with pytest.raises(KeyError, match=str(key)):
idx.get_loc(key, method=method)

# TODO: This method came from test_period; de-dup with version above
def test_get_loc3(self):

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

def test_get_loc_mismatched_freq(self):
# see also test_get_indexer_mismatched_dtype testing we get analogous
# behavior for get_loc
dti = date_range("2016-01-01", periods=3)
pi = dti.to_period("D")
pi2 = dti.to_period("W")
pi3 = pi.view(pi2.dtype) # i.e. matching i8 representations

with pytest.raises(KeyError, match="W-SUN"):
pi.get_loc(pi2[0])

with pytest.raises(KeyError, match="W-SUN"):
# even though we have matching i8 values
pi.get_loc(pi3[0])


class TestGetIndexer:
def test_get_indexer(self):
Expand Down

0 comments on commit 0b9c558

Please sign in to comment.