Как сохранить DataFrame в Excel
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это нужно
Excel — язык общения с бизнесом. CFO не будет открывать Jupyter ноутбук, CMO не захочет смотреть на Parquet. Но отдать xlsx с пятью листами, сводной таблицей и условным форматированием — и вы популярный аналитик в компании.
to_excel в pandas кажется простым, но для красивого отчёта нужно уметь: несколько листов, форматирование ячеек (валюта, проценты, даты), ширина колонок, freeze panes для scroll, условное форматирование (подсветка outliers), даже добавление графиков. Всё это доступно через openpyxl.
В статье — полный гайд:
- Один и несколько листов через
ExcelWriter - Форматирование заголовка (цвет, bold)
- Автоматическая ширина колонок по контенту
- Формат чисел: валюта, проценты, даты
- Условное форматирование (цветовая шкала)
- Auto-filter и freeze panes
- Добавить chart в Excel через pandas
- Альтернативы:
xlsxwritervsopenpyxl, Parquet для больших объёмов
Базовый синтаксис
import pandas as pd
df.to_excel('output.xlsx', index=False)Требует установленного openpyxl:
pip install openpyxl1. Без индекса
По умолчанию pandas сохраняет индекс. Обычно не нужно:
df.to_excel('output.xlsx', index=False)2. С именем листа
df.to_excel('output.xlsx', sheet_name='Report Q1', index=False)3. Несколько листов
with pd.ExcelWriter('output.xlsx') as writer:
df_sales.to_excel(writer, sheet_name='Sales', index=False)
df_users.to_excel(writer, sheet_name='Users', index=False)
df_summary.to_excel(writer, sheet_name='Summary', index=False)4. Форматирование
Через openpyxl engine
from openpyxl.styles import PatternFill, Font, Alignment
with pd.ExcelWriter('output.xlsx', engine='openpyxl') as writer:
df.to_excel(writer, sheet_name='Report', index=False)
worksheet = writer.sheets['Report']
# стиль заголовка
header_font = Font(bold=True, color='FFFFFF')
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
for cell in worksheet[1]:
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal='center')Ширина колонок
with pd.ExcelWriter('output.xlsx') as writer:
df.to_excel(writer, index=False)
worksheet = writer.sheets['Sheet1']
for col in worksheet.columns:
max_length = max(len(str(cell.value)) for cell in col)
worksheet.column_dimensions[col[0].column_letter].width = max_length + 25. Форматы чисел
from openpyxl.styles import numbers
with pd.ExcelWriter('output.xlsx') as writer:
df.to_excel(writer, index=False)
worksheet = writer.sheets['Sheet1']
# колонка B — формат валюты
for cell in worksheet['B'][1:]: # skip header
cell.number_format = '#,##0.00 ₽'
# колонка C — проценты
for cell in worksheet['C'][1:]:
cell.number_format = '0.00%'
# колонка D — дата
for cell in worksheet['D'][1:]:
cell.number_format = 'DD.MM.YYYY'6. Условное форматирование
from openpyxl.formatting.rule import ColorScaleRule
with pd.ExcelWriter('output.xlsx') as writer:
df.to_excel(writer, index=False)
worksheet = writer.sheets['Sheet1']
# цветовая шкала для колонки Revenue (B)
rule = ColorScaleRule(
start_type='min', start_color='FFFFFF',
end_type='max', end_color='FF0000'
)
worksheet.conditional_formatting.add('B2:B100', rule)7. С графиком
from openpyxl.chart import BarChart, Reference
with pd.ExcelWriter('output.xlsx') as writer:
df.to_excel(writer, index=False)
worksheet = writer.sheets['Sheet1']
chart = BarChart()
data = Reference(worksheet, min_col=2, min_row=1, max_row=len(df)+1, max_col=2)
categories = Reference(worksheet, min_col=1, min_row=2, max_row=len(df)+1)
chart.add_data(data, titles_from_data=True)
chart.set_categories(categories)
worksheet.add_chart(chart, 'D2')8. Auto filter (фильтры в заголовке)
with pd.ExcelWriter('output.xlsx') as writer:
df.to_excel(writer, index=False)
worksheet = writer.sheets['Sheet1']
worksheet.auto_filter.ref = worksheet.dimensions9. Freeze panes (зафиксировать заголовок)
with pd.ExcelWriter('output.xlsx') as writer:
df.to_excel(writer, index=False)
worksheet = writer.sheets['Sheet1']
worksheet.freeze_panes = 'A2' # первая строка зафиксирована10. Добавить к существующему файлу
with pd.ExcelWriter('existing.xlsx', engine='openpyxl', mode='a', if_sheet_exists='replace') as writer:
df.to_excel(writer, sheet_name='NewSheet', index=False)mode='a' — append. if_sheet_exists='replace' — заменить, если лист уже есть.
11. Изображения
from openpyxl.drawing.image import Image
with pd.ExcelWriter('output.xlsx') as writer:
df.to_excel(writer, index=False)
worksheet = writer.sheets['Sheet1']
img = Image('logo.png')
worksheet.add_image(img, 'F2')Альтернативы
xlsxwriter (быстрее, меньше функций)
with pd.ExcelWriter('output.xlsx', engine='xlsxwriter') as writer:
df.to_excel(writer, index=False)
workbook = writer.book
worksheet = writer.sheets['Sheet1']
currency_fmt = workbook.add_format({'num_format': '#,##0.00 ₽'})
worksheet.set_column('B:B', 15, currency_fmt)csv вместо Excel
Для большинства аналитических задач CSV достаточно и быстрее. Excel — когда нужна презентация.
Parquet для big data
Не Excel, но быстрее и меньше:
df.to_parquet('data.parquet')Частые ошибки
1. Не установлен openpyxl
ModuleNotFoundError: No module named 'openpyxl'Решение: pip install openpyxl.
2. Index=False забыли
Получаете лишнюю колонку в Excel.
3. Сохранение большого файла
Excel имеет лимит ~1 млн строк на лист. Для больше — CSV или Parquet.
4. Русские буквы
Обычно всё работает из коробки. Если нет — проверьте encoding данных.
5. Даты превращаются в числа
Excel хранит даты как числа. Устанавливайте format_code для колонок с датами.
Связанные темы
FAQ
openpyxl или xlsxwriter?
openpyxl — читать + писать. xlsxwriter — только писать, но быстрее.
Excel или CSV?
Excel для презентаций. CSV для данных и пайплайнов.
Как добавить лист к существующему?
ExcelWriter(..., mode='a', if_sheet_exists='replace').
Больше миллиона строк?
Excel не потянет. Разбейте на несколько файлов или используйте Parquet/CSV.
Тренируйте pandas — откройте тренажёр с 1500+ вопросами для собесов.