diff --git a/doc/source/whatsnew/v0.25.0.rst b/doc/source/whatsnew/v0.25.0.rst index 73745fe0d5988..e2c0c34f0a753 100644 --- a/doc/source/whatsnew/v0.25.0.rst +++ b/doc/source/whatsnew/v0.25.0.rst @@ -765,6 +765,7 @@ I/O - Fixed bug in :func:`pandas.read_csv` where a BOM would result in incorrect parsing using engine='python' (:issue:`26545`) - :func:`read_excel` now raises a ``ValueError`` when input is of type :class:`pandas.io.excel.ExcelFile` and ``engine`` param is passed since :class:`pandas.io.excel.ExcelFile` has an engine defined (:issue:`26566`) - Bug while selecting from :class:`HDFStore` with ``where=''`` specified (:issue:`26610`). +- Fixed bug in :func:`DataFrame.to_excel()` where custom objects (i.e. `PeriodIndex`) inside merged cells were not being converted into types safe for the Excel writer (:issue:`27006`) Plotting ^^^^^^^^ diff --git a/pandas/io/excel/_xlsxwriter.py b/pandas/io/excel/_xlsxwriter.py index 2dc736f81f6f8..2ddfcf3de5a8f 100644 --- a/pandas/io/excel/_xlsxwriter.py +++ b/pandas/io/excel/_xlsxwriter.py @@ -210,7 +210,7 @@ def write_cells(self, cells, sheet_name=None, startrow=0, startcol=0, startcol + cell.col, startrow + cell.mergestart, startcol + cell.mergeend, - cell.val, style) + val, style) else: wks.write(startrow + cell.row, startcol + cell.col, diff --git a/pandas/tests/io/excel/test_writers.py b/pandas/tests/io/excel/test_writers.py index ea75e97bace0b..a4fdcdf70a3ea 100644 --- a/pandas/tests/io/excel/test_writers.py +++ b/pandas/tests/io/excel/test_writers.py @@ -1162,6 +1162,22 @@ def test_path_local_path(self, engine, ext): path="foo.{ext}".format(ext=ext)) tm.assert_frame_equal(result, df) + def test_merged_cell_custom_objects(self, engine, merge_cells, ext): + # see GH-27006 + mi = MultiIndex.from_tuples([(pd.Period('2018'), pd.Period('2018Q1')), + (pd.Period('2018'), pd.Period('2018Q2'))]) + expected = DataFrame(np.ones((2, 2)), columns=mi) + expected.to_excel(self.path) + result = pd.read_excel(self.path, header=[0, 1], + index_col=0, convert_float=False) + # need to convert PeriodIndexes to standard Indexes for assert equal + expected.columns.set_levels([[str(i) for i in mi.levels[0]], + [str(i) for i in mi.levels[1]]], + level=[0, 1], + inplace=True) + expected.index = expected.index.astype(np.float64) + tm.assert_frame_equal(expected, result) + class TestExcelWriterEngineTests: