From 834981607a8fdb543936523520d79120cb03d029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 25 Apr 2025 10:49:29 -0400 Subject: [PATCH 1/3] GH730 Add float_format str for to_string methods in Series and DataFrame --- pandas-stubs/core/frame.pyi | 9 ++++++--- pandas-stubs/core/series.pyi | 5 +++-- tests/test_frame.py | 23 +++++++++++++++++++++++ tests/test_series.py | 24 ++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index abebc547c..232157b7c 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -18,7 +18,10 @@ from typing import ( overload, ) -from _typing import TimeZones +from _typing import ( + FloatFormatType, + TimeZones, +) from matplotlib.axes import Axes as PlotAxes import numpy as np from pandas import ( @@ -2311,7 +2314,7 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack): index: _bool = ..., na_rep: _str = ..., formatters: FormattersType | None = ..., - float_format: Callable[[float], str] | None = ..., + float_format: FloatFormatType | Callable[[float], str] | None = ..., sparsify: _bool | None = ..., index_names: _bool = ..., justify: _str | None = ..., @@ -2334,7 +2337,7 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack): index: _bool = ..., na_rep: _str = ..., formatters: FormattersType | None = ..., - float_format: Callable[[float], str] | None = ..., + float_format: FloatFormatType | Callable[[float], str] | None = ..., sparsify: _bool | None = ..., index_names: _bool = ..., justify: _str | None = ..., diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 64b7ec6c3..739845eb4 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -25,6 +25,7 @@ from typing import ( ) from _typing import ( + FloatFormatType, Label, ReplaceValue, TimeZones, @@ -530,7 +531,7 @@ class Series(IndexOpsMixin[S1], NDFrame): self, buf: FilePath | WriteBuffer[str], na_rep: _str = ..., - float_format: Callable[[float], str] = ..., + float_format: FloatFormatType | Callable[[float], str] = ..., header: _bool = ..., index: _bool = ..., length: _bool = ..., @@ -544,7 +545,7 @@ class Series(IndexOpsMixin[S1], NDFrame): self, buf: None = ..., na_rep: _str = ..., - float_format: Callable[[float], str] = ..., + float_format: FloatFormatType | Callable[[float], str] = ..., header: _bool = ..., index: _bool = ..., length: _bool = ..., diff --git a/tests/test_frame.py b/tests/test_frame.py index acb96183d..a368390d2 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -55,6 +55,7 @@ pytest_warns_bounded, ) +from pandas.io.formats.format import EngFormatter from pandas.io.formats.style import Styler from pandas.io.parsers import TextFileReader @@ -1698,6 +1699,28 @@ def test_types_to_string() -> None: df.to_string(col_space={"col1": 1, "col2": 3}) +def test_dataframe_to_string_float_fmt() -> None: + """Test the different argument types for float_format.""" + df = pd.DataFrame( + {"values": [2.304, 1.1, 3487392, 13.4732894237, 14.3, 18.0, 17.434, 19.3]} + ) + check(assert_type(df.to_string(), str), str) + + def _formatter(x) -> str: + return f"{x:.2f}" + + check(assert_type(df.to_string(), str), str) + check(assert_type(df.to_string(float_format=_formatter), str), str) + check( + assert_type( + df.to_string(float_format=EngFormatter(accuracy=2, use_eng_prefix=False)), + str, + ), + str, + ) + check(assert_type(df.to_string(float_format=".2%"), str), str) + + def test_types_to_html() -> None: df = pd.DataFrame( data={ diff --git a/tests/test_series.py b/tests/test_series.py index 3b3604c02..2dc93e928 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -60,6 +60,8 @@ ) from tests.extension.decimal.array import DecimalDtype +from pandas.io.formats.format import EngFormatter + if TYPE_CHECKING: from pandas.core.series import ( OffsetSeries, @@ -2932,6 +2934,28 @@ def test_to_string() -> None: ) +def test_series_to_string_float_fmt() -> None: + """Test the different argument types for float_format.""" + sr = pd.Series( + [2.304, 1.1, 3487392, 13.4732894237, 14.3, 18.0, 17.434, 19.3], name="values" + ) + check(assert_type(sr.to_string(), str), str) + + def _formatter(x) -> str: + return f"{x:.2f}" + + check(assert_type(sr.to_string(), str), str) + check(assert_type(sr.to_string(float_format=_formatter), str), str) + check( + assert_type( + sr.to_string(float_format=EngFormatter(accuracy=2, use_eng_prefix=False)), + str, + ), + str, + ) + check(assert_type(sr.to_string(float_format=".2%"), str), str) + + def test_types_mask() -> None: s = pd.Series([1, 2, 3, 4, 5]) From 9ac31af297dbbed4be0556dcb7146effc536ec40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 25 Apr 2025 11:13:01 -0400 Subject: [PATCH 2/3] GH730 Fix test --- tests/test_frame.py | 2 +- tests/test_series.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_frame.py b/tests/test_frame.py index a368390d2..9b2b354fe 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -1718,7 +1718,7 @@ def _formatter(x) -> str: ), str, ) - check(assert_type(df.to_string(float_format=".2%"), str), str) + check(assert_type(df.to_string(float_format="%.2f"), str), str) def test_types_to_html() -> None: diff --git a/tests/test_series.py b/tests/test_series.py index 2dc93e928..dfdaeb43a 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -2953,7 +2953,8 @@ def _formatter(x) -> str: ), str, ) - check(assert_type(sr.to_string(float_format=".2%"), str), str) + check(assert_type(sr.to_string(float_format="%.2f"), str), str) + check(assert_type(sr.to_string(float_format="%.2f"), str), str) def test_types_mask() -> None: From b910bdc91fd0f16479907673f48780a97cc16a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Diridollou?= Date: Fri, 25 Apr 2025 13:19:55 -0400 Subject: [PATCH 3/3] GH730 PR feedback --- pandas-stubs/_typing.pyi | 2 +- pandas-stubs/core/frame.pyi | 4 ++-- pandas-stubs/core/series.pyi | 4 ++-- tests/test_frame.py | 1 - tests/test_series.py | 2 -- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pandas-stubs/_typing.pyi b/pandas-stubs/_typing.pyi index 965a146da..d140b79d5 100644 --- a/pandas-stubs/_typing.pyi +++ b/pandas-stubs/_typing.pyi @@ -626,7 +626,7 @@ CompressionOptions: TypeAlias = ( FormattersType: TypeAlias = ( list[Callable] | tuple[Callable, ...] | Mapping[str | int, Callable] ) -FloatFormatType: TypeAlias = str | Callable | EngFormatter +FloatFormatType: TypeAlias = str | Callable[[float], str] | EngFormatter # converters ConvertersArg: TypeAlias = dict[Hashable, Callable[[Dtype], Dtype]] diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index 232157b7c..9ecec7c19 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -2314,7 +2314,7 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack): index: _bool = ..., na_rep: _str = ..., formatters: FormattersType | None = ..., - float_format: FloatFormatType | Callable[[float], str] | None = ..., + float_format: FloatFormatType | None = ..., sparsify: _bool | None = ..., index_names: _bool = ..., justify: _str | None = ..., @@ -2337,7 +2337,7 @@ class DataFrame(NDFrame, OpsMixin, _GetItemHack): index: _bool = ..., na_rep: _str = ..., formatters: FormattersType | None = ..., - float_format: FloatFormatType | Callable[[float], str] | None = ..., + float_format: FloatFormatType | None = ..., sparsify: _bool | None = ..., index_names: _bool = ..., justify: _str | None = ..., diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 739845eb4..ed19e23ee 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -531,7 +531,7 @@ class Series(IndexOpsMixin[S1], NDFrame): self, buf: FilePath | WriteBuffer[str], na_rep: _str = ..., - float_format: FloatFormatType | Callable[[float], str] = ..., + float_format: FloatFormatType = ..., header: _bool = ..., index: _bool = ..., length: _bool = ..., @@ -545,7 +545,7 @@ class Series(IndexOpsMixin[S1], NDFrame): self, buf: None = ..., na_rep: _str = ..., - float_format: FloatFormatType | Callable[[float], str] = ..., + float_format: FloatFormatType = ..., header: _bool = ..., index: _bool = ..., length: _bool = ..., diff --git a/tests/test_frame.py b/tests/test_frame.py index 9b2b354fe..d292058d3 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -1709,7 +1709,6 @@ def test_dataframe_to_string_float_fmt() -> None: def _formatter(x) -> str: return f"{x:.2f}" - check(assert_type(df.to_string(), str), str) check(assert_type(df.to_string(float_format=_formatter), str), str) check( assert_type( diff --git a/tests/test_series.py b/tests/test_series.py index dfdaeb43a..4cc33628c 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -2944,7 +2944,6 @@ def test_series_to_string_float_fmt() -> None: def _formatter(x) -> str: return f"{x:.2f}" - check(assert_type(sr.to_string(), str), str) check(assert_type(sr.to_string(float_format=_formatter), str), str) check( assert_type( @@ -2954,7 +2953,6 @@ def _formatter(x) -> str: str, ) check(assert_type(sr.to_string(float_format="%.2f"), str), str) - check(assert_type(sr.to_string(float_format="%.2f"), str), str) def test_types_mask() -> None: