From 90a85e0b154833577a4023f643d8aace0c0ae7f5 Mon Sep 17 00:00:00 2001 From: Sakar Panta Date: Fri, 12 Apr 2019 12:08:29 -0500 Subject: [PATCH] BUG: Display precision doesn't affect complex float numbers #25514 (#25745) --- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/core/frame.py | 12 +++++----- pandas/io/formats/format.py | 31 +++++++++++++++++++++----- pandas/tests/io/formats/test_format.py | 13 +++++++++++ 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 7b87f6a7f8d3c..b23da6ee4b806 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -293,6 +293,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`) - Bug in error messages in :meth:`DataFrame.corr` and :meth:`Series.corr`. Added the possibility of using a callable. (:issue:`25729`) - Bug in :meth:`Series.divmod` and :meth:`Series.rdivmod` which would raise an (incorrect) ``ValueError`` rather than return a pair of :class:`Series` objects as result (:issue:`25557`) - Raises a helpful exception when a non-numeric index is sent to :meth:`interpolate` with methods which require numeric index. (:issue:`21662`) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 13c8e97bff23f..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+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.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 b397a370cecbf..ce724a32a7a56 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -19,9 +19,9 @@ from pandas.compat import lzip 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) @@ -892,7 +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): + elif is_float_dtype(values.dtype) or is_complex_dtype(values.dtype): fmt_klass = FloatArrayFormatter elif is_integer_dtype(values.dtype): fmt_klass = IntArrayFormatter @@ -1084,6 +1084,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() @@ -1094,7 +1095,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_float(values, na_rep) return values @@ -1424,7 +1428,22 @@ def just(x): return result -def _trim_zeros(str_floats, na_rep='NaN'): +def _trim_zeros_complex(str_complexes, na_rep='NaN'): + """ + Separates the real and imaginary parts from the complex number, and + 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_float([num_arr[0]], 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_float(str_floats, na_rep='NaN'): """ Trims zeros, leaving just one before the decimal points if need be. """ diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index a960dcb9d164a..100b75e55600f 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -1370,6 +1370,19 @@ def test_to_string_float_index(self): '5.0 4') 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), + (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 ', ' \xe2\x80\xa2\xe2\x80\xa2\xe2\x80'