Skip to content

Commit 0e0b987

Browse files
authored
BUG: to_string ignoring formatter for FloatingArray (#50333)
1 parent 2fbea3f commit 0e0b987

File tree

3 files changed

+27
-3
lines changed

3 files changed

+27
-3
lines changed

doc/source/whatsnew/v2.0.0.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -880,7 +880,8 @@ I/O
880880
- Bug in :func:`read_sql_query` ignoring ``dtype`` argument when ``chunksize`` is specified and result is empty (:issue:`50245`)
881881
- Bug in :func:`read_csv` for a single-line csv with fewer columns than ``names`` raised :class:`.errors.ParserError` with ``engine="c"`` (:issue:`47566`)
882882
- Bug in displaying ``string`` dtypes not showing storage option (:issue:`50099`)
883-
- Bug in :func:`DataFrame.to_string` with ``header=False`` that printed the index name on the same line as the first row of the data (:issue:`49230`)
883+
- Bug in :meth:`DataFrame.to_string` with ``header=False`` that printed the index name on the same line as the first row of the data (:issue:`49230`)
884+
- Bug in :meth:`DataFrame.to_string` ignoring float formatter for extension arrays (:issue:`39336`)
884885
- Fixed memory leak which stemmed from the initialization of the internal JSON module (:issue:`49222`)
885886
- Fixed issue where :func:`json_normalize` would incorrectly remove leading characters from column names that matched the ``sep`` argument (:issue:`49861`)
886887
- Bug in :meth:`DataFrame.to_json` where it would segfault when failing to encode a string (:issue:`50307`)

pandas/io/formats/format.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,7 @@ def format_array(
12591259
decimal: str = ".",
12601260
leading_space: bool | None = True,
12611261
quoting: int | None = None,
1262+
fallback_formatter: Callable | None = None,
12621263
) -> list[str]:
12631264
"""
12641265
Format an array for printing.
@@ -1281,6 +1282,7 @@ def format_array(
12811282
When formatting an Index subclass
12821283
(e.g. IntervalIndex._format_native_types), we don't want the
12831284
leading space since it should be left-aligned.
1285+
fallback_formatter
12841286
12851287
Returns
12861288
-------
@@ -1322,6 +1324,7 @@ def format_array(
13221324
decimal=decimal,
13231325
leading_space=leading_space,
13241326
quoting=quoting,
1327+
fallback_formatter=fallback_formatter,
13251328
)
13261329

13271330
return fmt_obj.get_result()
@@ -1341,6 +1344,7 @@ def __init__(
13411344
quoting: int | None = None,
13421345
fixed_width: bool = True,
13431346
leading_space: bool | None = True,
1347+
fallback_formatter: Callable | None = None,
13441348
) -> None:
13451349
self.values = values
13461350
self.digits = digits
@@ -1353,6 +1357,7 @@ def __init__(
13531357
self.quoting = quoting
13541358
self.fixed_width = fixed_width
13551359
self.leading_space = leading_space
1360+
self.fallback_formatter = fallback_formatter
13561361

13571362
def get_result(self) -> list[str]:
13581363
fmt_values = self._format_strings()
@@ -1371,6 +1376,8 @@ def _format_strings(self) -> list[str]:
13711376

13721377
if self.formatter is not None:
13731378
formatter = self.formatter
1379+
elif self.fallback_formatter is not None:
1380+
formatter = self.fallback_formatter
13741381
else:
13751382
quote_strings = self.quoting is not None and self.quoting != QUOTE_NONE
13761383
formatter = partial(
@@ -1419,7 +1426,7 @@ def _format(x):
14191426

14201427
fmt_values = []
14211428
for i, v in enumerate(vals):
1422-
if not is_float_type[i] and leading_space:
1429+
if not is_float_type[i] and leading_space or self.formatter is not None:
14231430
fmt_values.append(f" {_format(v)}")
14241431
elif is_float_type[i]:
14251432
fmt_values.append(float_format(v))
@@ -1651,8 +1658,9 @@ def _format_strings(self) -> list[str]:
16511658
values = extract_array(self.values, extract_numpy=True)
16521659

16531660
formatter = self.formatter
1661+
fallback_formatter = None
16541662
if formatter is None:
1655-
formatter = values._formatter(boxed=True)
1663+
fallback_formatter = values._formatter(boxed=True)
16561664

16571665
if isinstance(values, Categorical):
16581666
# Categorical is special for now, so that we can preserve tzinfo
@@ -1671,6 +1679,7 @@ def _format_strings(self) -> list[str]:
16711679
decimal=self.decimal,
16721680
leading_space=self.leading_space,
16731681
quoting=self.quoting,
1682+
fallback_formatter=fallback_formatter,
16741683
)
16751684
return fmt_values
16761685

pandas/tests/frame/test_repr_info.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,17 @@ def test_datetime64tz_slice_non_truncate(self):
365365
df = df.iloc[:, :5]
366366
result = repr(df)
367367
assert result == expected
368+
369+
def test_masked_ea_with_formatter(self):
370+
# GH#39336
371+
df = DataFrame(
372+
{
373+
"a": Series([0.123456789, 1.123456789], dtype="Float64"),
374+
"b": Series([1, 2], dtype="Int64"),
375+
}
376+
)
377+
result = df.to_string(formatters=["{:.2f}".format, "{:.2f}".format])
378+
expected = """ a b
379+
0 0.12 1.00
380+
1 1.12 2.00"""
381+
assert result == expected

0 commit comments

Comments
 (0)