Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

BUG: series assignment with a boolean indexer #2686

Closed
wants to merge 3 commits into
from
View
@@ -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
=============
View
@@ -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__')
@@ -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