From 71e8b3169b77149923afd39e70fb52c4277d7ebb Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Thu, 4 Apr 2019 00:02:54 +0100 Subject: [PATCH 1/2] to_html formatter not called for float values in a mixed-type column --- doc/source/whatsnew/v0.25.0.rst | 1 + pandas/io/formats/format.py | 17 +++++++++++------ pandas/tests/io/formats/test_to_html.py | 10 ++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 1ef05ae5f9c6b..e01185b54f49d 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -344,6 +344,7 @@ I/O ^^^ - Bug in :func:`DataFrame.to_html()` where values were truncated using display options instead of outputting the full content (:issue:`17004`) +- Bug in :meth:`DataFrame.to_html` that would ignore ``formatters`` argument for float values in a column with ``dtype=object`` (:issue:`13021`) - Fixed bug in missing text when using :meth:`to_clipboard` if copying utf-16 characters in Python 3 on Windows (:issue:`25040`) - Bug in :func:`read_json` for ``orient='table'`` when it tries to infer dtypes by default, which is not applicable as dtypes are already defined in the JSON schema (:issue:`21345`) - Bug in :func:`read_json` for ``orient='table'`` and float index, as it infers index dtype by default, which is not applicable because index dtype is already defined in the JSON schema (:issue:`25433`) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index b658c8a53dc8b..e2f01e03e3cef 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -980,6 +980,17 @@ def _format(x): if leading_space is None: leading_space = is_float_type.any() + if leading_space is False: + # False specifically, so that the default is + # to include a space if we get here. + tpl = '{v}' + else: + tpl = ' {v}' + + # shortcut + if self.formatter is not None: + return [tpl.format(v=self.formatter(x)) for x in self.values] + fmt_values = [] for i, v in enumerate(vals): if not is_float_type[i] and leading_space: @@ -987,12 +998,6 @@ def _format(x): elif is_float_type[i]: fmt_values.append(float_format(v)) else: - if leading_space is False: - # False specifically, so that the default is - # to include a space if we get here. - tpl = '{v}' - else: - tpl = ' {v}' fmt_values.append(tpl.format(v=_format(v))) return fmt_values diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index d146e9c16e114..436dcff0aed60 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -633,3 +633,13 @@ def test_to_html_invalid_classes_type(classes): with pytest.raises(TypeError, match=msg): df.to_html(classes=classes) + + +def test_to_html_formatters_object_type(): + # GH 13021 + def f(x): + return x if type(x) is str else '${:,.0f}'.format(x) + + df = pd.DataFrame([['a'], [0], [10.4], [3]], columns=['x']) + result = df.to_html(formatters=dict(x=f)) + assert '$10' in result From 2a2bb57a9ee71c6686838df2ed47733865b015b0 Mon Sep 17 00:00:00 2001 From: Simon Hawkins Date: Thu, 4 Apr 2019 20:30:28 +0100 Subject: [PATCH 2/2] changes to test as requested --- .../data/html/gh13021_expected_output.html | 26 +++++++++++++++++++ pandas/tests/io/formats/test_to_html.py | 7 ++--- 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 pandas/tests/io/formats/data/html/gh13021_expected_output.html diff --git a/pandas/tests/io/formats/data/html/gh13021_expected_output.html b/pandas/tests/io/formats/data/html/gh13021_expected_output.html new file mode 100644 index 0000000000000..55d864d8a0d3d --- /dev/null +++ b/pandas/tests/io/formats/data/html/gh13021_expected_output.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +
x
0a
1$0
2$10
3$3
diff --git a/pandas/tests/io/formats/test_to_html.py b/pandas/tests/io/formats/test_to_html.py index 436dcff0aed60..29da4de36bf66 100644 --- a/pandas/tests/io/formats/test_to_html.py +++ b/pandas/tests/io/formats/test_to_html.py @@ -635,11 +635,12 @@ def test_to_html_invalid_classes_type(classes): df.to_html(classes=classes) -def test_to_html_formatters_object_type(): +def test_to_html_formatters_object_type(datapath): # GH 13021 def f(x): - return x if type(x) is str else '${:,.0f}'.format(x) + return x if isinstance(x, str) else '${:,.0f}'.format(x) df = pd.DataFrame([['a'], [0], [10.4], [3]], columns=['x']) result = df.to_html(formatters=dict(x=f)) - assert '$10' in result + expected = expected_html(datapath, 'gh13021_expected_output') + assert result == expected