Как сохранить 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
  • Альтернативы: xlsxwriter vs openpyxl, Parquet для больших объёмов

Базовый синтаксис

import pandas as pd

df.to_excel('output.xlsx', index=False)

Требует установленного openpyxl:

pip install openpyxl

1. Без индекса

По умолчанию 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 + 2

5. Форматы чисел

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.dimensions

9. 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+ вопросами для собесов.