Skip to content

Implement __array__ on ExtensionIndex #32255

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 7, 2020
2 changes: 2 additions & 0 deletions doc/source/whatsnew/v1.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Backwards incompatible API changes
now raise a ``TypeError`` if a not-accepted keyword argument is passed into it.
Previously a ``UnsupportedFunctionCall`` was raised (``AssertionError`` if ``min_count`` passed into :meth:`~DataFrameGroupby.median``) (:issue:`31485`)
- :meth:`DataFrame.at` and :meth:`Series.at` will raise a ``TypeError`` instead of a ``ValueError`` if an incompatible key is passed, and ``KeyError`` if a missing key is passed, matching the behavior of ``.loc[]`` (:issue:`31722`)
- Passing an integer dtype other than ``int64`` to ``np.array(period_index, dtype=...)`` will now raise ``TypeError`` instead of incorrectly using ``int64`` (:issue:`32255`)
-

.. _whatsnew_110.api_breaking.indexing_raises_key_errors:

Expand Down
7 changes: 6 additions & 1 deletion pandas/core/arrays/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,12 @@ def freq(self):
return self.dtype.freq

def __array__(self, dtype=None) -> np.ndarray:
# overriding DatetimelikeArray
if dtype == "i8":
return self.asi8
elif dtype == bool:
return ~self._isnan

# This will raise TypeErorr for non-object dtypes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo for follow-up

return np.array(list(self), dtype=object)

def __arrow_array__(self, type=None):
Expand Down
4 changes: 0 additions & 4 deletions pandas/core/indexes/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,6 @@ def __contains__(self, key: Any) -> bool:
hash(key)
return contains(self, key, container=self._engine)

def __array__(self, dtype=None) -> np.ndarray:
""" the array interface, return my values """
return np.array(self._data, dtype=dtype)

@Appender(Index.astype.__doc__)
def astype(self, dtype, copy=True):
if is_interval_dtype(dtype):
Expand Down
3 changes: 0 additions & 3 deletions pandas/core/indexes/datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,6 @@ def _simple_new(cls, values, name=None, freq=None, tz=None, dtype=None):

# --------------------------------------------------------------------

def __array__(self, dtype=None) -> np.ndarray:
return np.asarray(self._data, dtype=dtype)

@cache_readonly
def _is_dates_only(self) -> bool:
"""
Expand Down
3 changes: 3 additions & 0 deletions pandas/core/indexes/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ def __iter__(self):

# ---------------------------------------------------------------------

def __array__(self, dtype=None) -> np.ndarray:
return np.asarray(self._data, dtype=dtype)

@property
def _ndarray_values(self) -> np.ndarray:
return self._data._ndarray_values
Expand Down
7 changes: 0 additions & 7 deletions pandas/core/indexes/period.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
is_dtype_equal,
is_float,
is_integer,
is_integer_dtype,
is_object_dtype,
is_scalar,
pandas_dtype,
Expand Down Expand Up @@ -338,12 +337,6 @@ def _int64index(self) -> Int64Index:
# ------------------------------------------------------------------------
# Index Methods

def __array__(self, dtype=None) -> np.ndarray:
if is_integer_dtype(dtype):
return self.asi8
else:
return self.astype(object).values

def __array_wrap__(self, result, context=None):
"""
Gets called after a ufunc. Needs additional handling as
Expand Down
6 changes: 3 additions & 3 deletions pandas/tests/arrays/test_datetimelike.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,10 +687,10 @@ def test_array_interface(self, period_index):
result = np.asarray(arr, dtype=object)
tm.assert_numpy_array_equal(result, expected)

# to other dtypes
with pytest.raises(TypeError):
np.asarray(arr, dtype="int64")
result = np.asarray(arr, dtype="int64")
tm.assert_numpy_array_equal(result, arr.asi8)

# to other dtypes
with pytest.raises(TypeError):
np.asarray(arr, dtype="float64")

Expand Down
29 changes: 29 additions & 0 deletions pandas/tests/indexes/period/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,3 +681,32 @@ def test_is_monotonic_with_nat():
assert not obj.is_monotonic_increasing
assert not obj.is_monotonic_decreasing
assert obj.is_unique


@pytest.mark.parametrize("array", [True, False])
def test_dunder_array(array):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have this test for indexes generally?

obj = PeriodIndex(["2000-01-01", "2001-01-01"], freq="D")
if array:
obj = obj._data

expected = np.array([obj[0], obj[1]], dtype=object)
result = np.array(obj)
tm.assert_numpy_array_equal(result, expected)

result = np.asarray(obj)
tm.assert_numpy_array_equal(result, expected)

expected = obj.asi8
for dtype in ["i8", "int64", np.int64]:
result = np.array(obj, dtype=dtype)
tm.assert_numpy_array_equal(result, expected)

result = np.asarray(obj, dtype=dtype)
tm.assert_numpy_array_equal(result, expected)

for dtype in ["float64", "int32", "uint64"]:
msg = "argument must be"
with pytest.raises(TypeError, match=msg):
np.array(obj, dtype=dtype)
with pytest.raises(TypeError, match=msg):
np.array(obj, dtype=getattr(np, dtype))