From b6f55b3e392f688e894306a3b0cf86d78ee2125c Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 25 Feb 2020 16:13:15 -0800 Subject: [PATCH 1/4] Implement __array__ on ExtensionIndex --- doc/source/whatsnew/v1.1.0.rst | 1 + pandas/core/arrays/period.py | 7 ++++-- pandas/core/indexes/category.py | 4 ---- pandas/core/indexes/datetimes.py | 3 --- pandas/core/indexes/extension.py | 3 +++ pandas/core/indexes/period.py | 7 ------ pandas/tests/indexes/period/test_period.py | 28 ++++++++++++++++++++++ 7 files changed, 37 insertions(+), 16 deletions(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 705c335acfb48..291d478427f85 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -67,6 +67,7 @@ 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 ``NotImplementedError`` instead of incorrectly using ``int64`` (:issue:`?????`) - .. --------------------------------------------------------------------------- diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index 8141e2c78a7e2..deb3e75c5544a 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -282,8 +282,11 @@ def freq(self): return self.dtype.freq def __array__(self, dtype=None) -> np.ndarray: - # overriding DatetimelikeArray - return np.array(list(self), dtype=object) + if dtype is None or dtype == object: + return np.array(list(self), dtype=object) + elif dtype == "i8": + return self.asi8 + raise NotImplementedError(dtype) def __arrow_array__(self, type=None): """ diff --git a/pandas/core/indexes/category.py b/pandas/core/indexes/category.py index caa6a9a93141f..83348aad64458 100644 --- a/pandas/core/indexes/category.py +++ b/pandas/core/indexes/category.py @@ -385,10 +385,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): diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index e303e487b1a7d..a69aa5e15d270 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -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: """ diff --git a/pandas/core/indexes/extension.py b/pandas/core/indexes/extension.py index daccb35864e98..7b11df15f69fb 100644 --- a/pandas/core/indexes/extension.py +++ b/pandas/core/indexes/extension.py @@ -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 diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 0b85433b699a8..26bb1ed4794f4 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -17,7 +17,6 @@ is_dtype_equal, is_float, is_integer, - is_integer_dtype, is_object_dtype, is_scalar, pandas_dtype, @@ -357,12 +356,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 diff --git a/pandas/tests/indexes/period/test_period.py b/pandas/tests/indexes/period/test_period.py index 6479b14e9521e..f1abd435c828e 100644 --- a/pandas/tests/indexes/period/test_period.py +++ b/pandas/tests/indexes/period/test_period.py @@ -683,3 +683,31 @@ 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): + 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"]: + with pytest.raises(NotImplementedError, match=dtype): + np.array(obj, dtype=dtype) + with pytest.raises(NotImplementedError, match=dtype): + np.array(obj, dtype=getattr(np, dtype)) From 2a999c2b85d029966e21d08245f059688866eb8b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 25 Feb 2020 16:14:07 -0800 Subject: [PATCH 2/4] update GH ref --- doc/source/whatsnew/v1.1.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 291d478427f85..825c65a287cf4 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -67,7 +67,7 @@ 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 ``NotImplementedError`` instead of incorrectly using ``int64`` (:issue:`?????`) +- Passing an integer dtype other than ``int64`` to ``np.array(period_index, dtype=...)`` will now raise ``NotImplementedError`` instead of incorrectly using ``int64`` (:issue:`32255`) - .. --------------------------------------------------------------------------- From 6572964431eb8eda12843d5ff9ac930b2f467c5a Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 26 Feb 2020 09:28:49 -0800 Subject: [PATCH 3/4] NotImplementedError->TypeError --- pandas/core/arrays/period.py | 10 ++++++---- pandas/tests/arrays/test_datetimelike.py | 6 +++--- pandas/tests/indexes/period/test_period.py | 5 +++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pandas/core/arrays/period.py b/pandas/core/arrays/period.py index deb3e75c5544a..5eeee644b3854 100644 --- a/pandas/core/arrays/period.py +++ b/pandas/core/arrays/period.py @@ -282,11 +282,13 @@ def freq(self): return self.dtype.freq def __array__(self, dtype=None) -> np.ndarray: - if dtype is None or dtype == object: - return np.array(list(self), dtype=object) - elif dtype == "i8": + if dtype == "i8": return self.asi8 - raise NotImplementedError(dtype) + elif dtype == bool: + return ~self._isnan + + # This will raise TypeErorr for non-object dtypes + return np.array(list(self), dtype=object) def __arrow_array__(self, type=None): """ diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index 17818b6ce689f..f99ee542d543c 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -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") diff --git a/pandas/tests/indexes/period/test_period.py b/pandas/tests/indexes/period/test_period.py index 1d2c3cd71b89a..2993161ff3bd5 100644 --- a/pandas/tests/indexes/period/test_period.py +++ b/pandas/tests/indexes/period/test_period.py @@ -701,7 +701,8 @@ def test_dunder_array(array): tm.assert_numpy_array_equal(result, expected) for dtype in ["float64", "int32", "uint64"]: - with pytest.raises(NotImplementedError, match=dtype): + msg = "argument must be" + with pytest.raises(TypeError, match=msg): np.array(obj, dtype=dtype) - with pytest.raises(NotImplementedError, match=dtype): + with pytest.raises(TypeError, match=msg): np.array(obj, dtype=getattr(np, dtype)) From e94aa5cd88189bedf835c76fb520771bff4ce477 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Wed, 4 Mar 2020 16:59:49 -0800 Subject: [PATCH 4/4] fix incorrect whatnew --- doc/source/whatsnew/v1.1.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.1.0.rst b/doc/source/whatsnew/v1.1.0.rst index 8e5cdd5dbe8b2..63ed4f16c08fe 100644 --- a/doc/source/whatsnew/v1.1.0.rst +++ b/doc/source/whatsnew/v1.1.0.rst @@ -91,7 +91,7 @@ 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 ``NotImplementedError`` instead of incorrectly using ``int64`` (:issue:`32255`) +- 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: