From e51a1a4f5af85f5407b46102f6a83b8ea0776b56 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Fri, 2 Aug 2019 16:24:13 -0700 Subject: [PATCH 01/17] BUG: fix+test DTA/TDA/PA add/sub Index --- pandas/core/arrays/datetimelike.py | 6 +++--- pandas/tests/arithmetic/test_datetime64.py | 18 ++++++++++++++++++ pandas/tests/arithmetic/test_period.py | 12 ++++++++++++ pandas/tests/arithmetic/test_timedelta64.py | 19 +++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index f86b307e5ede3..07038729bbbdc 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -1219,7 +1219,7 @@ def _time_shift(self, periods, freq=None): def __add__(self, other): other = lib.item_from_zerodim(other) - if isinstance(other, (ABCSeries, ABCDataFrame)): + if isinstance(other, (ABCSeries, ABCDataFrame, ABCIndexClass)): return NotImplemented # scalar others @@ -1285,7 +1285,7 @@ def __radd__(self, other): def __sub__(self, other): other = lib.item_from_zerodim(other) - if isinstance(other, (ABCSeries, ABCDataFrame)): + if isinstance(other, (ABCSeries, ABCDataFrame, ABCIndexClass)): return NotImplemented # scalar others @@ -1352,7 +1352,7 @@ def __sub__(self, other): return result def __rsub__(self, other): - if is_datetime64_dtype(other) and is_timedelta64_dtype(self): + if is_datetime64_any_dtype(other) and is_timedelta64_dtype(self): # ndarray[datetime64] cannot be subtracted from self, so # we need to wrap in DatetimeArray/Index and flip the operation if not isinstance(other, DatetimeLikeArrayMixin): diff --git a/pandas/tests/arithmetic/test_datetime64.py b/pandas/tests/arithmetic/test_datetime64.py index cca6836acf626..e72ac2fd61c42 100644 --- a/pandas/tests/arithmetic/test_datetime64.py +++ b/pandas/tests/arithmetic/test_datetime64.py @@ -2249,6 +2249,23 @@ def test_add_datetimelike_and_dti(self, addend, tz): # ------------------------------------------------------------- + def test_dta_add_sub_index(self, tz_naive_fixture): + # Check that DatetimeArray defers to Index classes + dti = date_range("20130101", periods=3, tz=tz_naive_fixture) + dta = dti._data + result = dta - dti + expected = dti - dti + tm.assert_index_equal(result, expected) + + tdi = result + result = dta + tdi + expected = dti + tdi + tm.assert_index_equal(result, expected) + + result = dta - tdi + expected = dti - tdi + tm.assert_index_equal(result, expected) + def test_sub_dti_dti(self): # previously performed setop (deprecated in 0.16.0), now changed to # return subtraction -> TimeDeltaIndex (GH ...) @@ -2554,6 +2571,7 @@ def test_shift_months(years, months): tm.assert_index_equal(actual, expected) +# FIXME: this belongs in scalar tests class SubDatetime(datetime): pass diff --git a/pandas/tests/arithmetic/test_period.py b/pandas/tests/arithmetic/test_period.py index e54c16c7a27a4..62f12dd565b07 100644 --- a/pandas/tests/arithmetic/test_period.py +++ b/pandas/tests/arithmetic/test_period.py @@ -1013,6 +1013,18 @@ def test_parr_add_sub_td64_nat(self, box_transpose_fail): with pytest.raises(TypeError): other - obj + # --------------------------------------------------------------- + # Unsorted + + def test_parr_add_sub_index(self): + # Check that PeriodArray defers to Index on arithmetic ops + pi = pd.period_range("2000-12-31", periods=3) + parr = pi._data + + result = parr - pi + expected = pi - pi + tm.assert_index_equal(result, expected) + class TestPeriodSeriesArithmetic: def test_ops_series_timedelta(self): diff --git a/pandas/tests/arithmetic/test_timedelta64.py b/pandas/tests/arithmetic/test_timedelta64.py index 326c565308124..38e346768ae6e 100644 --- a/pandas/tests/arithmetic/test_timedelta64.py +++ b/pandas/tests/arithmetic/test_timedelta64.py @@ -480,6 +480,25 @@ def test_timedelta(self, freq): tm.assert_index_equal(result1, result4) tm.assert_index_equal(result2, result3) + def test_tda_add_sub_index(self): + # Check that TimedeltaArray defers to Index on arithmetic ops + tdi = TimedeltaIndex(["1 days", pd.NaT, "2 days"]) + tda = tdi._data + + dti = pd.date_range("1999-12-31", periods=3, freq="D") + + result = tda + dti + expected = tdi + dti + tm.assert_index_equal(result, expected) + + result = tda + tdi + expected = tdi + tdi + tm.assert_index_equal(result, expected) + + result = tda - tdi + expected = tdi - tdi + tm.assert_index_equal(result, expected) + class TestAddSubNaTMasking: # TODO: parametrize over boxes From 144df100812eb74d9e0d845d06d4e6970b491c41 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Sun, 4 Aug 2019 14:41:51 -0700 Subject: [PATCH 02/17] comment so i can branch --- pandas/core/arrays/datetimelike.py | 8 +++----- pandas/core/arrays/integer.py | 12 ++++++++++-- pandas/core/arrays/numpy_.py | 19 ++++++++++++++++--- pandas/core/indexes/datetimes.py | 4 ++-- pandas/core/indexes/period.py | 2 +- 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 07038729bbbdc..8d768b6252e63 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -190,9 +190,7 @@ def strftime(self, date_format): 'March 10, 2018, 09:00:02 AM'], dtype='object') """ - from pandas import Index - - return Index(self._format_native_types(date_format=date_format)) + return self._format_native_types(date_format=date_format) class TimelikeOps: @@ -1029,9 +1027,9 @@ def _add_delta_tdi(self, other): if isinstance(other, np.ndarray): # ndarray[timedelta64]; wrap in TimedeltaIndex for op - from pandas import TimedeltaIndex + from pandas.core.arrays import TimedeltaArray - other = TimedeltaIndex(other) + other = TimedeltaArray._from_sequence(other) self_i8 = self.asi8 other_i8 = other.asi8 diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py index 1f14bd169a228..de825f655a8b5 100644 --- a/pandas/core/arrays/integer.py +++ b/pandas/core/arrays/integer.py @@ -21,7 +21,12 @@ is_scalar, ) from pandas.core.dtypes.dtypes import register_extension_dtype -from pandas.core.dtypes.generic import ABCIndexClass, ABCSeries +from pandas.core.dtypes.generic import ( + ABCDatetimeArray, + ABCIndexClass, + ABCSeries, + ABCTimedeltaArray, +) from pandas.core.dtypes.missing import isna, notna from pandas.core import nanops, ops @@ -688,8 +693,11 @@ def integer_arithmetic_method(self, other): op_name = op.__name__ mask = None - if isinstance(other, (ABCSeries, ABCIndexClass)): + if isinstance( + other, (ABCSeries, ABCIndexClass, ABCDatetimeArray, ABCTimedeltaArray) + ): # Rely on pandas to unbox and dispatch to us. + # Defer to DatetimeArray, TimedeltaArray return NotImplemented if getattr(other, "ndim", 0) > 1: diff --git a/pandas/core/arrays/numpy_.py b/pandas/core/arrays/numpy_.py index 39529177b9e35..385d77d2c1b9c 100644 --- a/pandas/core/arrays/numpy_.py +++ b/pandas/core/arrays/numpy_.py @@ -9,7 +9,12 @@ from pandas.util._validators import validate_fillna_kwargs from pandas.core.dtypes.dtypes import ExtensionDtype -from pandas.core.dtypes.generic import ABCIndexClass, ABCSeries +from pandas.core.dtypes.generic import ( + ABCDatetimeArray, + ABCIndexClass, + ABCSeries, + ABCTimedeltaArray, +) from pandas.core.dtypes.inference import is_array_like, is_list_like from pandas.core.dtypes.missing import isna @@ -125,7 +130,11 @@ def __init__(self, values, copy=False): if isinstance(values, type(self)): values = values._ndarray if not isinstance(values, np.ndarray): - raise ValueError("'values' must be a NumPy array.") + raise ValueError( + "'values' must be a NumPy array, not {typ}".format( + typ=type(values).__name__ + ) + ) if values.ndim != 1: raise ValueError("PandasArray must be 1-dimensional.") @@ -437,8 +446,12 @@ def __invert__(self): @classmethod def _create_arithmetic_method(cls, op): def arithmetic_method(self, other): - if isinstance(other, (ABCIndexClass, ABCSeries)): + if isinstance( + other, (ABCIndexClass, ABCSeries, ABCDatetimeArray, ABCTimedeltaArray) + ): + # Defer to DatetimeArray, TimedeltaArray return NotImplemented + # TODO: also for IntegerArray? elif isinstance(other, cls): other = other._ndarray diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index d6f0008a2646f..90edb973c1166 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -69,7 +69,7 @@ class DatetimeDelegateMixin(DatetimelikeDelegateMixin): # Some are "raw" methods, the result is not not re-boxed in an Index # We also have a few "extra" attrs, which may or may not be raw, # which we we dont' want to expose in the .dt accessor. - _extra_methods = ["to_period", "to_perioddelta", "to_julian_date"] + _extra_methods = ["to_period", "to_perioddelta", "to_julian_date", "strftime"] _extra_raw_methods = ["to_pydatetime", "_local_timestamps", "_has_same_tz"] _extra_raw_properties = ["_box_func", "tz", "tzinfo"] _delegated_properties = DatetimeArray._datetimelike_ops + _extra_raw_properties @@ -1183,7 +1183,7 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): is_normalized = cache_readonly(DatetimeArray.is_normalized.fget) # type: ignore _resolution = cache_readonly(DatetimeArray._resolution.fget) # type: ignore - strftime = ea_passthrough(DatetimeArray.strftime) + #strftime = ea_passthrough(DatetimeArray.strftime) _has_same_tz = ea_passthrough(DatetimeArray._has_same_tz) @property diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 19fe1eb897f19..fdaa2f3c62daa 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -63,7 +63,7 @@ class PeriodDelegateMixin(DatetimelikeDelegateMixin): _delegate_class = PeriodArray _delegated_properties = PeriodArray._datetimelike_ops - _delegated_methods = set(PeriodArray._datetimelike_methods) | {"_addsub_int_array"} + _delegated_methods = set(PeriodArray._datetimelike_methods) | {"_addsub_int_array", "strftime"} _raw_properties = {"is_leap_year"} From ba8adf1db7ee139058e6743385239f688fb0b20a Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 5 Aug 2019 15:40:17 -0700 Subject: [PATCH 03/17] test for strftime returning ndarray[object] --- pandas/core/arrays/datetimelike.py | 6 +++--- pandas/tests/arrays/test_datetimelike.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 605bbaa54e956..99724e5566a68 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -161,8 +161,8 @@ def strftime(self, date_format): Returns ------- - Index - Index of formatted strings. + ndarray + ndarray of formatted strings. See Also -------- @@ -180,7 +180,7 @@ def strftime(self, date_format): 'March 10, 2018, 09:00:02 AM'], dtype='object') """ - return self._format_native_types(date_format=date_format) + return self._format_native_types(date_format=date_format).astype(object) class TimelikeOps: diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index ffda2f4de2700..0b3ccc0ae0e2d 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -462,6 +462,13 @@ def test_concat_same_type_different_freq(self): tm.assert_datetime_array_equal(result, expected) + def test_strftime(self, datetime_index): + arr = DatetimeArray(datetime_index) + + result = arr.strftime("%Y %b") + expected = np.array(datetime_index.strftime("%Y %b")) + tm.assert_numpy_array_equal(result, expected) + class TestTimedeltaArray(SharedTests): index_cls = pd.TimedeltaIndex @@ -652,6 +659,13 @@ def test_array_interface(self, period_index): expected = np.asarray(arr).astype("S20") tm.assert_numpy_array_equal(result, expected) + def test_strftime(self, period_index): + arr = PeriodArray(period_index) + + result = arr.strftime("%Y") + expected = np.array(period_index.strftime("%Y")) + tm.assert_numpy_array_equal(result, expected) + @pytest.mark.parametrize( "array,casting_nats", From 2038621d8a33bc930b4b906058fd64f619725480 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 5 Aug 2019 15:43:14 -0700 Subject: [PATCH 04/17] un-comment-out --- pandas/core/indexes/datetimes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 2cd4ec0131cec..9f2b31f23d2fa 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -1184,7 +1184,6 @@ def slice_indexer(self, start=None, end=None, step=None, kind=None): is_normalized = cache_readonly(DatetimeArray.is_normalized.fget) # type: ignore _resolution = cache_readonly(DatetimeArray._resolution.fget) # type: ignore - #strftime = ea_passthrough(DatetimeArray.strftime) _has_same_tz = ea_passthrough(DatetimeArray._has_same_tz) @property From ffd9274011db72ac2c5b4ae8c8b39e2e67cf012b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 5 Aug 2019 16:33:48 -0700 Subject: [PATCH 05/17] revert apparently-unnecessary --- pandas/core/arrays/integer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py index de825f655a8b5..3b34869c1fb5d 100644 --- a/pandas/core/arrays/integer.py +++ b/pandas/core/arrays/integer.py @@ -694,7 +694,7 @@ def integer_arithmetic_method(self, other): mask = None if isinstance( - other, (ABCSeries, ABCIndexClass, ABCDatetimeArray, ABCTimedeltaArray) + other, (ABCSeries, ABCIndexClass) ): # Rely on pandas to unbox and dispatch to us. # Defer to DatetimeArray, TimedeltaArray From 030fa64fc34301f1d852358fafdf29e059e33d6b Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Mon, 5 Aug 2019 17:40:53 -0700 Subject: [PATCH 06/17] add failing tests --- pandas/io/pytables.py | 4 ++-- pandas/tests/arrays/test_integer.py | 29 ++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index abc8a414eb37a..0ee0f27bf0f63 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -3202,7 +3202,7 @@ def read(self, start=None, stop=None, **kwargs): values = self.read_array( "block{idx}_values".format(idx=i), start=_start, stop=_stop ) - blk = make_block(values, placement=items.get_indexer(blk_items)) + blk = make_block(values, placement=items.get_indexer(blk_items), ndim=len(axes)) blocks.append(blk) return self.obj_type(BlockManager(blocks, axes)) @@ -4462,7 +4462,7 @@ def read(self, where=None, columns=None, **kwargs): if values.ndim == 1 and isinstance(values, np.ndarray): values = values.reshape((1, values.shape[0])) - block = make_block(values, placement=np.arange(len(cols_))) + block = make_block(values, placement=np.arange(len(cols_)), ndim=2) mgr = BlockManager([block], [cols_, index_]) frames.append(DataFrame(mgr)) diff --git a/pandas/tests/arrays/test_integer.py b/pandas/tests/arrays/test_integer.py index 8fbfb4c12f4b2..736df7d1b9425 100644 --- a/pandas/tests/arrays/test_integer.py +++ b/pandas/tests/arrays/test_integer.py @@ -1,3 +1,5 @@ +import operator + import numpy as np import pytest @@ -16,6 +18,7 @@ UInt32Dtype, UInt64Dtype, ) +from pandas.core.ops import roperator from pandas.tests.extension.base import BaseOpsUtil import pandas.util.testing as tm @@ -342,6 +345,31 @@ def test_rpow_one_to_na(self): expected = np.array([1.0, np.nan]) tm.assert_numpy_array_equal(result, expected) + @pytest.mark.xfail(reason="_add_sub_int_array does not recognize IntArray", raises=TypeError, strict=True) + @pytest.mark.parametrize("other", [ + pd.TimedeltaIndex(np.arange(100).astype("m8[h]"), freq="h"), + pd.date_range("2099-04-05", periods=100), + pd.date_range("2099-04-05", periods=100, tz="Asia/Tokyo"), + ]) + @pytest.mark.parametrize("op", [operator.add, roperator.radd, operator.sub, roperator.rsub]) + @pytest.mark.parametrize("use_array", [True, False]) + def test_intna_with_datetimelike(self, data, other, op, use_array): + if use_array: + other = other.array + + with pytest.warns(FutureWarning, match="Addition/subtraction of integers"): + expected = op(data._data, other) + + if use_array: + expected[data._mask] = pd.NaT + else: + # Index is not mutable, need edit underlying + expected._data[data._mask] = pd.NaT + + with pytest.warns(FutureWarning, match="Addition/subtraction of integers"): + result = op(data, other) + tm.assert_equal(result, expected) + class TestComparisonOps(BaseOpsUtil): def _compare_other(self, data, op_name, other): @@ -379,7 +407,6 @@ def test_compare_array(self, data, all_compare_operators): class TestCasting: - pass @pytest.mark.parametrize("dropna", [True, False]) def test_construct_index(self, all_data, dropna): From 870fb6e25d4be7b6ffcf07a375e03b2a4f208960 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 10:46:16 -0700 Subject: [PATCH 07/17] blackify --- pandas/core/arrays/integer.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py index 3b34869c1fb5d..8d268cd3a38bc 100644 --- a/pandas/core/arrays/integer.py +++ b/pandas/core/arrays/integer.py @@ -693,9 +693,7 @@ def integer_arithmetic_method(self, other): op_name = op.__name__ mask = None - if isinstance( - other, (ABCSeries, ABCIndexClass) - ): + if isinstance(other, (ABCSeries, ABCIndexClass)): # Rely on pandas to unbox and dispatch to us. # Defer to DatetimeArray, TimedeltaArray return NotImplemented From a4e15bc9f612927596ce683be1ea557dda42a4d2 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 10:47:03 -0700 Subject: [PATCH 08/17] remove unused imports --- pandas/core/arrays/integer.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py index 8d268cd3a38bc..78b2401df8e58 100644 --- a/pandas/core/arrays/integer.py +++ b/pandas/core/arrays/integer.py @@ -21,12 +21,7 @@ is_scalar, ) from pandas.core.dtypes.dtypes import register_extension_dtype -from pandas.core.dtypes.generic import ( - ABCDatetimeArray, - ABCIndexClass, - ABCSeries, - ABCTimedeltaArray, -) +from pandas.core.dtypes.generic import ABCIndexClass, ABCSeries from pandas.core.dtypes.missing import isna, notna from pandas.core import nanops, ops From e4c9258d7cce51968838069bb9bed151b6994de1 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 10:47:23 -0700 Subject: [PATCH 09/17] remove no-longer-accurate comment --- pandas/core/arrays/integer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pandas/core/arrays/integer.py b/pandas/core/arrays/integer.py index 78b2401df8e58..1f14bd169a228 100644 --- a/pandas/core/arrays/integer.py +++ b/pandas/core/arrays/integer.py @@ -690,7 +690,6 @@ def integer_arithmetic_method(self, other): if isinstance(other, (ABCSeries, ABCIndexClass)): # Rely on pandas to unbox and dispatch to us. - # Defer to DatetimeArray, TimedeltaArray return NotImplemented if getattr(other, "ndim", 0) > 1: From a15a93fa00905913882fc9dde7903c3dc020ff1e Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 15:55:30 -0700 Subject: [PATCH 10/17] revert --- pandas/core/arrays/numpy_.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/pandas/core/arrays/numpy_.py b/pandas/core/arrays/numpy_.py index 385d77d2c1b9c..79204f9a38414 100644 --- a/pandas/core/arrays/numpy_.py +++ b/pandas/core/arrays/numpy_.py @@ -9,12 +9,7 @@ from pandas.util._validators import validate_fillna_kwargs from pandas.core.dtypes.dtypes import ExtensionDtype -from pandas.core.dtypes.generic import ( - ABCDatetimeArray, - ABCIndexClass, - ABCSeries, - ABCTimedeltaArray, -) +from pandas.core.dtypes.generic import ABCIndexClass, ABCSeries from pandas.core.dtypes.inference import is_array_like, is_list_like from pandas.core.dtypes.missing import isna @@ -446,9 +441,7 @@ def __invert__(self): @classmethod def _create_arithmetic_method(cls, op): def arithmetic_method(self, other): - if isinstance( - other, (ABCIndexClass, ABCSeries, ABCDatetimeArray, ABCTimedeltaArray) - ): + if isinstance(other, (ABCIndexClass, ABCSeries)): # Defer to DatetimeArray, TimedeltaArray return NotImplemented # TODO: also for IntegerArray? From 1dafe5ccf602049fe598f637c84cbe92f9347e7e Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 15:55:55 -0700 Subject: [PATCH 11/17] revert comments --- pandas/core/arrays/numpy_.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pandas/core/arrays/numpy_.py b/pandas/core/arrays/numpy_.py index 79204f9a38414..667fb4501ed95 100644 --- a/pandas/core/arrays/numpy_.py +++ b/pandas/core/arrays/numpy_.py @@ -442,9 +442,7 @@ def __invert__(self): def _create_arithmetic_method(cls, op): def arithmetic_method(self, other): if isinstance(other, (ABCIndexClass, ABCSeries)): - # Defer to DatetimeArray, TimedeltaArray return NotImplemented - # TODO: also for IntegerArray? elif isinstance(other, cls): other = other._ndarray From 3dec0b4986bd28e5dbf99cee8aa0f327c139fafd Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 15:57:40 -0700 Subject: [PATCH 12/17] remove orthogonal test --- pandas/core/indexes/period.py | 5 ++++- pandas/tests/arrays/test_integer.py | 29 ----------------------------- 2 files changed, 4 insertions(+), 30 deletions(-) diff --git a/pandas/core/indexes/period.py b/pandas/core/indexes/period.py index 656a292f9f753..b0cc386f7783d 100644 --- a/pandas/core/indexes/period.py +++ b/pandas/core/indexes/period.py @@ -63,7 +63,10 @@ class PeriodDelegateMixin(DatetimelikeDelegateMixin): _delegate_class = PeriodArray _delegated_properties = PeriodArray._datetimelike_ops - _delegated_methods = set(PeriodArray._datetimelike_methods) | {"_addsub_int_array", "strftime"} + _delegated_methods = set(PeriodArray._datetimelike_methods) | { + "_addsub_int_array", + "strftime", + } _raw_properties = {"is_leap_year"} diff --git a/pandas/tests/arrays/test_integer.py b/pandas/tests/arrays/test_integer.py index 736df7d1b9425..50cd1469e5196 100644 --- a/pandas/tests/arrays/test_integer.py +++ b/pandas/tests/arrays/test_integer.py @@ -1,5 +1,3 @@ -import operator - import numpy as np import pytest @@ -18,7 +16,6 @@ UInt32Dtype, UInt64Dtype, ) -from pandas.core.ops import roperator from pandas.tests.extension.base import BaseOpsUtil import pandas.util.testing as tm @@ -345,31 +342,6 @@ def test_rpow_one_to_na(self): expected = np.array([1.0, np.nan]) tm.assert_numpy_array_equal(result, expected) - @pytest.mark.xfail(reason="_add_sub_int_array does not recognize IntArray", raises=TypeError, strict=True) - @pytest.mark.parametrize("other", [ - pd.TimedeltaIndex(np.arange(100).astype("m8[h]"), freq="h"), - pd.date_range("2099-04-05", periods=100), - pd.date_range("2099-04-05", periods=100, tz="Asia/Tokyo"), - ]) - @pytest.mark.parametrize("op", [operator.add, roperator.radd, operator.sub, roperator.rsub]) - @pytest.mark.parametrize("use_array", [True, False]) - def test_intna_with_datetimelike(self, data, other, op, use_array): - if use_array: - other = other.array - - with pytest.warns(FutureWarning, match="Addition/subtraction of integers"): - expected = op(data._data, other) - - if use_array: - expected[data._mask] = pd.NaT - else: - # Index is not mutable, need edit underlying - expected._data[data._mask] = pd.NaT - - with pytest.warns(FutureWarning, match="Addition/subtraction of integers"): - result = op(data, other) - tm.assert_equal(result, expected) - class TestComparisonOps(BaseOpsUtil): def _compare_other(self, data, op_name, other): @@ -407,7 +379,6 @@ def test_compare_array(self, data, all_compare_operators): class TestCasting: - @pytest.mark.parametrize("dropna", [True, False]) def test_construct_index(self, all_data, dropna): # ensure that we do not coerce to Float64Index, rather From 09c80482e812a73d381e20dedb78f62764839a95 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 16:04:06 -0700 Subject: [PATCH 13/17] fix xfail --- pandas/tests/series/test_period.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/tests/series/test_period.py b/pandas/tests/series/test_period.py index 9b34b52bf39b9..4aeb211170d8f 100644 --- a/pandas/tests/series/test_period.py +++ b/pandas/tests/series/test_period.py @@ -71,10 +71,9 @@ def test_NaT_scalar(self): series[2] = val assert pd.isna(series[2]) - @pytest.mark.xfail(reason="PeriodDtype Series not supported yet") def test_NaT_cast(self): result = Series([np.nan]).astype("period[D]") - expected = Series([pd.NaT]) + expected = Series([pd.NaT], dtype="period[D]") tm.assert_series_equal(result, expected) def test_set_none(self): From fe0d5ece55ef3b57f3bd647aa9913949b7b85633 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 16:21:57 -0700 Subject: [PATCH 14/17] Assorted cleanups --- pandas/core/algorithms.py | 5 ++--- pandas/core/arrays/categorical.py | 17 +++++++---------- pandas/core/arrays/sparse.py | 8 ++++---- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index 21d12d02c9008..4e03ca266465f 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -838,7 +838,7 @@ def duplicated(values, keep="first"): return f(values, keep=keep) -def mode(values, dropna=True): +def mode(values, dropna: bool = True): """ Returns the mode(s) of an array. @@ -1901,7 +1901,7 @@ def searchsorted(arr, value, side="left", sorter=None): } -def diff(arr, n, axis=0): +def diff(arr, n: int, axis: int = 0): """ difference of n between self, analogous to s-s.shift(n) @@ -1917,7 +1917,6 @@ def diff(arr, n, axis=0): Returns ------- shifted - """ n = int(n) diff --git a/pandas/core/arrays/categorical.py b/pandas/core/arrays/categorical.py index d22b4bd4d3f2b..69fa956b73f28 100644 --- a/pandas/core/arrays/categorical.py +++ b/pandas/core/arrays/categorical.py @@ -22,7 +22,6 @@ ensure_int64, ensure_object, ensure_platform_int, - is_categorical, is_categorical_dtype, is_datetime64_dtype, is_datetimelike, @@ -2659,18 +2658,18 @@ def _get_codes_for_values(values, categories): return coerce_indexer_dtype(t.lookup(vals), cats) -def _recode_for_categories(codes, old_categories, new_categories): +def _recode_for_categories(codes: np.ndarray, old_categories, new_categories): """ Convert a set of codes for to a new set of categories Parameters ---------- - codes : array + codes : np.ndarray old_categories, new_categories : Index Returns ------- - new_codes : array + new_codes : np.ndarray[np.int64] Examples -------- @@ -2725,17 +2724,15 @@ def _factorize_from_iterable(values): If `values` has a categorical dtype, then `categories` is a CategoricalIndex keeping the categories and order of `values`. """ - from pandas.core.indexes.category import CategoricalIndex - if not is_list_like(values): raise TypeError("Input must be list-like") - if is_categorical(values): - values = CategoricalIndex(values) - # The CategoricalIndex level we want to build has the same categories + if is_categorical_dtype(values): + values = extract_array(values) + # The Categorical we want to build has the same categories # as values but its codes are by def [0, ..., len(n_categories) - 1] cat_codes = np.arange(len(values.categories), dtype=values.codes.dtype) - categories = values._create_from_codes(cat_codes) + categories = Categorical.from_codes(cat_codes, dtype=values.dtype) codes = values.codes else: # The value of ordered is irrelevant since we don't use cat as such, diff --git a/pandas/core/arrays/sparse.py b/pandas/core/arrays/sparse.py index 47c7c72051150..476e2aa223d03 100644 --- a/pandas/core/arrays/sparse.py +++ b/pandas/core/arrays/sparse.py @@ -1781,11 +1781,11 @@ def sparse_arithmetic_method(self, other): @classmethod def _create_comparison_method(cls, op): - def cmp_method(self, other): - op_name = op.__name__ + op_name = op.__name__ + if op_name in {"and_", "or_"}: + op_name = op_name[:-1] - if op_name in {"and_", "or_"}: - op_name = op_name[:-1] + def cmp_method(self, other): if isinstance(other, (ABCSeries, ABCIndexClass)): # Rely on pandas to unbox and dispatch to us. From 394883fc9dcc6c3c65c0f8fc372167d14a212307 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 17:03:49 -0700 Subject: [PATCH 15/17] blackify --- pandas/io/pytables.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pandas/io/pytables.py b/pandas/io/pytables.py index 0ee0f27bf0f63..6af5dd6f1bf37 100644 --- a/pandas/io/pytables.py +++ b/pandas/io/pytables.py @@ -3202,7 +3202,9 @@ def read(self, start=None, stop=None, **kwargs): values = self.read_array( "block{idx}_values".format(idx=i), start=_start, stop=_stop ) - blk = make_block(values, placement=items.get_indexer(blk_items), ndim=len(axes)) + blk = make_block( + values, placement=items.get_indexer(blk_items), ndim=len(axes) + ) blocks.append(blk) return self.obj_type(BlockManager(blocks, axes)) From c447d3c1a05cdc85857e3fe23f465371e5c12e7c Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 17:55:23 -0700 Subject: [PATCH 16/17] revert to fix mypy --- pandas/core/algorithms.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/core/algorithms.py b/pandas/core/algorithms.py index 4e03ca266465f..21d12d02c9008 100644 --- a/pandas/core/algorithms.py +++ b/pandas/core/algorithms.py @@ -838,7 +838,7 @@ def duplicated(values, keep="first"): return f(values, keep=keep) -def mode(values, dropna: bool = True): +def mode(values, dropna=True): """ Returns the mode(s) of an array. @@ -1901,7 +1901,7 @@ def searchsorted(arr, value, side="left", sorter=None): } -def diff(arr, n: int, axis: int = 0): +def diff(arr, n, axis=0): """ difference of n between self, analogous to s-s.shift(n) @@ -1917,6 +1917,7 @@ def diff(arr, n: int, axis: int = 0): Returns ------- shifted + """ n = int(n) From c118ac382155d4ba351e53428b7510be13f263d4 Mon Sep 17 00:00:00 2001 From: jbrockmendel Date: Tue, 6 Aug 2019 17:56:41 -0700 Subject: [PATCH 17/17] try to make docstring validation happy --- pandas/core/arrays/datetimelike.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/core/arrays/datetimelike.py b/pandas/core/arrays/datetimelike.py index 99724e5566a68..b3548a1dc20d6 100644 --- a/pandas/core/arrays/datetimelike.py +++ b/pandas/core/arrays/datetimelike.py @@ -162,7 +162,7 @@ def strftime(self, date_format): Returns ------- ndarray - ndarray of formatted strings. + NumPy ndarray of formatted strings. See Also --------