From 84376e93747eb8d7aae7900961d7c71243c6ef98 Mon Sep 17 00:00:00 2001 From: Phillip Cloud Date: Wed, 12 Feb 2014 21:37:37 -0500 Subject: [PATCH 1/4] BUG: fix bool no-op replace calls --- pandas/core/common.py | 8 +++++++- pandas/tests/test_frame.py | 19 ++++++++++++++++++- pandas/tests/test_series.py | 18 ++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/pandas/core/common.py b/pandas/core/common.py index e895c8ed0cf2d..b61b996f9752b 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -347,7 +347,13 @@ def mask_missing(arr, values_to_mask): values_to_mask = [values_to_mask] try: - values_to_mask = np.array(values_to_mask, dtype=arr.dtype) + values = np.array(values_to_mask) + cant_cast = not np.can_cast(values.dtype, arr.dtype, casting='safe') + + if cant_cast and arr.dtype == np.bool_: + values_to_mask = values + else: + values_to_mask = np.array(values_to_mask, dtype=arr.dtype) except Exception: values_to_mask = np.array(values_to_mask, dtype=object) diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index a6ac80c99e323..f3a0f156e0e3b 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -7975,7 +7975,7 @@ def test_replace_dict_tuple_list_ordering_remains_the_same(self): tm.assert_frame_equal(res2, res3) tm.assert_frame_equal(res3, expected) - def test_replace_doesnt_replace_with_no_regex(self): + def test_replace_doesnt_replace_without_regex(self): from pandas.compat import StringIO raw = """fol T_opp T_Dir T_Enh 0 1 0 0 vo @@ -7986,6 +7986,23 @@ def test_replace_doesnt_replace_with_no_regex(self): res = df.replace({'\D': 1}) tm.assert_frame_equal(df, res) + def test_replace_bool_with_string(self): + df = DataFrame({'a': [True, False], 'b': list('ab')}) + result = df.replace(True, 'a') + expected = DataFrame({'a': ['a', False], 'b': df.b}) + tm.assert_frame_equal(result, expected) + + def test_replace_pure_bool_with_string_no_op(self): + df = DataFrame(np.random.rand(2, 2) > 0.5) + result = df.replace('asdf', 'fdsa') + tm.assert_frame_equal(df, result) + + def test_replace_bool_with_bool(self): + df = DataFrame(np.random.rand(2, 2) > 0.5) + result = df.replace(False, True) + expected = DataFrame(np.ones((2, 2), dtype=bool)) + tm.assert_frame_equal(result, expected) + def test_combine_multiple_frames_dtypes(self): from pandas import concat diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index c43b7b3eee533..e8a357665c1c9 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -5349,6 +5349,24 @@ def check_replace(to_rep, val, expected): assert_series_equal(r, Series([1.0,2,'a'] + dr[3:].tolist(),dtype=object)) + def test_replace_bool_with_string_no_op(self): + s = Series([True, False, True]) + result = s.replace('fun', 'in-the-sun') + tm.assert_series_equal(s, result) + + def test_replace_bool_with_string(self): + # nonexistent elements + s = Series([True, False, True]) + result = s.replace(True, '2u') + expected = Series(['2u', False, '2u']) + tm.assert_series_equal(expected, result) + + def test_replace_bool_with_bool(self): + s = Series([True, False, True]) + result = s.replace(True, False) + expected = Series([False] * len(s)) + tm.assert_series_equal(expected, result) + def test_asfreq(self): ts = Series([0., 1., 2.], index=[datetime(2009, 10, 30), datetime(2009, 11, 30), From 818adb0104f14659bcdcc17bdcb60a7bdbb349de Mon Sep 17 00:00:00 2001 From: Phillip Cloud Date: Wed, 12 Feb 2014 22:39:32 -0500 Subject: [PATCH 2/4] CLN: replace in bool block rather than special-casing in common --- pandas/core/common.py | 8 +------- pandas/core/internals.py | 9 +++++++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/pandas/core/common.py b/pandas/core/common.py index b61b996f9752b..e895c8ed0cf2d 100644 --- a/pandas/core/common.py +++ b/pandas/core/common.py @@ -347,13 +347,7 @@ def mask_missing(arr, values_to_mask): values_to_mask = [values_to_mask] try: - values = np.array(values_to_mask) - cant_cast = not np.can_cast(values.dtype, arr.dtype, casting='safe') - - if cant_cast and arr.dtype == np.bool_: - values_to_mask = values - else: - values_to_mask = np.array(values_to_mask, dtype=arr.dtype) + values_to_mask = np.array(values_to_mask, dtype=arr.dtype) except Exception: values_to_mask = np.array(values_to_mask, dtype=object) diff --git a/pandas/core/internals.py b/pandas/core/internals.py index a7e1548b41bbb..faf8587460825 100644 --- a/pandas/core/internals.py +++ b/pandas/core/internals.py @@ -1216,6 +1216,7 @@ def should_store(self, value): return (issubclass(value.dtype.type, np.floating) and value.dtype == self.dtype) + class ComplexBlock(FloatOrComplexBlock): is_complex = True @@ -1355,6 +1356,14 @@ def _try_cast(self, element): def should_store(self, value): return issubclass(value.dtype.type, np.bool_) + def replace(self, to_replace, value, inplace=False, filter=None, + regex=False): + to_replace_values = np.atleast_1d(to_replace) + if not np.can_cast(to_replace_values, bool): + to_replace = to_replace_values + return super(BoolBlock, self).replace(to_replace, value, + inplace=inplace, filter=filter, + regex=regex) class ObjectBlock(Block): is_object = True From d794618774dc9f20bcd92c2a0765836d298b26c3 Mon Sep 17 00:00:00 2001 From: Phillip Cloud Date: Wed, 12 Feb 2014 23:25:33 -0500 Subject: [PATCH 3/4] TST: add tests for dict with bool keys --- pandas/tests/test_frame.py | 6 ++++++ pandas/tests/test_series.py | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index f3a0f156e0e3b..e73f1e792e826 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -8003,6 +8003,12 @@ def test_replace_bool_with_bool(self): expected = DataFrame(np.ones((2, 2), dtype=bool)) tm.assert_frame_equal(result, expected) + def test_replace_with_dict_with_bool_keys(self): + df = DataFrame({0: [True, False], 1: [False, True]}) + result = df.replace({'asdf': 'asdb', True: 'yes'}) + expected = DataFrame({0: ['yes', False], 1: [False, 'yes']}) + tm.assert_frame_equal(expected, result) + def test_combine_multiple_frames_dtypes(self): from pandas import concat diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index e8a357665c1c9..38aae8ad2b905 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -5291,7 +5291,6 @@ def test_replace(self): result = ser.replace(Timestamp('20130103'), Timestamp('20120101')) assert_series_equal(result, expected) - def test_replace_with_single_list(self): ser = Series([0, 1, 2, 3, 4]) result = ser.replace([1,2,3]) @@ -5307,6 +5306,7 @@ def test_replace_with_single_list(self): s.replace([1,2,3],inplace=True,method='crash_cymbal') assert_series_equal(s, ser) + def test_replace_mixed_types(self): s = Series(np.arange(5),dtype='int64') @@ -5367,6 +5367,12 @@ def test_replace_bool_with_bool(self): expected = Series([False] * len(s)) tm.assert_series_equal(expected, result) + def test_replace_with_dict_with_bool_keys(self): + s = Series([True, False, True]) + result = s.replace({'asdf': 'asdb', True: 'yes'}) + expected = Series(['yes', False, 'yes']) + tm.assert_series_equal(expected, result) + def test_asfreq(self): ts = Series([0., 1., 2.], index=[datetime(2009, 10, 30), datetime(2009, 11, 30), From 3cd050e81105789c08f6880659d88a7e66246f56 Mon Sep 17 00:00:00 2001 From: Phillip Cloud Date: Wed, 12 Feb 2014 23:28:46 -0500 Subject: [PATCH 4/4] DOC: release notes --- doc/source/release.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/release.rst b/doc/source/release.rst index a8fe12940d479..40913e40f485f 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -92,6 +92,8 @@ Bug Fixes - ``HDFStore.select_as_coordinates`` and ``select_column`` works where clauses that result in filters (:issue:`6177`) - Regression in join of non_unique_indexes (:issue:`6329`) - Issue with groupby ``agg`` with a single function and a a mixed-type frame (:issue:`6337`) +- Bug in ``DataFrame.replace()`` when passing a non- ``bool`` + ``to_replace`` argument (:issue:`6332`) pandas 0.13.1 -------------