pandas to_excel
Write a pandas DataFrame into an existing Excel sheet without overwriting other sheets
Open the file with pd.ExcelWriter(path, mode="a", engine="openpyxl", if_sheet_exists="overlay") and call to_excel with startrow and header=False. On pandas 1.4.0 and later, the block is written into the named sheet and the values on every other sheet are kept. Sparklines, text boxes, cell check boxes and data bar extensions on the other sheets are lost when openpyxl saves the file, so use WolfXL modify mode when the workbook has them.
Why the other sheets disappear
The examples on this page use a workbook test.xlsx with two sheets. Here holds a header row and three dated rows, and Keep holds data that must survive. The goal is to write two more rows into Here!A5:C6 and leave the rest of the workbook alone.
pd.ExcelWriter("test.xlsx") opens the file in write mode, the default. In write mode pandas creates a new workbook, so any sheet you do not write during that session is missing from the saved file. That is why Keep disappears.
Why the writer.book = load_workbook(...) recipe fails on pandas 2.0+
The older recipe loads the workbook with openpyxl, assigns it to writer.book, fills writer.sheets, calls to_excel with startrow, and finishes with writer.save(). In pandas 2.0.0, book became read-only and ExcelWriter.save() was removed. On pandas 3.1.0, running the recipe fails at the assignment with AttributeError: property 'book' of 'OpenpyxlWriter' object has no setter.
The pandas-only fix
import pandas as pd
update = pd.DataFrame({'A': [3, 4], 'B': [4, 5]},
index=pd.date_range('2004-04-30', periods=2, freq='ME'))
update.index = update.index.strftime('%Y-%m-%d')
with pd.ExcelWriter('test.xlsx', mode='a', engine='openpyxl',
if_sheet_exists='overlay') as writer:
update.to_excel(writer, sheet_name='Here', startrow=4, header=False)mode="a": open the existing file for appending, so pandas keeps its sheets.engine="openpyxl": use the engine that supports append mode for .xlsx files. XlsxWriter can only create new files.if_sheet_exists="overlay": write into the existingHeresheet and keep the cells you do not write. With the default,"error", pandas raisesValueError: Sheet 'Here' already exists and if_sheet_exists is set to 'error'.With"replace", pandas deletes the sheet and writes a new one, so the rows you wanted to keep are lost.startrow=4: the row number is zero-based, so the first written row is row 5. The index goes into column A, so the two rows coverA5:C6.header=False: leave out the column-name row, so the data starts at row 5.
Version notes: mode="a" is available since pandas 0.24.0, if_sheet_exists since 1.3.0, and its "overlay" option since 1.4.0. The writer.book setter and writer.save() were removed in pandas 2.0.0.
Dates: format the index before writing
The fix uses strftime("%Y-%m-%d") to turn the index into text, the same way the existing rows of Here were written. If you keep a real DatetimeIndex, pandas writes proper Excel dates with the number format YYYY-MM-DD HH:MM:SS. Passing datetime_format="YYYY-MM-DD" to ExcelWriter has no effect with the openpyxl engine through pandas 3.1 (pandas issue 44284, fixed for pandas 3.2).
freq="ME" is the month-end spelling on pandas 2.2 and later. pandas 3 rejects freq="M" with ValueError: Invalid frequency: M. On pandas older than 2.2, use "M".
The limit: mode="a" saves the whole workbook again
With mode="a", openpyxl loads every sheet, Keep included, and writes the whole workbook again. Values and the styles openpyxl models come through. Workbook features openpyxl does not model are dropped on save. For a plain data workbook this is fine: the Keep values come through unchanged, although the Keep sheet XML is rewritten.
The table compares four approaches on two copies of the same two-sheet workbook. In the second copy, Keep also has a data bar, a sparkline, a text box and a cell check box. The rows written in this comparison kept their DatetimeIndex.
| Method | Plain two-sheet workbook | Keep with a data bar, sparkline, text box and check box |
|---|---|---|
| Older writer.book = load_workbook(...) recipe | AttributeError at writer.book = book | AttributeError at writer.book = book |
| mode="a" without if_sheet_exists | ValueError: Sheet 'Here' already exists ... | ValueError: Sheet 'Here' already exists ... |
| mode="a", if_sheet_exists="overlay" | A5:C6 written. Sheet Keep: values unchanged, XML rewritten | A5:C6 written. Sheet Keep: values and italic font kept; data bar extension, sparkline, text box and cell check box lost |
| WolfXL modify mode, cell loop | A5:C6 written. Sheet Keep: XML byte-identical | A5:C6 written. Sheet Keep: all four features kept, XML byte-identical |
Keep the other sheets byte-identical with WolfXL modify mode
In modify mode, WolfXL rewrites the sheet you edit and copies the other sheets unchanged. WolfXL has no to_excel integration, so the write is a plain cell loop over the DataFrame. Install version 2.0.18 or later:
pip install "wolfxl>=2.0.18"The loop continues from the pandas example, so update and its text index are already defined:
import wolfxl
wb = wolfxl.load_workbook('test.xlsx', modify=True)
ws = wb['Here']
for r, (day, row) in enumerate(update.iterrows(), start=5):
ws.cell(row=r, column=1, value=day)
for c, value in enumerate(row, start=2):
ws.cell(row=r, column=c, value=value)
wb.save('test.xlsx')
wb.close()To write real Excel dates, skip the strftime line and pass day.date(): the cell gets the number format yyyy-mm-dd. Passing the pandas Timestamp directly gives yyyy-mm-dd h:mm:ss. Date writes need wolfxl 2.0.18 or later, because 2.0.17 raised ValueError: Unknown cell type: 'datetime' in modify mode.
How this was tested
Last checked on 2026-10-26 with pandas 3.1.0, openpyxl 3.1.5, XlsxWriter 3.3.0, wolfxl 2.0.18 and CPython 3.13. Both code blocks ran as shown, with only the file paths changed, each against its own copy of the two-sheet test.xlsx. After each one, Here held its original rows plus 2004-04-30, 3, 4 and 2004-05-31, 4, 5 in rows 5 and 6, and Keep still had all five rows. The second workbook in the comparison table was built with XlsxWriter, and each result was checked by reloading the saved file and comparing the Keep worksheet XML part with the source.