From 0e1f3b6bfc0ea70d4427ee6b87581a5345abd301 Mon Sep 17 00:00:00 2001 From: Sakar Panta Date: Sat, 16 Mar 2019 12:56:01 -0500 Subject: [PATCH 1/6] BUG: Display precision doesn't affect complex float numbers #25514 --- pandas/core/frame.py | 12 ++++++------ pandas/io/formats/format.py | 8 +++++--- pandas/tests/io/formats/test_format.py | 12 ++++++++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 6b4d95055d06d..e2d23f9041759 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2498,12 +2498,12 @@ def memory_usage(self, index=True, deep=False): ... for t in dtypes]) >>> df = pd.DataFrame(data) >>> df.head() - int64 float64 complex128 object bool - 0 1 1.0 (1+0j) 1 True - 1 1 1.0 (1+0j) 1 True - 2 1 1.0 (1+0j) 1 True - 3 1 1.0 (1+0j) 1 True - 4 1 1.0 (1+0j) 1 True + int64 float64 complex128 object bool + 0 1 1.0 1.000000+0.000000j 1 True + 1 1 1.0 1.000000+0.000000j 1 True + 2 1 1.0 1.000000+0.000000j 1 True + 3 1 1.0 1.000000+0.000000j 1 True + 4 1 1.0 1.000000+0.000000j 1 True >>> df.memory_usage() Index 80 diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index f68ef2cc39006..9a99947c70982 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -16,9 +16,9 @@ from pandas.compat import StringIO, lzip, map, u, zip from pandas.core.dtypes.common import ( - is_categorical_dtype, is_datetime64_dtype, is_datetime64tz_dtype, - is_extension_array_dtype, is_float, is_float_dtype, is_integer, - is_integer_dtype, is_list_like, is_numeric_dtype, is_scalar, + is_categorical_dtype, is_complex_dtype, is_datetime64_dtype, + is_datetime64tz_dtype, is_extension_array_dtype, is_float, is_float_dtype, + is_integer, is_integer_dtype, is_list_like, is_numeric_dtype, is_scalar, is_timedelta64_dtype) from pandas.core.dtypes.generic import ( ABCIndexClass, ABCMultiIndex, ABCSeries, ABCSparseArray) @@ -885,6 +885,8 @@ def format_array(values, formatter, float_format=None, na_rep='NaN', fmt_klass = ExtensionArrayFormatter elif is_float_dtype(values.dtype): fmt_klass = FloatArrayFormatter + elif is_complex_dtype(values.dtype): + fmt_klass = FloatArrayFormatter elif is_integer_dtype(values.dtype): fmt_klass = IntArrayFormatter else: diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index b0cf5a2f17609..1384f7788ada9 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -1417,6 +1417,18 @@ def test_to_string_float_index(self): '5.0 4') assert result == expected + def test_to_string_complex_float_formatting(self): + fmt.set_option('display.precision', 5) + df = DataFrame({'x': [ + (0.4467846931321966 + 0.0715185102060818j), + (0.2739442392974528 + 0.23515228785438969j), + (0.26974928742135185 + 0.3250604054898979j)]}) + result = df.to_string() + expected = (' x\n0 0.44678+0.07152j\n' + '1 0.27394+0.23515j\n' + '2 0.26975+0.32506j') + assert result == expected + def test_to_string_ascii_error(self): data = [('0 ', u(' .gitignore '), u(' 5 '), ' \xe2\x80\xa2\xe2\x80\xa2\xe2\x80' From 34c9d0d326e92804d788692679609a5a8e64ab91 Mon Sep 17 00:00:00 2001 From: Sakar Panta Date: Tue, 2 Apr 2019 13:57:09 -0500 Subject: [PATCH 2/6] Add whatsnew entry --- doc/source/whatsnew/v0.25.0.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 124ec8f4ab92c..e4c9c785b4111 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -157,7 +157,7 @@ Numeric - Bug in :meth:`to_numeric` in which large negative numbers were being improperly handled (:issue:`24910`) - Bug in :meth:`to_numeric` in which numbers were being coerced to float, even though ``errors`` was not ``coerce`` (:issue:`24910`) -- +- Bug in :class:`format` in which floating point complex numbers were not being formatted to proper display precision and trimming (:issue:`25514`) - - From 6b1a3652e2e857f5f8c3b60112c234a3a467dd46 Mon Sep 17 00:00:00 2001 From: Sakar Panta Date: Tue, 2 Apr 2019 13:57:58 -0500 Subject: [PATCH 3/6] Fixed the zeros not being trimmed issue --- pandas/core/frame.py | 12 ++++++------ pandas/io/formats/format.py | 21 ++++++++++++++++++++- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index e2d23f9041759..b30ea59e35696 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2498,12 +2498,12 @@ def memory_usage(self, index=True, deep=False): ... for t in dtypes]) >>> df = pd.DataFrame(data) >>> df.head() - int64 float64 complex128 object bool - 0 1 1.0 1.000000+0.000000j 1 True - 1 1 1.0 1.000000+0.000000j 1 True - 2 1 1.0 1.000000+0.000000j 1 True - 3 1 1.0 1.000000+0.000000j 1 True - 4 1 1.0 1.000000+0.000000j 1 True + int64 float64 complex128 object bool + 0 1 1.0 1.0+0.0j 1 True + 1 1 1.0 1.0+0.0j 1 True + 2 1 1.0 1.0+0.0j 1 True + 3 1 1.0 1.0+0.0j 1 True + 4 1 1.0 1.0+0.0j 1 True >>> df.memory_usage() Index 80 diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 9a99947c70982..4769e5a1c740a 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1071,6 +1071,7 @@ def format_values_with(float_format): # separate the wheat from the chaff values = self.values + is_complex = is_complex_dtype(values) mask = isna(values) if hasattr(values, 'to_dense'): # sparse numpy ndarray values = values.to_dense() @@ -1081,7 +1082,10 @@ def format_values_with(float_format): for val in values.ravel()[imask]]) if self.fixed_width: - return _trim_zeros(values, na_rep) + if is_complex: + return _trim_zeros_complex(values, na_rep) + else: + return _trim_zeros(values, na_rep) return values @@ -1411,6 +1415,21 @@ def just(x): return result +def _trim_zeros_complex(str_complexes, na_rep='NaN'): + """ + Separates the real and imaginary parts from the complex number, and + executes the _trim_zeros method on each of those. + """ + def separate_and_trim(str_complex, na_rep): + num_arr = str_complex.split('+') + return (_trim_zeros([num_arr[0]], na_rep) + + ['+'] + + _trim_zeros([num_arr[1][:-1]], na_rep) + + ['j']) + + return [''.join(separate_and_trim(x, na_rep)) for x in str_complexes] + + def _trim_zeros(str_floats, na_rep='NaN'): """ Trims zeros, leaving just one before the decimal points if need be. From 423eea1bbfd15863dc916ca21c417cc50865d0ce Mon Sep 17 00:00:00 2001 From: Sakar Panta Date: Wed, 10 Apr 2019 21:26:22 -0500 Subject: [PATCH 4/6] Remove extra space and add context manager to test --- pandas/core/frame.py | 12 ++++++------ pandas/tests/io/formats/test_format.py | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index c3e07563fa6b1..fdc99e957e257 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -2497,12 +2497,12 @@ def memory_usage(self, index=True, deep=False): ... for t in dtypes]) >>> df = pd.DataFrame(data) >>> df.head() - int64 float64 complex128 object bool - 0 1 1.0 1.0+0.0j 1 True - 1 1 1.0 1.0+0.0j 1 True - 2 1 1.0 1.0+0.0j 1 True - 3 1 1.0 1.0+0.0j 1 True - 4 1 1.0 1.0+0.0j 1 True + int64 float64 complex128 object bool + 0 1 1.0 1.0+0.0j 1 True + 1 1 1.0 1.0+0.0j 1 True + 2 1 1.0 1.0+0.0j 1 True + 3 1 1.0 1.0+0.0j 1 True + 4 1 1.0 1.0+0.0j 1 True >>> df.memory_usage() Index 80 diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index 02f350a0d2ac4..df16c8edefdea 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -1371,16 +1371,16 @@ def test_to_string_float_index(self): assert result == expected def test_to_string_complex_float_formatting(self): - fmt.set_option('display.precision', 5) - df = DataFrame({'x': [ - (0.4467846931321966 + 0.0715185102060818j), - (0.2739442392974528 + 0.23515228785438969j), - (0.26974928742135185 + 0.3250604054898979j)]}) - result = df.to_string() - expected = (' x\n0 0.44678+0.07152j\n' - '1 0.27394+0.23515j\n' - '2 0.26975+0.32506j') - assert result == expected + with pd.option_context('display.precision', 5): + df = DataFrame({'x': [ + (0.4467846931321966 + 0.0715185102060818j), + (0.2739442392974528 + 0.23515228785438969j), + (0.26974928742135185 + 0.3250604054898979j)]}) + result = df.to_string() + expected = (' x\n0 0.44678+0.07152j\n' + '1 0.27394+0.23515j\n' + '2 0.26975+0.32506j') + assert result == expected def test_to_string_ascii_error(self): data = [('0 ', ' .gitignore ', ' 5 ', From 7dc8a3334f0baa059e7be2e299543fd6e3d0401b Mon Sep 17 00:00:00 2001 From: Sakar Panta Date: Wed, 10 Apr 2019 21:50:34 -0500 Subject: [PATCH 5/6] Refactor format class list and rename _trim_zeros to _trim_zeros_float --- pandas/io/formats/format.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 3896538fca616..ce724a32a7a56 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -892,9 +892,7 @@ def format_array(values, formatter, float_format=None, na_rep='NaN', fmt_klass = Timedelta64Formatter elif is_extension_array_dtype(values.dtype): fmt_klass = ExtensionArrayFormatter - elif is_float_dtype(values.dtype): - fmt_klass = FloatArrayFormatter - elif is_complex_dtype(values.dtype): + elif is_float_dtype(values.dtype) or is_complex_dtype(values.dtype): fmt_klass = FloatArrayFormatter elif is_integer_dtype(values.dtype): fmt_klass = IntArrayFormatter @@ -1100,7 +1098,7 @@ def format_values_with(float_format): if is_complex: return _trim_zeros_complex(values, na_rep) else: - return _trim_zeros(values, na_rep) + return _trim_zeros_float(values, na_rep) return values @@ -1433,19 +1431,19 @@ def just(x): def _trim_zeros_complex(str_complexes, na_rep='NaN'): """ Separates the real and imaginary parts from the complex number, and - executes the _trim_zeros method on each of those. + executes the _trim_zeros_float method on each of those. """ def separate_and_trim(str_complex, na_rep): num_arr = str_complex.split('+') - return (_trim_zeros([num_arr[0]], na_rep) + + return (_trim_zeros_float([num_arr[0]], na_rep) + ['+'] + - _trim_zeros([num_arr[1][:-1]], na_rep) + + _trim_zeros_float([num_arr[1][:-1]], na_rep) + ['j']) return [''.join(separate_and_trim(x, na_rep)) for x in str_complexes] -def _trim_zeros(str_floats, na_rep='NaN'): +def _trim_zeros_float(str_floats, na_rep='NaN'): """ Trims zeros, leaving just one before the decimal points if need be. """ From 3fd69e9e4cd8e553470e3c5c6676ce5807cbf711 Mon Sep 17 00:00:00 2001 From: Sakar Panta Date: Fri, 12 Apr 2019 09:24:29 -0500 Subject: [PATCH 6/6] Add comment with issue #25514 --- pandas/tests/io/formats/test_format.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index df16c8edefdea..100b75e55600f 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -1371,6 +1371,7 @@ def test_to_string_float_index(self): assert result == expected def test_to_string_complex_float_formatting(self): + # GH #25514 with pd.option_context('display.precision', 5): df = DataFrame({'x': [ (0.4467846931321966 + 0.0715185102060818j),