Skip to content

Commit 2805c8d

Browse files
committed
List indexer on PeriodIndex doesn't coerce strings (#11278)
1 parent db491cb commit 2805c8d

File tree

6 files changed

+84
-2
lines changed

6 files changed

+84
-2
lines changed

doc/source/whatsnew/v1.1.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ Indexing
148148
^^^^^^^^
149149
- Bug in slicing on a :class:`DatetimeIndex` with a partial-timestamp dropping high-resolution indices near the end of a year, quarter, or month (:issue:`31064`)
150150
- Bug in :meth:`PeriodIndex.get_loc` treating higher-resolution strings differently from :meth:`PeriodIndex.get_value` (:issue:`31172`)
151+
- Indexer which is list of string has a datetime format fails on :class:`PeriodIndex` or :class:`DatetimeIndex` (:issue:`11278`)
151152
-
152153

153154
Missing

pandas/core/common.py

+9
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020
from pandas.core.dtypes.common import (
2121
is_array_like,
2222
is_bool_dtype,
23+
is_datetime64_any_dtype,
2324
is_extension_array_dtype,
2425
is_integer,
26+
is_period_dtype,
2527
)
2628
from pandas.core.dtypes.generic import ABCIndex, ABCIndexClass, ABCSeries
2729
from pandas.core.dtypes.inference import _iterable_not_string
2830
from pandas.core.dtypes.missing import isna, isnull, notnull # noqa
2931

32+
from pandas.core.construction import array
33+
3034

3135
class SettingWithCopyError(ValueError):
3236
pass
@@ -231,6 +235,11 @@ def asarray_tuplesafe(values, dtype=None):
231235
if isinstance(values, list) and dtype in [np.object_, object]:
232236
return construct_1d_object_array_from_listlike(values)
233237

238+
if isinstance(values, list) and (
239+
is_datetime64_any_dtype(dtype) or is_period_dtype(dtype)
240+
):
241+
return array(values, dtype)
242+
234243
result = np.asarray(values, dtype=dtype)
235244

236245
if issubclass(result.dtype.type, str):

pandas/core/indexes/base.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3264,7 +3264,8 @@ def _convert_listlike_indexer(self, keyarr, kind=None):
32643264

32653265
@Appender(_index_shared_docs["_convert_arr_indexer"])
32663266
def _convert_arr_indexer(self, keyarr):
3267-
keyarr = com.asarray_tuplesafe(keyarr)
3267+
dtype = self.dtype if isinstance(keyarr, list) else None
3268+
keyarr = com.asarray_tuplesafe(keyarr, dtype)
32683269
return keyarr
32693270

32703271
_index_shared_docs[

pandas/core/indexes/datetimes.py

+3
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,9 @@ def indexer_between_time(
901901

902902
return mask.nonzero()[0]
903903

904+
def _convert_list_indexer(self, keyarr, kind=None):
905+
return super().get_indexer_for(keyarr)
906+
904907

905908
DatetimeIndex._add_numeric_methods_disabled()
906909
DatetimeIndex._add_logical_methods_disabled()

pandas/core/indexes/period.py

+3
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,9 @@ def memory_usage(self, deep=False):
823823
result += self._int64index.memory_usage(deep=deep)
824824
return result
825825

826+
def _convert_list_indexer(self, keyarr, kind=None):
827+
return super().get_indexer_for(keyarr)
828+
826829

827830
PeriodIndex._add_numeric_methods_disabled()
828831
PeriodIndex._add_logical_methods_disabled()

pandas/tests/indexing/test_loc.py

+66-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import pytest
77

88
import pandas as pd
9-
from pandas import DataFrame, Series, Timestamp, date_range
9+
from pandas import DataFrame, Period, Series, Timestamp, date_range, period_range
1010
import pandas._testing as tm
1111
from pandas.api.types import is_scalar
1212
from pandas.tests.indexing.common import Base
@@ -942,6 +942,71 @@ def test_loc_reverse_assignment(self):
942942

943943
tm.assert_series_equal(result, expected)
944944

945+
@pytest.mark.parametrize(
946+
"idx,labels,expected_idx",
947+
[
948+
(
949+
period_range(start="2000", periods=20, freq="D"),
950+
["2000-01-04", "2000-01-08", "2000-01-12"],
951+
[
952+
Period("2000-01-04", freq="D"),
953+
Period("2000-01-08", freq="D"),
954+
Period("2000-01-12", freq="D"),
955+
],
956+
),
957+
(
958+
date_range(start="2000", periods=20, freq="D"),
959+
["2000-01-04", "2000-01-08", "2000-01-12"],
960+
[
961+
Timestamp("2000-01-04", freq="D"),
962+
Timestamp("2000-01-08", freq="D"),
963+
Timestamp("2000-01-12", freq="D"),
964+
],
965+
),
966+
],
967+
)
968+
def test_loc_with_datetime_string_list(self, idx, labels, expected_idx):
969+
# GH 11278
970+
s = Series(range(20), index=idx)
971+
df = DataFrame(range(20), index=idx)
972+
973+
expected_value = [3, 7, 11]
974+
expected_s = Series(expected_value, expected_idx)
975+
expected_df = DataFrame(expected_value, expected_idx)
976+
977+
tm.assert_series_equal(expected_s, s.loc[labels])
978+
tm.assert_series_equal(expected_s, s[labels])
979+
tm.assert_frame_equal(expected_df, df.loc[labels])
980+
981+
@pytest.mark.parametrize(
982+
"idx,labels,msg",
983+
[
984+
(
985+
period_range(start="2000", periods=20, freq="D"),
986+
["2000-01-04", "2000-01-30"],
987+
r"None of \[PeriodIndex\(\['2000-01-04', '2000-01-30'\], "
988+
r"dtype='period\[D\]', freq='D'\)\] are in the \[index\]",
989+
),
990+
(
991+
date_range(start="2000", periods=20, freq="D"),
992+
["2000-01-04", "2000-01-30"],
993+
r"None of \[DatetimeIndex\(\['2000-01-04', '2000-01-30'\], "
994+
r"dtype='datetime64\[ns\]', freq=None\)\] are in the \[index\]",
995+
),
996+
],
997+
)
998+
def test_loc_with_datetime_string_and_missing_value(self, idx, labels, msg):
999+
# GH 11278
1000+
s = Series(range(20), index=idx)
1001+
df = DataFrame(range(20), index=idx)
1002+
1003+
with pytest.raises(KeyError, match=msg):
1004+
s.loc[labels]
1005+
with pytest.raises(KeyError, match=msg):
1006+
s[labels]
1007+
with pytest.raises(KeyError, match=msg):
1008+
df.loc[labels]
1009+
9451010

9461011
def test_series_loc_getitem_label_list_missing_values():
9471012
# gh-11428

0 commit comments

Comments
 (0)