From 0713a0b6c6b89e10971d683690e81023045afb53 Mon Sep 17 00:00:00 2001 From: jreback Date: Sat, 15 Feb 2014 11:07:38 -0500 Subject: [PATCH] BUG: correctly handle placement of pos/neg inf when dividing by integer 0 (GH6178) --- doc/source/release.rst | 1 + pandas/core/common.py | 28 ++++++++++++++++++++++++---- pandas/core/ops.py | 6 +++--- pandas/core/panel.py | 2 +- pandas/tests/test_panel.py | 13 +++++-------- pandas/tests/test_series.py | 6 ++++++ 6 files changed, 40 insertions(+), 16 deletions(-) diff --git a/doc/source/release.rst b/doc/source/release.rst index 35ce6c9359d56..aef3fc289538a 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -105,6 +105,7 @@ Bug Fixes - TimeGrouper has a more compatible API to the rest of the groupers (e.g. ``groups`` was missing) (:issue:`3881`) - Bug in ``pd.eval`` when parsing strings with possible tokens like ``'&'`` (:issue:`6351`) +- Bug correctly handle placements of ``-inf`` in Panels when dividing by integer 0 (:issue:`6359`) pandas 0.13.1 ------------- diff --git a/pandas/core/common.py b/pandas/core/common.py index e895c8ed0cf2d..9ea81cfdd0e0c 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -1179,13 +1179,23 @@ def _lcd_dtypes(a_dtype, b_dtype): return np.object -def _fill_zeros(result, y, fill): - """ if we have an integer value (or array in y) +def _fill_zeros(result, x, y, name, fill): + """ + if this is a reversed op, then flip x,y + + if we have an integer value (or array in y) and we have 0's, fill them with the fill, return the result + + mask the nan's from x """ if fill is not None: + + if name.startswith('r'): + x,y = y,x + + if not isinstance(y, np.ndarray): dtype, value = _infer_dtype_from_scalar(y) y = pa.empty(result.shape, dtype=dtype) @@ -1196,8 +1206,18 @@ def _fill_zeros(result, y, fill): mask = y.ravel() == 0 if mask.any(): shape = result.shape - result, changed = _maybe_upcast_putmask( - result.ravel(), mask, fill) + result = result.ravel().astype('float64') + + signs = np.sign(result) + nans = np.isnan(x.ravel()) + np.putmask(result, mask & ~nans, fill) + + # if we have a fill of inf, then sign it + # correctly + # GH 6178 + if np.isinf(fill): + np.putmask(result,signs<0 & mask & ~nans,-fill) + result = result.reshape(shape) return result diff --git a/pandas/core/ops.py b/pandas/core/ops.py index 3bd4b9ee4d16c..b8e92fb25cec5 100644 --- a/pandas/core/ops.py +++ b/pandas/core/ops.py @@ -454,7 +454,7 @@ def na_op(x, y): result, changed = com._maybe_upcast_putmask(result, -mask, pa.NA) - result = com._fill_zeros(result, y, fill_zeros) + result = com._fill_zeros(result, x, y, name, fill_zeros) return result def wrapper(left, right, name=name): @@ -749,7 +749,7 @@ def na_op(x, y): result, changed = com._maybe_upcast_putmask(result, -mask, np.nan) result = result.reshape(x.shape) - result = com._fill_zeros(result, y, fill_zeros) + result = com._fill_zeros(result, x, y, name, fill_zeros) return result @@ -913,7 +913,7 @@ def na_op(x, y): result[mask] = op(x[mask], y) result, changed = com._maybe_upcast_putmask(result, -mask, pa.NA) - result = com._fill_zeros(result, y, fill_zeros) + result = com._fill_zeros(result, x, y, name, fill_zeros) return result # work only for scalars diff --git a/pandas/core/panel.py b/pandas/core/panel.py index fc4373764172f..cb149abb7c9cf 100644 --- a/pandas/core/panel.py +++ b/pandas/core/panel.py @@ -1396,7 +1396,7 @@ def na_op(x, y): # handles discrepancy between numpy and numexpr on division/mod # by 0 though, given that these are generally (always?) # non-scalars, I'm not sure whether it's worth it at the moment - result = com._fill_zeros(result, y, fill_zeros) + result = com._fill_zeros(result, x, y, name, fill_zeros) return result @Substitution(name) diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py index 0f66d5b29ec83..2727d0b5b0881 100644 --- a/pandas/tests/test_panel.py +++ b/pandas/tests/test_panel.py @@ -2071,23 +2071,20 @@ def test_arith_flex_panel(self): else: aliases = {'div': 'truediv'} self.panel = self.panel.to_panel() - n = np.random.randint(-50, 50) - for op in ops: - try: + + for n in [ np.random.randint(-50, -1), np.random.randint(1, 50), 0]: + for op in ops: alias = aliases.get(op, op) f = getattr(operator, alias) - result = getattr(self.panel, op)(n) exp = f(self.panel, n) + result = getattr(self.panel, op)(n) assert_panel_equal(result, exp, check_panel_type=True) # rops r_f = lambda x, y: f(y, x) - result = getattr(self.panel, 'r' + op)(n) exp = r_f(self.panel, n) + result = getattr(self.panel, 'r' + op)(n) assert_panel_equal(result, exp) - except: - print("Failing operation %r" % op) - raise def test_sort(self): def is_sorted(arr): diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index 38aae8ad2b905..63f4bbe360f09 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -2212,6 +2212,12 @@ def test_div(self): assert_series_equal(result, p['first'].astype('float64')) self.assertFalse(np.array_equal(result, p['second'] / p['first'])) + # inf signing + s = Series([np.nan,1.,-1.]) + result = s / 0 + expected = Series([np.nan,np.inf,-np.inf]) + assert_series_equal(result, expected) + def test_operators(self): def _check_op(series, other, op, pos_only=False):