Pandas и openpyxl: взаимодействие для обработки данных

Раздел: Обработка данных -> Библиотеки для работы с данными

Основные подходы к работе с Excel через Pandas и openpyxl

Наиболее эффективное решение для чтения и записи данных в формате Excel с минимальными усилиями - использование Pandas в паре с движком openpyxl. Pandas предоставляет высокоуровневые методы read_excel и to_excel, которые автоматически конвертируют данные в DataFrame и обратно. Для сохранения форматирования (цвета, границы, шрифты) применяется openpyxl, который можно вызвать через параметр engine='openpyxl'.

Базовый пример записи DataFrame в Excel с сохранением форматирования выглядит так:

import pandas as pd
df = pd.DataFrame({'A': [1,2], 'B': [3,4]})
with pd.ExcelWriter('output.xlsx', engine='openpyxl') as writer:
    df.to_excel(writer, sheet_name='Sheet1', index=False)

Python pandas openpyxl (pandas и openpyxl в python)

Для чтения достаточно указать движок:

df = pd.read_excel('file.xlsx', engine='openpyxl')

Этот подход покрывает 80% типовых задач - импорт/экспорт таблиц, преобразование типов, работа с датами. Однако он не даёт полного контроля над внешним видом ячеек.

Как прочитать данные из определённого листа с заданными типами столбцов?

При чтении Excel часто требуется указать лист, диапазон или типы данных. Pandas позволяет это через аргументы sheet_name и dtype.

df = pd.read_excel('data.xlsx', sheet_name='Продажи', dtype={'ID': str, 'Цена': float})

Если листов несколько, можно передать список или None для загрузки всех:

sheets_dict = pd.read_excel('data.xlsx', sheet_name=None, engine='openpyxl')

Типичная ошибка - игнорирование параметра header. Если заголовки находятся не в первой строке, нужно указывать номер строки явно. Иначе Pandas может прочитать заголовки как данные, и DataFrame получится с числовыми именами колонок.

df = pd.read_excel('data.xlsx', header=2)  # заголовки с 3-й строки

Как записать несколько DataFrame в разные листы одной книги с дополнительными настройками?

Класс ExcelWriter позволяет управлять порядком листов, скрывать индекс, настраивать заголовки. Вариант записи двух таблиц:

with pd.ExcelWriter('report.xlsx', engine='openpyxl') as writer:
    df1.to_excel(writer, sheet_name='Регион', index=False, startrow=5)
    df2.to_excel(writer, sheet_name='Товары', startcol=2, index_label='ID')

При записи с помощью startrow и startcol можно размещать данные на листе с отступом.

Распространённая проблема - потеря форматирования при повторной записи в тот же файл. Если открыть файл в режиме 'a' (append), openpyxl может не сохранить все стили. Рекомендуется всегда перезаписывать файл целиком или использовать библиотеку openpyxl.load_workbook для предварительного чтения.

Как применить сложное форматирование (цвета, границы, шрифты) к данным, экспортированным из Pandas?

Pandas не управляет стилями напрямую. Решение - экспортировать данные через ExcelWriter, затем открыть полученную книгу через openpyxl и модифицировать ячейки. Пример изменения цвета фона:

from openpyxl import load_workbook
from openpyxl.styles import PatternFill

with pd.ExcelWriter('styled.xlsx', engine='openpyxl') as writer:
    df.to_excel(writer, sheet_name='Data', index=False)
    workbook = writer.book
    sheet = workbook['Data']
    for row in sheet.iter_rows(min_row=2, max_row=5, min_col=1, max_col=2):
        for cell in row:
            cell.fill = PatternFill(start_color='FFCC00', end_color='FFCC00', fill_type='solid')

Такой подход сохраняет данные из DataFrame и добавляет оформление без потери информации.

Ошибка: обращение к объекту writer.book возможно только внутри блока with. Если нужно работать с книгой после завершения ExcelWriter, требуется сначала сохранить файл, затем загрузить его через load_workbook.

Как обработать файл Excel с миллионами строк, не загружая его целиком в память?

Для больших файлов Pandas читает всё содержимое, что может вызвать нехватку памяти. Альтернатива - использовать openpyxl в режиме read_only или write_only. Вариант построчного чтения:

from openpyxl import load_workbook
wb = load_workbook('big.xlsx', read_only=True)
ws = wb.active
for row in ws.iter_rows(values_only=True):
    # обработка каждой строки
    pass
wb.close()

Для записи без хранения в памяти применяется WriteOnlyWorkbook:

from openpyxl import Workbook
wb = Workbook(write_only=True)
ws = wb.create_sheet()
ws.append(['Name', 'Value'])
for i in range(1000000):
    ws.append([f'Row{i}', i])
