diff --git a/doc/source/release.rst b/doc/source/release.rst index 341450410f1e5..94ab7cd0cff8a 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -113,6 +113,8 @@ Bug Fixes - Bug correctly handle placements of ``-inf`` in Panels when dividing by integer 0 (:issue:`6178`) - ``DataFrame.shift`` with ``axis=1`` was raising (:issue:`6371`) - Disabled clipboard tests until release time (run locally with ``nosetests -A disabled`` (:issue:`6048`). +- Bug in ``DataFrame.replace()`` when passing a nested ``dict`` that contained + keys not in the values to be replaced (:issue:`6342`) pandas 0.13.1 ------------- diff --git a/pandas/core/generic.py b/pandas/core/generic.py index d90185fef0a3a..c8e1247416806 100644 --- a/pandas/core/generic.py +++ b/pandas/core/generic.py @@ -2330,8 +2330,8 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, value_dict = {} for k, v in items: - to_rep_dict[k] = v.keys() - value_dict[k] = v.values() + to_rep_dict[k] = list(v.keys()) + value_dict[k] = list(v.values()) to_replace, value = to_rep_dict, value_dict else: @@ -2349,7 +2349,6 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, new_data = self._data if is_dictlike(to_replace): if is_dictlike(value): # {'A' : NA} -> {'A' : 0} - new_data = self._data for c, src in compat.iteritems(to_replace): if c in value and c in self: new_data = new_data.replace(to_replace=src, @@ -2360,7 +2359,6 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None, # {'A': NA} -> 0 elif not com.is_list_like(value): - new_data = self._data for k, src in compat.iteritems(to_replace): if k in self: new_data = new_data.replace(to_replace=src, diff --git a/pandas/core/internals.py b/pandas/core/internals.py index be64bea1c7c23..94d0deaec80e3 100644 --- a/pandas/core/internals.py +++ b/pandas/core/internals.py @@ -729,7 +729,7 @@ def putmask(self, mask, new, align=True, inplace=False): def create_block(v, m, n, item, reshape=True): """ return a new block, try to preserve dtype if possible """ - # n should the length of the mask or a scalar here + # n should be the length of the mask or a scalar here if not is_list_like(n): n = np.array([n] * len(m)) @@ -742,7 +742,7 @@ def create_block(v, m, n, item, reshape=True): if (nn == nn_at).all(): nv = v.copy() nv[mask] = nn_at - except: + except (ValueError, IndexError, TypeError): pass # change the dtype @@ -751,8 +751,10 @@ def create_block(v, m, n, item, reshape=True): nv = v.astype(dtype) try: nv[m] = n - except: - np.putmask(nv, m, n) + except ValueError: + idx, = np.where(np.squeeze(m)) + for mask_index, new_val in zip(idx, n): + nv[mask_index] = new_val if reshape: nv = _block_shape(nv) diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index da5a106425099..6262a354065d2 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -7804,6 +7804,27 @@ def test_replace_mixed(self): expected.iloc[1,1] = m[1] assert_frame_equal(result,expected) + def test_replace_simple_nested_dict(self): + df = DataFrame({'col': range(1, 5)}) + expected = DataFrame({'col': ['a', 2, 3, 'b']}) + + result = df.replace({'col': {1: 'a', 4: 'b'}}) + tm.assert_frame_equal(expected, result) + + # in this case, should be the same as the not nested version + result = df.replace({1: 'a', 4: 'b'}) + tm.assert_frame_equal(expected, result) + + def test_replace_simple_nested_dict_with_nonexistent_value(self): + df = DataFrame({'col': range(1, 5)}) + expected = DataFrame({'col': ['a', 2, 3, 'b']}) + + result = df.replace({-1: '-', 1: 'a', 4: 'b'}) + tm.assert_frame_equal(expected, result) + + result = df.replace({'col': {-1: '-', 1: 'a', 4: 'b'}}) + tm.assert_frame_equal(expected, result) + def test_interpolate(self): pass @@ -12148,7 +12169,6 @@ def check_query_with_named_multiindex(self, parser, engine): name='color') # equality - #import ipdb; ipdb.set_trace() res1 = df.query('color == "red"', parser=parser, engine=engine) res2 = df.query('"red" == color', parser=parser, engine=engine) exp = df[ind == 'red']