diff --git a/RELEASE.rst b/RELEASE.rst index 59a86221d14a9..3db8f5702e7bf 100644 --- a/RELEASE.rst +++ b/RELEASE.rst @@ -55,6 +55,7 @@ pandas 0.10.1 - Add ``logx`` option to DataFrame/Series.plot (GH2327_, #2565) - Support reading gzipped data from file-like object - ``pivot_table`` aggfunc can be anything used in GroupBy.aggregate (GH2643_) + - Add methods ``neg`` and ``inv`` to Series **Bug fixes** @@ -78,6 +79,7 @@ pandas 0.10.1 - Exclude non-numeric data from DataFrame.quantile by default (GH2625_) - Fix a Cython C int64 boxing issue causing read_csv to return incorrect results (GH2599_) + - Fix setitem on a Series with a boolean key and a non-scalar as value (GH2686_) **API Changes** @@ -98,6 +100,7 @@ pandas 0.10.1 .. _GH2625: https://github.com/pydata/pandas/issues/2625 .. _GH2643: https://github.com/pydata/pandas/issues/2643 .. _GH2637: https://github.com/pydata/pandas/issues/2637 +.. _GH2686: https://github.com/pydata/pandas/issues/2686 pandas 0.10.0 ============= diff --git a/pandas/core/series.py b/pandas/core/series.py index 56b6e06844b86..b2703b96e611b 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -604,6 +604,9 @@ def where(self, cond, other=nan, inplace=False): if len(cond) != len(self): raise ValueError('condition must have same length as series') + if cond.dtype != np.bool_: + cond = cond.astype(np.bool_) + ser = self if inplace else self.copy() if not isinstance(other, (list, tuple, np.ndarray)): ser._set_with(~cond, other) @@ -661,9 +664,9 @@ def __setitem__(self, key, value): if _is_bool_indexer(key): key = self._check_bool_indexer(key) - key = np.asarray(key, dtype=bool) - - self._set_with(key, value) + self.where(~key,value,inplace=True) + else: + self._set_with(key, value) def _set_with(self, key, value): # other: fancy integer or otherwise @@ -695,7 +698,7 @@ def _set_with(self, key, value): else: return self._set_values(key, value) elif key_type == 'boolean': - self._set_values(key, value) + self._set_values(key, value) else: self._set_labels(key, value) @@ -740,6 +743,12 @@ def _check_bool_indexer(self, key): raise ValueError('cannot index with vector containing ' 'NA / NaN values') + # coerce to bool type + if not hasattr(result, 'shape'): + result = np.array(result) + if result.dtype != np.bool_: + result = result.astype(np.bool_) + return result def __setslice__(self, i, j, value): @@ -1097,6 +1106,15 @@ def iteritems(self): __le__ = _comp_method(operator.le, '__le__') __eq__ = _comp_method(operator.eq, '__eq__') __ne__ = _comp_method(operator.ne, '__ne__') + + # inversion + def __neg__(self): + arr = operator.neg(self.values) + return Series(arr, self.index, name=self.name) + + def __invert__(self): + arr = operator.inv(self.values) + return Series(arr, self.index, name=self.name) # binary logic __or__ = _bool_method(operator.or_, '__or__') diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index 335d747e960fb..c21c7588b643e 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -1044,6 +1044,22 @@ def test_ix_setitem(self): self.assertEquals(self.series[d1], 4) self.assertEquals(self.series[d2], 6) + def test_setitem_boolean(self): + mask = self.series > self.series.median() + + # similiar indexed series + result = self.series.copy() + result[mask] = self.series*2 + expected = self.series*2 + assert_series_equal(result[mask], expected[mask]) + + # needs alignment + result = self.series.copy() + result[mask] = (self.series*2)[0:5] + expected = (self.series*2)[0:5].reindex_like(self.series) + expected[-mask] = self.series[mask] + assert_series_equal(result[mask], expected[mask]) + def test_ix_setitem_boolean(self): mask = self.series > self.series.median() @@ -1517,6 +1533,12 @@ def check(series, other): check(self.ts, self.ts[::2]) check(self.ts, 5) + def test_neg(self): + assert_series_equal(-self.series, -1 * self.series) + + def test_invert(self): + assert_series_equal(-(self.series < 0), ~(self.series < 0)) + def test_operators(self): def _check_op(series, other, op, pos_only=False): @@ -3211,9 +3233,9 @@ def test_interpolate_index_values(self): expected = s.copy() bad = isnull(expected.values) good = -bad - expected[bad] = np.interp(vals[bad], vals[good], s.values[good]) + expected = Series(np.interp(vals[bad], vals[good], s.values[good]), index=s.index[bad]) - assert_series_equal(result, expected) + assert_series_equal(result[bad], expected) def test_weekday(self): # Just run the function