wb.save('big_out.xlsx')

При использовании read_only нельзя изменять ячейки и стили - только чтение. Режим write_only не позволяет вставлять строки или удалять данные после добавления, только последовательная запись.

Расширенные примеры работы с Pandas и openpyxl

Чтение Excel с пропуском строк и обработкой дат

Иногда данные начинаются не с первой строки, а даты представлены в нестандартном формате. Пример файла dates.xlsx с шапкой в строке 3 и датами в столбце B.

Пример
import pandas as pd
df = pd.read_excel('dates.xlsx', header=3, parse_dates=['Date'], engine='openpyxl')
print(df.dtypes)
print(df.head())
Date    datetime64[ns]
Value           float64
dtype: object
        Date  Value
0 2023-01-01  10.5
1 2023-01-02  12.3

Параметр parse_dates автоматически преобразует столбец в pandas Timestamp, а header сдвигает начало данных.

Запись DataFrame с условным форматированием через openpyxl

Допустим, требуется выделить красным ячейки, где значение меньше нуля. Сначала записываем DataFrame через Pandas, затем добавляем условное форматирование.

Пример
import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import PatternFill
from openpyxl.formatting.rule import CellIsRule

df = pd.DataFrame({'A': [1, -2, 3], 'B': [-4, 5, -6]})
with pd.ExcelWriter('cond_format.xlsx', engine='openpyxl') as writer:
    df.to_excel(writer, index=False)
    book = writer.book
    sheet = book.active
    red_fill = PatternFill(start_color='FF0000', end_color='FF0000', fill_type='solid')
    sheet.conditional_formatting.add('A2:C4',
        CellIsRule(operator='lessThan', formula=['0'], fill=red_fill))
wb.save('cond_format.xlsx')

После записи в файле ячейки с отрицательными числами будут окрашены красным.

Объединение данных из нескольких листов в один DataFrame

Часто Excel содержит по листу на месяц, и требуется свести все данные вместе. Использование sheet_name=None загружает все листы в словарь.

Пример
import pandas as pd
all_sheets = pd.read_excel('months.xlsx', sheet_name=None, engine='openpyxl')
df_all = pd.concat(all_sheets.values(), ignore_index=True)
print(len(df_all))  # общее количество строк

Если листы имеют разную структуру, перед объединением нужно привести их к единой схеме, добавив столбец с именем листа:

Пример
dfs = []
for name, sheet_df in all_sheets.items():
    sheet_df['Месяц'] = name
    dfs.append(sheet_df)
df_combined = pd.concat(dfs, ignore_index=True)

Добавление формул в ячейки при записи через Pandas + openpyxl

Формулы Excel (SUM, AVERAGE) можно добавить после записи данных. Пример: после таблицы чисел вставляется строка с суммой.

Пример
import pandas as pd
from openpyxl import load_workbook

df = pd.DataFrame({'Числа': [10, 20, 30]})
with pd.ExcelWriter('formulas.xlsx', engine='openpyxl') as writer:
    df.to_excel(writer, index=False, startrow=0)
    book = writer.book
    sheet = book.active
    last_row = len(df) + 1  # предполагаем, что данные начинаются с A1
    sheet.cell(row=last_row+1, column=1, value='=SUM(A2:A{})'.format(last_row))
    # можно добавить подпись
    sheet.cell(row=last_row, column=1, value='Итого')

Формула будет вычислена при открытии файла в Excel.

Изменение существующего файла без потери форматирования

Если требуется отредактировать один из листов большой книги, не затрагивая остальные листы и стили, применяется следующий подход: загружаем книгу через openpyxl, преобразуем нужный лист в DataFrame, модифицируем, и записываем обратно только этот лист, сохранив остальные.

Пример
import pandas as pd
from openpyxl import load_workbook

book = load_workbook('existing.xlsx')
sheet = book['Data']
data = sheet.values
columns = next(data)[0:]  # первая строка - заголовки
df = pd.DataFrame(data, columns=columns)
# модификация DataFrame
df['New_Col'] = df['Old_Col'] * 2

# Очистка листа и запись обновлённых данных
sheet.delete_rows(1, sheet.max_row)
for r_idx, row in enumerate(df.itertuples(index=False), 1):
    for c_idx, value in enumerate(row, 1):
        sheet.cell(row=r_idx, column=c_idx, value=value)

# Другие листы и их форматирование не затронуты
book.save('existing_updated.xlsx')

Этот метод позволяет сохранить стили, объединённые ячейки и прочие элементы, не поддерживаемые Pandas.

Pandas и openpyxl в Python - comments

En
Python pandas openpyxl (python)