From 7ba53f0e0e61b0e542bf553707a1df7cf8cfa83e Mon Sep 17 00:00:00 2001 From: Marco Gorelli <33491632+MarcoGorelli@users.noreply.github.com> Date: Mon, 13 Jan 2020 19:03:25 +0000 Subject: [PATCH 1/2] BUG: -1 to the power of pd.NA was returning -1 (#30960) --- doc/source/user_guide/missing_data.rst | 1 - pandas/_libs/missing.pyx | 4 ++-- pandas/tests/arrays/test_integer.py | 12 ++++++----- pandas/tests/scalar/test_na_scalar.py | 29 ++++++++++++++------------ 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/doc/source/user_guide/missing_data.rst b/doc/source/user_guide/missing_data.rst index abbb6feef6056..df9949e8ac261 100644 --- a/doc/source/user_guide/missing_data.rst +++ b/doc/source/user_guide/missing_data.rst @@ -831,7 +831,6 @@ Operation Result ================ ====== ``pd.NA ** 0`` 0 ``1 ** pd.NA`` 1 -``-1 ** pd.NA`` -1 ================ ====== In equality and comparison operations, ``pd.NA`` also propagates. This deviates diff --git a/pandas/_libs/missing.pyx b/pandas/_libs/missing.pyx index 26653438356b1..4d17a6f883c1c 100644 --- a/pandas/_libs/missing.pyx +++ b/pandas/_libs/missing.pyx @@ -417,12 +417,12 @@ class NAType(C_NAType): if other is C_NA: return NA elif isinstance(other, (numbers.Number, np.bool_)): - if other == 1 or other == -1: + if other == 1: return other else: return NA elif isinstance(other, np.ndarray): - return np.where((other == 1) | (other == -1), other, NA) + return np.where(other == 1, other, NA) return NotImplemented diff --git a/pandas/tests/arrays/test_integer.py b/pandas/tests/arrays/test_integer.py index 0c8980c43c370..f1a7cc741603d 100644 --- a/pandas/tests/arrays/test_integer.py +++ b/pandas/tests/arrays/test_integer.py @@ -363,24 +363,26 @@ def test_divide_by_zero(self, zero, negative): tm.assert_numpy_array_equal(result, expected) def test_pow_scalar(self): - a = pd.array([0, 1, None, 2], dtype="Int64") + a = pd.array([-1, 0, 1, None, 2], dtype="Int64") result = a ** 0 - expected = pd.array([1, 1, 1, 1], dtype="Int64") + expected = pd.array([1, 1, 1, 1, 1], dtype="Int64") tm.assert_extension_array_equal(result, expected) result = a ** 1 - expected = pd.array([0, 1, None, 2], dtype="Int64") + expected = pd.array([-1, 0, 1, None, 2], dtype="Int64") tm.assert_extension_array_equal(result, expected) result = a ** pd.NA - expected = pd.array([None, 1, None, None], dtype="Int64") + expected = pd.array([None, None, 1, None, None], dtype="Int64") tm.assert_extension_array_equal(result, expected) result = a ** np.nan - expected = np.array([np.nan, 1, np.nan, np.nan], dtype="float64") + expected = np.array([np.nan, np.nan, 1, np.nan, np.nan], dtype="float64") tm.assert_numpy_array_equal(result, expected) # reversed + a = a[1:] # Can't raise integers to negative powers. + result = 0 ** a expected = pd.array([1, 0, None, 0], dtype="Int64") tm.assert_extension_array_equal(result, expected) diff --git a/pandas/tests/scalar/test_na_scalar.py b/pandas/tests/scalar/test_na_scalar.py index 7d05511239ebc..dcb9d66708724 100644 --- a/pandas/tests/scalar/test_na_scalar.py +++ b/pandas/tests/scalar/test_na_scalar.py @@ -96,19 +96,7 @@ def test_pow_special(value, asarray): @pytest.mark.parametrize( - "value", - [ - 1, - 1.0, - -1, - -1.0, - True, - np.bool_(True), - np.int_(1), - np.float_(1), - np.int_(-1), - np.float_(-1), - ], + "value", [1, 1.0, True, np.bool_(True), np.int_(1), np.float_(1)], ) @pytest.mark.parametrize("asarray", [True, False]) def test_rpow_special(value, asarray): @@ -125,6 +113,21 @@ def test_rpow_special(value, asarray): assert result == value +@pytest.mark.parametrize( + "value", [-1, -1.0, np.int_(-1), np.float_(-1)], +) +@pytest.mark.parametrize("asarray", [True, False]) +def test_rpow_minus_one(value, asarray): + if asarray: + value = np.array([value]) + result = value ** pd.NA + + if asarray: + result = result[0] + + assert pd.isna(result) + + def test_unary_ops(): assert +NA is NA assert -NA is NA From 67fcdefbd42921faa116558d5cf93635dd6fb1fc Mon Sep 17 00:00:00 2001 From: Terji Petersen Date: Mon, 13 Jan 2020 19:28:23 +0000 Subject: [PATCH 2/2] TYP: NDFrame.resample (#30947) --- pandas/core/frame.py | 7 ++++--- pandas/core/generic.py | 10 +++++++--- pandas/core/resample.py | 4 ++-- pandas/core/series.py | 7 ++++--- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 676b78573399c..594b8a00a8672 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -102,7 +102,6 @@ from pandas.core.arrays.datetimelike import DatetimeLikeArrayMixin as DatetimeLikeArray from pandas.core.arrays.sparse import SparseFrameAccessor from pandas.core.generic import NDFrame, _shared_docs -from pandas.core.groupby import generic as groupby_generic from pandas.core.indexes import base as ibase from pandas.core.indexes.api import Index, ensure_index, ensure_index_from_sequences from pandas.core.indexes.datetimes import DatetimeIndex @@ -129,6 +128,7 @@ import pandas.plotting if TYPE_CHECKING: + from pandas.core.groupby.generic import DataFrameGroupBy from pandas.io.formats.style import Styler # --------------------------------------------------------------------- @@ -5777,13 +5777,14 @@ def groupby( group_keys: bool = True, squeeze: bool = False, observed: bool = False, - ) -> "groupby_generic.DataFrameGroupBy": + ) -> "DataFrameGroupBy": + from pandas.core.groupby.generic import DataFrameGroupBy if level is None and by is None: raise TypeError("You have to supply one of 'by' and 'level'") axis = self._get_axis_number(axis) - return groupby_generic.DataFrameGroupBy( + return DataFrameGroupBy( obj=self, keys=by, axis=axis, diff --git a/pandas/core/generic.py b/pandas/core/generic.py index 04ce424edbee4..05066ac0ec128 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -8,6 +8,7 @@ import re from textwrap import dedent from typing import ( + TYPE_CHECKING, Any, Callable, Dict, @@ -101,6 +102,9 @@ from pandas.io.formats.printing import pprint_thing from pandas.tseries.frequencies import to_offset +if TYPE_CHECKING: + from pandas.core.resample import Resampler + # goal is to be able to define the docs close to function, while still being # able to share _shared_docs: Dict[str, str] = dict() @@ -7685,7 +7689,7 @@ def resample( base: int = 0, on=None, level=None, - ): + ) -> "Resampler": """ Resample time-series data. @@ -7950,10 +7954,10 @@ def resample( 2000-01-04 36 90 """ - from pandas.core.resample import resample + from pandas.core.resample import get_resampler axis = self._get_axis_number(axis) - return resample( + return get_resampler( self, freq=rule, label=label, diff --git a/pandas/core/resample.py b/pandas/core/resample.py index 0e43880dfda07..fb837409a00f5 100644 --- a/pandas/core/resample.py +++ b/pandas/core/resample.py @@ -1262,7 +1262,7 @@ def _constructor(self): return TimedeltaIndexResampler -def resample(obj, kind=None, **kwds): +def get_resampler(obj, kind=None, **kwds): """ Create a TimeGrouper and return our resampler. """ @@ -1270,7 +1270,7 @@ def resample(obj, kind=None, **kwds): return tg._get_resampler(obj, kind=kind) -resample.__doc__ = Resampler.__doc__ +get_resampler.__doc__ = Resampler.__doc__ def get_resampler_for_grouping( diff --git a/pandas/core/series.py b/pandas/core/series.py index ed338700f1011..fe5c5fd5e2bc8 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -70,7 +70,6 @@ is_empty_data, sanitize_array, ) -from pandas.core.groupby import generic as groupby_generic from pandas.core.indexers import maybe_convert_indices from pandas.core.indexes.accessors import CombinedDatetimelikeProperties from pandas.core.indexes.api import ( @@ -94,6 +93,7 @@ if TYPE_CHECKING: from pandas.core.frame import DataFrame + from pandas.core.groupby.generic import SeriesGroupBy __all__ = ["Series"] @@ -1634,13 +1634,14 @@ def groupby( group_keys: bool = True, squeeze: bool = False, observed: bool = False, - ) -> "groupby_generic.SeriesGroupBy": + ) -> "SeriesGroupBy": + from pandas.core.groupby.generic import SeriesGroupBy if level is None and by is None: raise TypeError("You have to supply one of 'by' and 'level'") axis = self._get_axis_number(axis) - return groupby_generic.SeriesGroupBy( + return SeriesGroupBy( obj=self, keys=by, axis=axis,