From 3e567001355f96aabcb2053f74b047aa3a926d3f Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Mon, 4 Nov 2024 20:58:29 +0800 Subject: [PATCH 01/13] Convert output type in Excel for PeriodIndex in Index or MultiIndex be timestamps --- pandas/io/formats/excel.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pandas/io/formats/excel.py b/pandas/io/formats/excel.py index 52b5755558900..ba77746471ecb 100644 --- a/pandas/io/formats/excel.py +++ b/pandas/io/formats/excel.py @@ -826,6 +826,9 @@ def _format_hierarchical_rows(self) -> Iterable[ExcelCell]: fill_value=levels._na_value, ) + if isinstance(values, PeriodIndex): + values = values.to_timestamp() + for i, span_val in spans.items(): mergestart, mergeend = None, None if span_val > 1: @@ -849,6 +852,10 @@ def _format_hierarchical_rows(self) -> Iterable[ExcelCell]: # Format hierarchical rows with non-merged values. for indexcolvals in zip(*self.df.index): for idx, indexcolval in enumerate(indexcolvals): + + if isinstance(indexcolval, Period): + indexcolval = indexcolval.to_timestamp() + yield CssExcelCell( row=self.rowcounter + idx, col=gcolidx, From 71ef0aa50564345799a347fc24f33c7466d8d2cd Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Fri, 8 Nov 2024 20:22:25 +0800 Subject: [PATCH 02/13] add tests and references --- pandas/io/formats/excel.py | 7 +++-- pandas/tests/io/formats/test_to_excel.py | 39 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/pandas/io/formats/excel.py b/pandas/io/formats/excel.py index ba77746471ecb..e123de91d4770 100644 --- a/pandas/io/formats/excel.py +++ b/pandas/io/formats/excel.py @@ -37,6 +37,7 @@ DataFrame, Index, MultiIndex, + Period, PeriodIndex, ) import pandas.core.common as com @@ -825,8 +826,8 @@ def _format_hierarchical_rows(self) -> Iterable[ExcelCell]: allow_fill=levels._can_hold_na, fill_value=levels._na_value, ) - - if isinstance(values, PeriodIndex): + # GH#60099 + if isinstance(values[0], Period): values = values.to_timestamp() for i, span_val in spans.items(): @@ -852,7 +853,7 @@ def _format_hierarchical_rows(self) -> Iterable[ExcelCell]: # Format hierarchical rows with non-merged values. for indexcolvals in zip(*self.df.index): for idx, indexcolval in enumerate(indexcolvals): - + # GH#60099 if isinstance(indexcolval, Period): indexcolval = indexcolval.to_timestamp() diff --git a/pandas/tests/io/formats/test_to_excel.py b/pandas/tests/io/formats/test_to_excel.py index b40201b9ba1e6..b3b7cd2093b82 100644 --- a/pandas/tests/io/formats/test_to_excel.py +++ b/pandas/tests/io/formats/test_to_excel.py @@ -9,6 +9,13 @@ from pandas.errors import CSSWarning +from pandas import ( + DataFrame, + MultiIndex, + Timestamp, + period_range, + to_datetime, +) import pandas._testing as tm from pandas.io.formats.excel import ( @@ -428,3 +435,35 @@ def test_css_excel_cell_cache(styles, cache_hits, cache_misses): assert cache_info.hits == cache_hits assert cache_info.misses == cache_misses + + +def test_format_hierarchical_rows_with_periodindex(): + # GH#60099 + period_index = period_range(start="2006-10-06", end="2006-10-07", freq="D") + df = DataFrame({"A": [0, 0]}, index=period_index) + converter = CSSToExcelConverter() + cells: list[CssExcelCell] = list(converter._format_hierarchical_rows(df)) + for cell in cells: + assert isinstance(cell.val, Timestamp), "Expected cell value to be a Timestamp" + assert cell.val in to_datetime( + ["2006-10-06", "2006-10-07"] + ), "Unexpected cell value" + + +def test_format_hierarchical_rows_with_period(): + # GH#60099 + period_index = period_range(start="2006-10-06", end="2006-10-07", freq="D") + number_index = ["1", "2"] + df = DataFrame( + {"A": [0, 0]}, index=MultiIndex.from_arrays([period_index, number_index]) + ) + converter = CSSToExcelConverter() + cells: list[CssExcelCell] = list(converter._format_hierarchical_rows(df)) + for cell in cells: + if cell.css_col == 0: + assert isinstance( + cell.val, Timestamp + ), "Expected cell value to be a Timestamp" + assert cell.val in to_datetime( + ["2006-10-06", "2006-10-07"] + ), "Unexpected cell value" From be86dd03ab55fda5db697510e3ce86c72f00bee7 Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Sat, 9 Nov 2024 17:05:34 +0800 Subject: [PATCH 03/13] Modify the tests --- pandas/tests/io/excel/test_style.py | 39 ++++++++++++++++++++++++ pandas/tests/io/formats/test_to_excel.py | 39 ------------------------ 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/pandas/tests/io/excel/test_style.py b/pandas/tests/io/excel/test_style.py index f70e65e34c584..0adc4b6c7d3f6 100644 --- a/pandas/tests/io/excel/test_style.py +++ b/pandas/tests/io/excel/test_style.py @@ -9,6 +9,9 @@ from pandas import ( DataFrame, + MultiIndex, + Period, + period_range, read_excel, ) import pandas._testing as tm @@ -333,3 +336,39 @@ def test_styler_to_s3(s3_public_bucket, s3so): f"s3://{mock_bucket_name}/{target_file}", index_col=0, storage_options=s3so ) tm.assert_frame_equal(result, df) + + +def test_format_hierarchical_rows_periodindex_merge_cells(): + # GH#60099 + df = DataFrame( + {"A": [1, 2]}, + index=MultiIndex.from_arrays( + [period_range("2023-01", "2023-02", freq="M"), ["X", "Y"]], + names=["date", "category"], + ), + ) + formatter = ExcelFormatter(df, merge_cells=True) + formatted_cells = list(formatter._format_hierarchical_rows()) + + for cell in formatted_cells: + assert not isinstance( + cell.val, Period + ), "Period should be converted to Timestamp" + + +def test_format_hierarchical_rows_periodindex_no_merge_cells(): + # GH#60099 + df = DataFrame( + {"A": [1, 2]}, + index=MultiIndex.from_arrays( + [period_range("2023-01", "2023-02", freq="M"), ["X", "Y"]], + names=["date", "category"], + ), + ) + formatter = ExcelFormatter(df, merge_cells=False) + formatted_cells = list(formatter._format_hierarchical_rows()) + + for cell in formatted_cells: + assert not isinstance( + cell.val, Period + ), "Period should be converted to Timestamp" diff --git a/pandas/tests/io/formats/test_to_excel.py b/pandas/tests/io/formats/test_to_excel.py index b3b7cd2093b82..b40201b9ba1e6 100644 --- a/pandas/tests/io/formats/test_to_excel.py +++ b/pandas/tests/io/formats/test_to_excel.py @@ -9,13 +9,6 @@ from pandas.errors import CSSWarning -from pandas import ( - DataFrame, - MultiIndex, - Timestamp, - period_range, - to_datetime, -) import pandas._testing as tm from pandas.io.formats.excel import ( @@ -435,35 +428,3 @@ def test_css_excel_cell_cache(styles, cache_hits, cache_misses): assert cache_info.hits == cache_hits assert cache_info.misses == cache_misses - - -def test_format_hierarchical_rows_with_periodindex(): - # GH#60099 - period_index = period_range(start="2006-10-06", end="2006-10-07", freq="D") - df = DataFrame({"A": [0, 0]}, index=period_index) - converter = CSSToExcelConverter() - cells: list[CssExcelCell] = list(converter._format_hierarchical_rows(df)) - for cell in cells: - assert isinstance(cell.val, Timestamp), "Expected cell value to be a Timestamp" - assert cell.val in to_datetime( - ["2006-10-06", "2006-10-07"] - ), "Unexpected cell value" - - -def test_format_hierarchical_rows_with_period(): - # GH#60099 - period_index = period_range(start="2006-10-06", end="2006-10-07", freq="D") - number_index = ["1", "2"] - df = DataFrame( - {"A": [0, 0]}, index=MultiIndex.from_arrays([period_index, number_index]) - ) - converter = CSSToExcelConverter() - cells: list[CssExcelCell] = list(converter._format_hierarchical_rows(df)) - for cell in cells: - if cell.css_col == 0: - assert isinstance( - cell.val, Timestamp - ), "Expected cell value to be a Timestamp" - assert cell.val in to_datetime( - ["2006-10-06", "2006-10-07"] - ), "Unexpected cell value" From a7db60222d73b58ad4d892762d00a3657ef7da43 Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Mon, 11 Nov 2024 14:45:54 +0800 Subject: [PATCH 04/13] Merge and add the tests --- pandas/tests/io/excel/test_style.py | 23 +++-------------------- pandas/tests/io/excel/test_writers.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/pandas/tests/io/excel/test_style.py b/pandas/tests/io/excel/test_style.py index 0adc4b6c7d3f6..8bded070ea68b 100644 --- a/pandas/tests/io/excel/test_style.py +++ b/pandas/tests/io/excel/test_style.py @@ -338,7 +338,8 @@ def test_styler_to_s3(s3_public_bucket, s3so): tm.assert_frame_equal(result, df) -def test_format_hierarchical_rows_periodindex_merge_cells(): +@pytest.mark.parametrize("merge_cells", [True, False]) +def test_format_hierarchical_rows_periodindex(merge_cells): # GH#60099 df = DataFrame( {"A": [1, 2]}, @@ -347,25 +348,7 @@ def test_format_hierarchical_rows_periodindex_merge_cells(): names=["date", "category"], ), ) - formatter = ExcelFormatter(df, merge_cells=True) - formatted_cells = list(formatter._format_hierarchical_rows()) - - for cell in formatted_cells: - assert not isinstance( - cell.val, Period - ), "Period should be converted to Timestamp" - - -def test_format_hierarchical_rows_periodindex_no_merge_cells(): - # GH#60099 - df = DataFrame( - {"A": [1, 2]}, - index=MultiIndex.from_arrays( - [period_range("2023-01", "2023-02", freq="M"), ["X", "Y"]], - names=["date", "category"], - ), - ) - formatter = ExcelFormatter(df, merge_cells=False) + formatter = ExcelFormatter(df, merge_cells=merge_cells) formatted_cells = list(formatter._format_hierarchical_rows()) for cell in formatted_cells: diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 81aa0be24bffc..095f2283c8c14 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -25,6 +25,7 @@ MultiIndex, date_range, option_context, + period_range, ) import pandas._testing as tm @@ -1549,3 +1550,24 @@ def test_subclass_attr(klass): attrs_base = {name for name in dir(ExcelWriter) if not name.startswith("_")} attrs_klass = {name for name in dir(klass) if not name.startswith("_")} assert not attrs_base.symmetric_difference(attrs_klass) + + +@pytest.mark.parametrize("merge_cells", [True, False]) +def test_excel_round_trip_with_periodindex(merge_cells): + # GH#60099 + df = DataFrame( + {"A": [1, 2]}, + index=MultiIndex.from_arrays( + [period_range("2023-01", "2023-02", freq="M"), ["X", "Y"]], + names=["date", "category"], + ), + ) + + with BytesIO() as buffer: + with ExcelWriter(buffer, engine="xlsxwriter") as writer: + df.to_excel(writer, merge_cells=merge_cells) + + buffer.seek(0) + result_df = pd.read_excel(buffer, index_col=[0, 1]) + + tm.assert_frame_equal(df, result_df) From 901f69fa92c8fea3053c2d448eb7261085379904 Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Mon, 11 Nov 2024 15:39:18 +0800 Subject: [PATCH 05/13] Merge and add the tests --- pandas/tests/io/excel/test_writers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 095f2283c8c14..101733094c7ab 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -1569,5 +1569,8 @@ def test_excel_round_trip_with_periodindex(merge_cells): buffer.seek(0) result_df = pd.read_excel(buffer, index_col=[0, 1]) + result_df.index = result_df.index.set_levels( + [result_df.index.levels[0].to_period("M"), result_df.index.levels[1]] + ) tm.assert_frame_equal(df, result_df) From 9636523415f34e9e0d35d0be2648204edb1a8fbe Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Mon, 11 Nov 2024 19:31:08 +0800 Subject: [PATCH 06/13] Change excel engine --- pandas/tests/io/excel/test_writers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 101733094c7ab..5ebaeef8d3bb8 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -1553,7 +1553,8 @@ def test_subclass_attr(klass): @pytest.mark.parametrize("merge_cells", [True, False]) -def test_excel_round_trip_with_periodindex(merge_cells): +@pytest.mark.parametrize("engine", ["openpyxl", "xlsxwriter"]) +def test_excel_round_trip_with_periodindex(merge_cells, engine): # GH#60099 df = DataFrame( {"A": [1, 2]}, @@ -1564,7 +1565,7 @@ def test_excel_round_trip_with_periodindex(merge_cells): ) with BytesIO() as buffer: - with ExcelWriter(buffer, engine="xlsxwriter") as writer: + with ExcelWriter(buffer, engine=engine) as writer: df.to_excel(writer, merge_cells=merge_cells) buffer.seek(0) From 7cfd85781e50e7ca25b7fec8d1c876a52b1fc9cc Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Tue, 12 Nov 2024 00:37:22 +0800 Subject: [PATCH 07/13] Delete engine --- pandas/tests/io/excel/test_writers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 5ebaeef8d3bb8..41553fb02d988 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -1553,7 +1553,6 @@ def test_subclass_attr(klass): @pytest.mark.parametrize("merge_cells", [True, False]) -@pytest.mark.parametrize("engine", ["openpyxl", "xlsxwriter"]) def test_excel_round_trip_with_periodindex(merge_cells, engine): # GH#60099 df = DataFrame( @@ -1565,7 +1564,7 @@ def test_excel_round_trip_with_periodindex(merge_cells, engine): ) with BytesIO() as buffer: - with ExcelWriter(buffer, engine=engine) as writer: + with ExcelWriter(buffer) as writer: df.to_excel(writer, merge_cells=merge_cells) buffer.seek(0) From 469b6e4b61b9ebcd05cbbe3c372008238446ed25 Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Tue, 12 Nov 2024 10:56:17 +0800 Subject: [PATCH 08/13] Modify the tests --- pandas/tests/io/excel/test_style.py | 5 ++- pandas/tests/io/excel/test_writers.py | 54 +++++++++++++++------------ 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/pandas/tests/io/excel/test_style.py b/pandas/tests/io/excel/test_style.py index 8bded070ea68b..8bfbd8761ab4a 100644 --- a/pandas/tests/io/excel/test_style.py +++ b/pandas/tests/io/excel/test_style.py @@ -344,7 +344,10 @@ def test_format_hierarchical_rows_periodindex(merge_cells): df = DataFrame( {"A": [1, 2]}, index=MultiIndex.from_arrays( - [period_range("2023-01", "2023-02", freq="M"), ["X", "Y"]], + [ + period_range(start="2006-10-06", end="2006-10-07", freq="D"), + ["X", "Y"], + ], names=["date", "category"], ), ) diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 41553fb02d988..4ad7bc1445375 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -338,6 +338,36 @@ def test_multiindex_interval_datetimes(self, tmp_excel): ) tm.assert_frame_equal(result, expected) + @pytest.mark.parametrize("merge_cells", [True, False]) + def test_excel_round_trip_with_periodindex(self, tmp_excel, merge_cells): + # GH#60099 + df = DataFrame( + {"A": [1, 2]}, + index=MultiIndex.from_arrays( + [ + period_range(start="2006-10-06", end="2006-10-07", freq="D"), + ["X", "Y"], + ], + names=["date", "category"], + ), + ) + df.to_excel(tmp_excel, merge_cells=merge_cells) + result = pd.read_excel(tmp_excel, index_col=[0, 1]) + expected = DataFrame( + {"A": [1, 2]}, + MultiIndex.from_arrays( + [ + [ + "2006-10-06 00:00:00", + "2020-01-31 00:00:00", + ], + ["X", "Y"], + ], + names=["date", "category"], + ), + ) + tm.assert_frame_equal(result, expected) + @pytest.mark.parametrize( "engine,ext", @@ -1550,27 +1580,3 @@ def test_subclass_attr(klass): attrs_base = {name for name in dir(ExcelWriter) if not name.startswith("_")} attrs_klass = {name for name in dir(klass) if not name.startswith("_")} assert not attrs_base.symmetric_difference(attrs_klass) - - -@pytest.mark.parametrize("merge_cells", [True, False]) -def test_excel_round_trip_with_periodindex(merge_cells, engine): - # GH#60099 - df = DataFrame( - {"A": [1, 2]}, - index=MultiIndex.from_arrays( - [period_range("2023-01", "2023-02", freq="M"), ["X", "Y"]], - names=["date", "category"], - ), - ) - - with BytesIO() as buffer: - with ExcelWriter(buffer) as writer: - df.to_excel(writer, merge_cells=merge_cells) - - buffer.seek(0) - result_df = pd.read_excel(buffer, index_col=[0, 1]) - result_df.index = result_df.index.set_levels( - [result_df.index.levels[0].to_period("M"), result_df.index.levels[1]] - ) - - tm.assert_frame_equal(df, result_df) From 1b284d4ea506ec8178d92836b30d217fd86b839e Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Tue, 12 Nov 2024 14:16:41 +0800 Subject: [PATCH 09/13] unit conversions --- pandas/tests/io/excel/test_writers.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index 4ad7bc1445375..cb0610fb68569 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -352,20 +352,30 @@ def test_excel_round_trip_with_periodindex(self, tmp_excel, merge_cells): ), ) df.to_excel(tmp_excel, merge_cells=merge_cells) + result = pd.read_excel(tmp_excel, index_col=[0, 1]) + if result.index.levels[0].dtype == "datetime64[us]": + result.index = result.index.set_levels( + result.index.levels[0].astype("datetime64[s]"), level=0 + ) + expected = DataFrame( {"A": [1, 2]}, MultiIndex.from_arrays( [ [ - "2006-10-06 00:00:00", - "2020-01-31 00:00:00", + pd.to_datetime("2006-10-06 00:00:00"), + pd.to_datetime("2006-10-07 00:00:00"), ], ["X", "Y"], ], names=["date", "category"], ), ) + expected.index = expected.index.set_levels( + expected.index.levels[0].astype("datetime64[s]"), level=0 + ) + tm.assert_frame_equal(result, expected) From 3096671ae1439461581f6dd42589e67018545cac Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Wed, 13 Nov 2024 19:03:45 +0800 Subject: [PATCH 10/13] Modify the 'expected' --- pandas/tests/io/excel/test_style.py | 2 +- pandas/tests/io/excel/test_writers.py | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pandas/tests/io/excel/test_style.py b/pandas/tests/io/excel/test_style.py index 8bfbd8761ab4a..45cf240363846 100644 --- a/pandas/tests/io/excel/test_style.py +++ b/pandas/tests/io/excel/test_style.py @@ -338,7 +338,7 @@ def test_styler_to_s3(s3_public_bucket, s3so): tm.assert_frame_equal(result, df) -@pytest.mark.parametrize("merge_cells", [True, False]) +@pytest.mark.parametrize("merge_cells", [True, False, "columns"]) def test_format_hierarchical_rows_periodindex(merge_cells): # GH#60099 df = DataFrame( diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index cb0610fb68569..b9b49bbf1456a 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -338,7 +338,7 @@ def test_multiindex_interval_datetimes(self, tmp_excel): ) tm.assert_frame_equal(result, expected) - @pytest.mark.parametrize("merge_cells", [True, False]) + @pytest.mark.parametrize("merge_cells", [True, False, "columns"]) def test_excel_round_trip_with_periodindex(self, tmp_excel, merge_cells): # GH#60099 df = DataFrame( @@ -352,13 +352,7 @@ def test_excel_round_trip_with_periodindex(self, tmp_excel, merge_cells): ), ) df.to_excel(tmp_excel, merge_cells=merge_cells) - result = pd.read_excel(tmp_excel, index_col=[0, 1]) - if result.index.levels[0].dtype == "datetime64[us]": - result.index = result.index.set_levels( - result.index.levels[0].astype("datetime64[s]"), level=0 - ) - expected = DataFrame( {"A": [1, 2]}, MultiIndex.from_arrays( @@ -372,9 +366,14 @@ def test_excel_round_trip_with_periodindex(self, tmp_excel, merge_cells): names=["date", "category"], ), ) - expected.index = expected.index.set_levels( - expected.index.levels[0].astype("datetime64[s]"), level=0 - ) + if tmp_excel.endswith(".ods"): + expected.index = expected.index.set_levels( + expected.index.levels[0].astype("datetime64[s]"), level=0 + ) + else: + expected.index = expected.index.set_levels( + expected.index.levels[0].astype("datetime64[us]"), level=0 + ) tm.assert_frame_equal(result, expected) From 704d42a529590beaa848441ae2ce4427807958f3 Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Sun, 17 Nov 2024 17:12:06 +0800 Subject: [PATCH 11/13] save memory and modify format --- pandas/tests/io/excel/test_style.py | 2 +- pandas/tests/io/excel/test_writers.py | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/pandas/tests/io/excel/test_style.py b/pandas/tests/io/excel/test_style.py index 45cf240363846..8f615cbddf7f8 100644 --- a/pandas/tests/io/excel/test_style.py +++ b/pandas/tests/io/excel/test_style.py @@ -352,7 +352,7 @@ def test_format_hierarchical_rows_periodindex(merge_cells): ), ) formatter = ExcelFormatter(df, merge_cells=merge_cells) - formatted_cells = list(formatter._format_hierarchical_rows()) + formatted_cells = formatter._format_hierarchical_rows() for cell in formatted_cells: assert not isinstance( diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index b9b49bbf1456a..a77c9ce26c8eb 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -366,14 +366,12 @@ def test_excel_round_trip_with_periodindex(self, tmp_excel, merge_cells): names=["date", "category"], ), ) - if tmp_excel.endswith(".ods"): - expected.index = expected.index.set_levels( - expected.index.levels[0].astype("datetime64[s]"), level=0 - ) - else: - expected.index = expected.index.set_levels( - expected.index.levels[0].astype("datetime64[us]"), level=0 - ) + time_format = ( + "datetime64[s]" if tmp_excel.endswith(".ods") else "datetime64[us]" + ) + expected.index = expected.index.set_levels( + expected.index.levels[0].astype(time_format), level=0 + ) tm.assert_frame_equal(result, expected) From 160c249425b891756635f7dfe579e38ac9045088 Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Mon, 18 Nov 2024 00:41:08 +0800 Subject: [PATCH 12/13] add a note --- doc/source/whatsnew/v3.0.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 89bc942cb7250..c2b74f6215a6c 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -691,6 +691,7 @@ I/O - Bug in :meth:`DataFrame.to_stata` when writing :class:`DataFrame` and ``byteorder=`big```. (:issue:`58969`) - Bug in :meth:`DataFrame.to_stata` when writing more than 32,000 value labels. (:issue:`60107`) - Bug in :meth:`DataFrame.to_string` that raised ``StopIteration`` with nested DataFrames. (:issue:`16098`) +- Bug in :meth:`ExcelFormatter._format_hierarchical_rows` where output type in excel for multiIndex with period levels is not a date (:issue:`60099`) - Bug in :meth:`HDFStore.get` was failing to save data of dtype datetime64[s] correctly (:issue:`59004`) - Bug in :meth:`read_csv` causing segmentation fault when ``encoding_errors`` is not a string. (:issue:`59059`) - Bug in :meth:`read_csv` raising ``TypeError`` when ``index_col`` is specified and ``na_values`` is a dict containing the key ``None``. (:issue:`57547`) From 192c1b73e158d6aef59e9ae265c7224f21dd7f85 Mon Sep 17 00:00:00 2001 From: ZKaoChi <1953542921@qq.com> Date: Fri, 22 Nov 2024 16:29:51 +0800 Subject: [PATCH 13/13] assert Timestamp instead --- doc/source/whatsnew/v3.0.0.rst | 2 +- pandas/tests/io/excel/test_style.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 3ab8a24e736d5..7242503d7b181 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -688,10 +688,10 @@ I/O - Bug in :meth:`DataFrame.from_records` where ``columns`` parameter with numpy structured array was not reordering and filtering out the columns (:issue:`59717`) - Bug in :meth:`DataFrame.to_dict` raises unnecessary ``UserWarning`` when columns are not unique and ``orient='tight'``. (:issue:`58281`) - Bug in :meth:`DataFrame.to_excel` when writing empty :class:`DataFrame` with :class:`MultiIndex` on both axes (:issue:`57696`) +- Bug in :meth:`DataFrame.to_excel` where the :class:`MultiIndex` index with a period level was not a date (:issue:`60099`) - Bug in :meth:`DataFrame.to_stata` when writing :class:`DataFrame` and ``byteorder=`big```. (:issue:`58969`) - Bug in :meth:`DataFrame.to_stata` when writing more than 32,000 value labels. (:issue:`60107`) - Bug in :meth:`DataFrame.to_string` that raised ``StopIteration`` with nested DataFrames. (:issue:`16098`) -- Bug in :meth:`ExcelFormatter._format_hierarchical_rows` where output type in excel for multiIndex with period levels is not a date (:issue:`60099`) - Bug in :meth:`HDFStore.get` was failing to save data of dtype datetime64[s] correctly (:issue:`59004`) - Bug in :meth:`read_csv` causing segmentation fault when ``encoding_errors`` is not a string. (:issue:`59059`) - Bug in :meth:`read_csv` raising ``TypeError`` when ``index_col`` is specified and ``na_values`` is a dict containing the key ``None``. (:issue:`57547`) diff --git a/pandas/tests/io/excel/test_style.py b/pandas/tests/io/excel/test_style.py index 8f615cbddf7f8..71ef1201e523f 100644 --- a/pandas/tests/io/excel/test_style.py +++ b/pandas/tests/io/excel/test_style.py @@ -10,7 +10,7 @@ from pandas import ( DataFrame, MultiIndex, - Period, + Timestamp, period_range, read_excel, ) @@ -355,6 +355,7 @@ def test_format_hierarchical_rows_periodindex(merge_cells): formatted_cells = formatter._format_hierarchical_rows() for cell in formatted_cells: - assert not isinstance( - cell.val, Period - ), "Period should be converted to Timestamp" + if cell.row != 0 and cell.col == 0: + assert isinstance( + cell.val, Timestamp + ), "Period should be converted to Timestamp"