diff --git a/doc/source/whatsnew/v0.20.0.txt b/doc/source/whatsnew/v0.20.0.txt index 83a70aa34fccf..75b6df0ed86dc 100644 --- a/doc/source/whatsnew/v0.20.0.txt +++ b/doc/source/whatsnew/v0.20.0.txt @@ -246,6 +246,7 @@ Bug Fixes - Bug in ``DataFrame`` construction in which unsigned 64-bit integer elements were being converted to objects (:issue:`14881`) - Bug in ``astype()`` where ``inf`` values were incorrectly converted to integers. Now raises error now with ``astype()`` for Series and DataFrames (:issue:`14265`) - Bug in ``describe()`` when passing a numpy array which does not contain the median to the ``percentiles`` keyword argument (:issue:`14908`) +- Bug in ``.to_html``, ``.to_latex`` and ``.to_string`` ignoring ``na_rep`` in the presence of a ``float_format`` function. (:issue:`13911`) diff --git a/pandas/formats/format.py b/pandas/formats/format.py index 0cf6050e515e0..9e4576486cc21 100644 --- a/pandas/formats/format.py +++ b/pandas/formats/format.py @@ -2063,7 +2063,19 @@ def get_result_as_array(self): """ if self.formatter is not None: - return np.array([self.formatter(x) for x in self.values]) + if self.na_rep is None: + fmt_values = np.array([self.formatter(x) + for x in self.values]) + else: + fmt_values = self.values + mask = isnull(fmt_values) + fmt_values = np.array(fmt_values, dtype='object') + fmt_values[mask] = self.na_rep + imask = (~mask).ravel() + fmt_values.flat[imask] = np.array([self.formatter(x) + for x in + fmt_values.ravel()[imask]]) + return fmt_values if self.fixed_width: threshold = get_option("display.chop_threshold") @@ -2129,7 +2141,7 @@ def format_values_with(float_format): def _format_strings(self): # shortcut - if self.formatter is not None: + if self.formatter is not None and self.na_rep is None: return [self.formatter(x) for x in self.values] return list(self.get_result_as_array()) diff --git a/pandas/tests/formats/test_format.py b/pandas/tests/formats/test_format.py index e7c32a4baa4ea..434fde1dc698f 100644 --- a/pandas/tests/formats/test_format.py +++ b/pandas/tests/formats/test_format.py @@ -1667,6 +1667,34 @@ def test_to_html_border_zero(self): result = df.to_html(border=0) self.assertTrue('border="0"' in result) + def test_to_html_na_rep_and_float_format(self): + # GH 13828 + df = DataFrame([['A', 1.2225], ['A', ]], columns=['Group', 'Data']) + result = df.to_html(na_rep='Ted', float_format='{0:.2f}'.format) + expected = '''\ + + + + + + + + + + + + + + + + + + + + +
GroupData
0A1.22
1ATed
''' + self.assertEqual(result, expected) + def test_nonunicode_nonascii_alignment(self): df = DataFrame([["aa\xc3\xa4\xc3\xa4", 1], ["bbbb", 2]]) rep_str = df.to_string() @@ -2906,6 +2934,22 @@ def test_to_latex_with_formatters(self): """ self.assertEqual(result, expected) + def test_to_latex_na_rep_and_float_format(self): + # GH 13828 + df = DataFrame([['A', 1.2225], ['A', ]], columns=['Group', 'Data']) + result = df.to_latex(na_rep='Ted', float_format='{0:.2f}'.format) + expected = '''\ +\\begin{tabular}{llr} +\\toprule +{} & Group & Data \\\\ +\\midrule +0 & A & 1.22 \\\\ +1 & A & Ted \\\\ +\\bottomrule +\\end{tabular} +''' + self.assertEqual(result, expected) + def test_to_latex_multiindex(self): df = DataFrame({('x', 'y'): ['a']}) result = df.to_latex() @@ -3450,6 +3494,16 @@ def test_to_string_float_na_spacing(self): '3 -3.0000\n' + '4 NaN') self.assertEqual(result, expected) + def test_to_string_na_rep_and_float_format(self): + # GH 13828 + df = DataFrame([['A', 1.2225], ['A', ]], columns=['Group', 'Data']) + result = df.to_string(na_rep='Ted', float_format='{0:.2f}'.format) + expected = '''\ + Group Data +0 A 1.22 +1 A Ted''' + self.assertEqual(result, expected) + def test_to_string_without_index(self): # GH 11729 Test index=False option s = Series([1, 2, 3, 4])