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.