Python处理Excel多工作表:openpyxl与pandas实战对比
1. 场景概述
假设我们需要处理一个包含多个工作表的销售数据Excel文件,主要任务包括:
- 读取和分析数据
- 跨工作表数据汇总
- 数据清洗和转换
- 生成汇总报表
2. 示例数据准备
先创建一个示例Excel文件(sales_data.xlsx):
Sheet1: 北京销售数据
Sheet2: 上海销售数据
Sheet3: 广州销售数据
每个工作表包含:日期、产品、销售额、数量等字段。
3. openpyxl实战示例
3.1 基础操作
from openpyxl import load_workbook, Workbook
from openpyxl.styles import Font, PatternFill, Alignment
from openpyxl.utils import get_column_letter
import datetime
# 1. 读取工作簿
def read_excel_openpyxl(file_path):
"""读取Excel文件的所有工作表"""
wb = load_workbook(file_path, data_only=True) # data_only=True获取计算后的值
sheet_info = {}
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
data = []
# 读取数据(假设第一行是标题)
for row in ws.iter_rows(min_row=2, values_only=True): # 跳过标题行
if any(cell is not None for cell in row): # 跳过空行
data.append(row)
sheet_info[sheet_name] = {
'data': data,
'max_row': ws.max_row,
'max_column': ws.max_column
}
wb.close()
return sheet_info
# 2. 跨工作表汇总
def summarize_sales_openpyxl(file_path):
"""汇总各城市的销售总额"""
wb = load_workbook(file_path, data_only=True)
summary = {}
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
total_sales = 0
total_quantity = 0
# 假设第3列是销售额,第4列是数量
for row in ws.iter_rows(min_row=2, max_col=4):
sales = row[2].value or 0 # 第3列
quantity = row[3].value or 0 # 第4列
if isinstance(sales, (int, float)):
total_sales += sales
if isinstance(quantity, (int, float)):
total_quantity += quantity
summary[sheet_name] = {
'total_sales': total_sales,
'total_quantity': total_quantity,
'avg_price': total_sales / total_quantity if total_quantity > 0 else 0
}
wb.close()
return summary
# 3. 数据清洗和格式设置
def clean_and_format_openpyxl(file_path, output_path):
"""数据清洗并添加格式"""
wb = load_workbook(file_path)
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
# 添加汇总行
total_row = ws.max_row + 2
ws[f'A{total_row}'] = '总计'
# 计算总和
sales_col = get_column_letter(3) # C列
quantity_col = get_column_letter(4) # D列
ws[f'{sales_col}{total_row}'] = f'=SUM({sales_col}2:{sales_col}{ws.max_row})'
ws[f'{quantity_col}{total_row}'] = f'=SUM({quantity_col}2:{quantity_col}{ws.max_row})'
# 设置格式
header_font = Font(bold=True, color="FFFFFF")
header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
for cell in ws[1]: # 标题行
cell.font = header_font
cell.fill = header_fill
cell.alignment = Alignment(horizontal='center')
# 创建汇总工作表
summary_ws = wb.create_sheet(title='汇总')
summary_ws.append(['城市', '销售总额', '总数量', '平均单价'])
summary_data = summarize_sales_openpyxl(file_path)
for city, data in summary_data.items():
summary_ws.append([city, data['total_sales'], data['total_quantity'], data['avg_price']])
# 自动调整列宽
for sheet in wb.worksheets:
for column in sheet.columns:
max_length = 0
column_letter = get_column_letter(column[0].column)
for cell in column:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = min(max_length + 2, 50)
sheet.column_dimensions[column_letter].width = adjusted_width
wb.save(output_path)
wb.close()
print(f"处理完成,保存到: {output_path}")
4. pandas实战示例
4.1 基础操作
import pandas as pd
import numpy as np
from openpyxl import load_workbook
from openpyxl.styles import Font, PatternFill
import warnings
warnings.filterwarnings('ignore')
# 1. 读取工作簿
def read_excel_pandas(file_path):
"""使用pandas读取所有工作表"""
# 方法1:读取所有工作表到字典
all_sheets = pd.read_excel(file_path, sheet_name=None)
# 方法2:分别读取每个工作表(更灵活)
xl = pd.ExcelFile(file_path)
sheet_dict = {}
for sheet_name in xl.sheet_names:
df = xl.parse(sheet_name)
sheet_dict[sheet_name] = df
return sheet_dict
# 2. 跨工作表汇总
def summarize_sales_pandas(file_path):
"""使用pandas进行数据汇总"""
# 读取所有工作表
all_sheets = pd.read_excel(file_path, sheet_name=None)
summary_list = []
for sheet_name, df in all_sheets.items():
# 数据清洗:删除空值
df_clean = df.dropna(subset=['销售额', '数量'])
# 计算统计指标
total_sales = df_clean['销售额'].sum()
total_quantity = df_clean['数量'].sum()
avg_price = total_sales / total_quantity if total_quantity > 0 else 0
# 更多统计分析
stats = {
'城市': sheet_name,
'销售总额': total_sales,
'总数量': total_quantity,
'平均单价': avg_price,
'最大单笔销售': df_clean['销售额'].max(),
'最小单笔销售': df_clean['销售额'].min(),
'销售笔数': len(df_clean),
'平均每单数量': df_clean['数量'].mean()
}
summary_list.append(stats)
# 创建汇总DataFrame
summary_df = pd.DataFrame(summary_list)
# 计算总计
totals = pd.DataFrame([{
'城市': '总计',
'销售总额': summary_df['销售总额'].sum(),
'总数量': summary_df['总数量'].sum(),
'平均单价': summary_df['销售总额'].sum() / summary_df['总数量'].sum(),
'销售笔数': summary_df['销售笔数'].sum()
}])
return pd.concat([summary_df, totals], ignore_index=True)
# 3. 数据透视和复杂分析
def advanced_analysis_pandas(file_path):
"""使用pandas进行高级分析"""
# 合并所有工作表数据
all_sheets = pd.read_excel(file_path, sheet_name=None)
# 添加城市列并合并
combined_data = []
for city, df in all_sheets.items():
df['城市'] = city
combined_data.append(df)
combined_df = pd.concat(combined_data, ignore_index=True)
# 数据透视表:按产品和城市汇总
pivot_table = pd.pivot_table(
combined_df,
values=['销售额', '数量'],
index=['产品'],
columns=['城市'],
aggfunc={'销售额': 'sum', '数量': 'sum'},
fill_value=0,
margins=True, # 添加总计
margins_name='总计'
)
# 分组分析
grouped = combined_df.groupby(['城市', '产品']).agg({
'销售额': ['sum', 'mean', 'count'],
'数量': 'sum'
}).round(2)
# 时间序列分析(假设有日期列)
if '日期' in combined_df.columns:
combined_df['日期'] = pd.to_datetime(combined_df['日期'])
combined_df['月份'] = combined_df['日期'].dt.to_period('M')
monthly_sales = combined_df.groupby(['月份', '城市'])['销售额'].sum().unstack()
return {
'combined_data': combined_df,
'pivot_table': pivot_table,
'grouped_analysis': grouped,
'monthly_sales': monthly_sales if '日期' in combined_df.columns else None
}
# 4. 输出到Excel(保留格式)
def export_to_excel_with_format_pandas(file_path, output_path):
"""使用pandas处理,openpyxl美化输出"""
# 使用pandas进行数据处理
summary_df = summarize_sales_pandas(file_path)
analysis_results = advanced_analysis_pandas(file_path)
# 创建Excel写入器
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
# 写入汇总数据
summary_df.to_excel(writer, sheet_name='汇总', index=False)
# 写入透视表
analysis_results['pivot_table'].to_excel(writer, sheet_name='数据透视')
# 写入详细分析
analysis_results['grouped_analysis'].to_excel(writer, sheet_name='分组分析')
if analysis_results['monthly_sales'] is not None:
analysis_results['monthly_sales'].to_excel(writer, sheet_name='月度趋势')
# 获取workbook对象进行格式设置
workbook = writer.book
# 设置汇总表格式
summary_sheet = workbook['汇总']
# 设置标题格式
header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
header_font = Font(bold=True, color="FFFFFF")
for cell in summary_sheet[1]: # 第一行
cell.fill = header_fill
cell.font = header_font
# 设置数字格式
for row in summary_sheet.iter_rows(min_row=2, max_col=4):
for cell in row[1:4]: # 数值列
cell.number_format = '#,##0.00'
# 自动调整列宽
for column in summary_sheet.columns:
max_length = 0
column_letter = column[0].column_letter
for cell in column:
try:
cell_value = str(cell.value) if cell.value else ""
max_length = max(max_length, len(cell_value))
except:
pass
adjusted_width = min(max_length + 2, 50)
summary_sheet.column_dimensions[column_letter].width = adjusted_width
print(f"处理完成,保存到: {output_path}")
5. 实战对比表
| 特性 |
openpyxl |
pandas |
|---|
| 读取性能 |
中等,适合逐行处理 |
优秀,批量读取快 |
| 内存使用 |
较低,支持只读模式 |
较高,全量加载到内存 |
| 写入功能 |
强大,支持单元格级控制 |
良好,支持DataFrame输出 |
| 格式控制 |
非常精细,单元格级控制 |
有限,依赖openpyxl引擎 |
| 数据处理 |
基础,需要手动实现 |
强大,内置丰富功能 |
| 公式支持 |
支持读取和写入公式 |
有限支持 |
| 图表支持 |
支持创建图表 |
不支持 |
| 学习曲线 |
较平缓 |
较陡峭(需pandas知识) |
6. 综合对比示例
def comprehensive_comparison(input_file):
"""
综合对比两种方法的优势
"""
print("=" * 60)
print("Excel处理方案对比")
print("=" * 60)
# 场景1:快速查看数据
print("\n1. 快速查看数据:")
print(" pandas方案:")
sheets_pandas = read_excel_pandas(input_file)
for name, df in sheets_pandas.items():
print(f" {name}: {len(df)} 行 × {len(df.columns)} 列")
# 场景2:数据汇总
print("\n2. 数据汇总分析:")
print(" openpyxl汇总结果(基础):")
openpyxl_summary = summarize_sales_openpyxl(input_file)
for city, data in openpyxl_summary.items():
print(f" {city}: 销售额={data['total_sales']:,.2f}")
print("\n pandas汇总结果(高级):")
pandas_summary = summarize_sales_pandas(input_file)
print(pandas_summary.to_string())
# 场景3:复杂分析
print("\n3. 复杂分析能力:")
print(" pandas可轻松完成:")
analysis = advanced_analysis_pandas(input_file)
print(" - 数据透视表")
print(" - 分组聚合")
print(" - 时间序列分析")
# 性能测试
import time
print("\n4. 性能测试:")
# openpyxl读取测试
start = time.time()
for _ in range(10):
read_excel_openpyxl(input_file)
openpyxl_time = time.time() - start
# pandas读取测试
start = time.time()
for _ in range(10):
read_excel_pandas(input_file)
pandas_time = time.time() - start
print(f" 读取性能:")
print(f" openpyxl: {openpyxl_time:.2f} 秒")
print(f" pandas: {pandas_time:.2f} 秒")
print(f" pandas比openpyxl快 {openpyxl_time/pandas_time:.1f} 倍")
# 使用示例
if __name__ == "__main__":
# 创建测试数据
from create_sample_data import create_sample_excel
create_sample_excel("sales_data.xlsx")
# 对比测试
comprehensive_comparison("sales_data.xlsx")
# 使用openpyxl处理
clean_and_format_openpyxl("sales_data.xlsx", "output_openpyxl.xlsx")
# 使用pandas处理
export_to_excel_with_format_pandas("sales_data.xlsx", "output_pandas.xlsx")
7. 创建示例数据的辅助函数
# create_sample_data.py
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
def create_sample_excel(filepath):
"""创建示例Excel文件"""
cities = ['北京', '上海', '广州']
products = ['产品A', '产品B', '产品C', '产品D']
with pd.ExcelWriter(filepath, engine='openpyxl') as writer:
for city in cities:
# 生成模拟数据
dates = [datetime(2024, 1, 1) + timedelta(days=i) for i in range(30)]
data = {
'日期': np.random.choice(dates, 100),
'产品': np.random.choice(products, 100),
'销售额': np.random.uniform(100, 10000, 100).round(2),
'数量': np.random.randint(1, 100, 100),
'客户类型': np.random.choice(['新客户', '老客户'], 100)
}
df = pd.DataFrame(data)
df.to_excel(writer, sheet_name=city, index=False)
print(f"示例文件已创建: {filepath}")
8. 选择建议
使用openpyxl的场景:
需要精细控制格式(字体、颜色、边框、合并单元格等)
需要操作Excel公式和图表
文件非常大,需要流式读取处理
只需要读取部分单元格,而非整个工作表
使用pandas的场景:
需要进行复杂的数据分析和转换
需要数据透视、分组聚合等操作
需要合并多个Excel文件或工作表
需要与其它数据源(数据库、CSV等)整合处理
最佳实践:结合使用
def hybrid_solution(input_file, output_file):
"""
结合openpyxl和pandas的最佳实践
"""
# 1. 使用pandas进行数据处理
all_data = pd.read_excel(input_file, sheet_name=None)
# 数据处理逻辑
processed_data = {}
for sheet_name, df in all_data.items():
# pandas数据清洗和转换
df_clean = df.dropna()
df_clean['利润率'] = (df_clean['销售额'] * 0.2) # 示例计算
processed_data[sheet_name] = df_clean
# 2. 使用openpyxl进行格式设置
from openpyxl import Workbook
from openpyxl.utils.dataframe import dataframe_to_rows
wb = Workbook()
for sheet_name, df in processed_data.items():
ws = wb.create_sheet(title=sheet_name)
# 将DataFrame写入工作表
for r_idx, row in enumerate(dataframe_to_rows(df, index=False, header=True), 1):
for c_idx, value in enumerate(row, 1):
ws.cell(row=r_idx, column=c_idx, value=value)
# openpyxl格式设置
from openpyxl.styles import Font, Alignment
# 设置标题格式
for cell in ws[1]:
cell.font = Font(bold=True, color="FFFFFF")
# 设置数字格式
for row in ws.iter_rows(min_row=2):
for cell in row[2:4]: # 数值列
cell.number_format = '#,##0.00'
# 删除默认创建的空工作表
if 'Sheet' in wb.sheetnames:
std = wb['Sheet']
wb.remove(std)
wb.save(output_file)
return output_file
总结
openpyxl更适合:需要对Excel文件进行精细控制、格式设置、公式操作等场景
pandas更适合:需要进行数据清洗、转换、分析和复杂计算的场景
结合使用:先用pandas处理数据,再用openpyxl设置格式,发挥各自优势
根据具体需求选择合适的工具,或者结合两者优势,可以高效地处理Excel多工作表任务。