Python openpyxl 实战:智能车竞赛 8 大赛区报名数据自动合并与统计
·
Python openpyxl 实战:智能车竞赛 8 大赛区报名数据自动合并与统计
全国大学生智能车竞赛作为一项面向高校学生的科技赛事,每年吸引数千支队伍参与。面对来自不同赛区、不同组别的海量报名数据,如何高效完成数据清洗、合并与统计分析,成为赛事组织者和参赛学校技术负责人的共同挑战。本文将基于Python生态中强大的openpyxl库,构建一套完整的自动化数据处理解决方案。
1. 数据预处理与工程架构设计
在开始编写代码前,我们需要明确数据处理的整体流程。典型的智能车竞赛报名数据包含两个核心Excel文件:队伍报名信息表和队员个人信息表。前者记录团队基础信息、参赛组别和指导老师等数据,后者则包含每个队员的详细资料。
关键数据结构分析:
# 典型数据结构示例
team_data_structure = {
"团队编号": "A2023-001",
"团队名称": "极速追风队",
"所选赛题": "摄像头三轮",
"所属赛区": "华东赛区",
"学生信息": [
{"姓名": "张三", "身份证号": "310***19990101****"},
# 最多5名学生...
],
"指导教师": [
{"姓名": "李教授", "手机号": "138****1234", "邮箱": "li@univ.edu"}
]
}
工程目录结构建议:
smartcar_data_processor/
├── config/ # 配置文件
│ ├── regions.json # 8大赛区定义
│ └── categories.json # 竞赛组别定义
├── input/ # 原始数据
│ ├── team_data.xlsx
│ └── member_data.xlsx
├── output/ # 处理结果
├── utils/ # 工具函数
│ ├── data_cleaner.py
│ └── report_generator.py
└── main.py # 主程序入口
提示:建议使用JSON文件管理赛区和组别等元数据,便于后期维护调整,避免硬编码。
2. 核心数据处理模块实现
2.1 多表格数据加载与校验
使用openpyxl加载Excel数据时,需要特别注意数据完整性和格式校验:
from openpyxl import load_workbook
from openpyxl.utils import get_column_letter
def load_and_validate(file_path):
try:
wb = load_workbook(filename=file_path, data_only=True)
ws = wb.active
# 表头验证
expected_headers = ["团队编号", "团队名称", "..."] # 完整列名
actual_headers = [cell.value for cell in ws[1]]
if actual_headers != expected_headers:
raise ValueError("表格列名不匹配,请检查模板版本")
# 数据质量检查
for row in ws.iter_rows(min_row=2, values_only=True):
if not row[0]: # 团队编号为空
print(f"警告:第{row}行数据不完整")
return ws
except Exception as e:
print(f"文件加载失败:{str(e)}")
raise
常见数据问题处理策略:
| 问题类型 | 处理方案 | 技术实现 |
|---|---|---|
| 空值字段 | 自动填充默认值 | ws.cell(row, column, value="N/A") |
| 格式错误 | 正则表达式校验 | re.match(r'^\d{11}$', phone) |
| 重复数据 | 哈希值比对 | hashlib.md5(str(row).encode()).hexdigest() |
| 赛区不符 | 映射表校正 | region_mapping.get(raw_region) |
2.2 数据关联与合并算法
队员信息与团队数据的关联是处理的核心难点。我们采用身份证号作为唯一键进行匹配:
def build_member_index(member_sheet):
"""构建队员信息索引字典"""
index = {}
for row in member_sheet.iter_rows(min_row=2, values_only=True):
if not row[4]: # 身份证号为空
continue
index[row[4]] = {
"name": row[2],
"phone": row[3],
"university": row[8]
}
return index
def merge_data(team_sheet, member_index):
"""合并团队与队员数据"""
merged = []
for row in team_sheet.iter_rows(min_row=2, values_only=True):
team = extract_team_info(row)
# 关联队员信息
for i in range(5): # 最多5名队员
id_card = row[6 + i*2] # 身份证号位置
if id_card and id_card in member_index:
team["members"].append(member_index[id_card])
merged.append(team)
return merged
性能优化技巧:
- 使用字典哈希替代线性查找
- 批量写入代替单单元格操作
- 启用openpyxl的只读模式处理大文件
3. 赛区统计分析与报表生成
3.1 多维度数据统计
基于合并后的数据,我们可以实现多种维度的统计分析:
def analyze_by_region(merged_data):
stats = {
"total_teams": len(merged_data),
"regions": defaultdict(lambda: {
"count": 0,
"categories": defaultdict(int),
"universities": set()
})
}
for team in merged_data:
region = team["region"]
stats["regions"][region]["count"] += 1
stats["regions"][region]["categories"][team["category"]] += 1
stats["regions"][region]["universities"].add(team["university"])
return stats
典型统计报表输出:
| 赛区 | 队伍数 | 高校数 | 热门组别 |
|---|---|---|---|
| 华东 | 387 | 58 | 摄像头三轮(112) |
| 华北 | 265 | 42 | 电能接力(89) |
| 东北 | 204 | 35 | 负压电磁(76) |
3.2 自动化报告生成
利用openpyxl的丰富样式设置功能,可以生成专业级的统计报告:
from openpyxl.styles import Font, Alignment, Border, Side
def generate_report(stats, output_path):
wb = Workbook()
ws = wb.active
# 设置标题样式
title_font = Font(name='微软雅黑', size=14, bold=True)
header_fill = PatternFill(start_color='FFD700', fill_type='solid')
# 写入数据
ws.append(["赛区", "队伍数量", "参与高校", "组别分布"])
for region, data in stats["regions"].items():
categories = ", ".join([f"{k}({v})" for k,v in data["categories"].items()])
ws.append([region, data["count"], len(data["universities"]), categories])
# 应用样式
for row in ws.iter_rows():
for cell in row:
cell.border = Border(left=Side(style='thin'), right=Side(style='thin'))
if cell.row == 1:
cell.font = title_font
cell.fill = header_fill
wb.save(output_path)
4. 异常处理与日志系统
健壮的数据处理系统需要完善的错误处理机制:
import logging
from datetime import datetime
logging.basicConfig(
filename=f'process_{datetime.now().strftime("%Y%m%d")}.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
class DataProcessor:
def __init__(self):
self.error_count = 0
def process_row(self, row):
try:
# 数据处理逻辑...
logging.info(f"成功处理团队:{row[0]}")
except Exception as e:
self.error_count += 1
logging.error(f"处理失败:{row[0]} - {str(e)}")
if self.error_count > 10:
logging.critical("错误次数超过阈值,终止处理")
raise
错误处理策略对比:
| 错误级别 | 处理方式 | 恢复措施 |
|---|---|---|
| 数据格式错误 | 记录并跳过 | 使用默认值继续 |
| 逻辑错误 | 记录并终止当前任务 | 回滚当前操作 |
| 系统错误 | 立即终止程序 | 发送警报通知 |
实际项目中,我们通过单元测试确保核心模块的可靠性:
import unittest
class TestDataMerge(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.test_data = create_test_data()
def test_member_matching(self):
result = merge_data(*self.test_data)
self.assertEqual(len(result[0]["members"]), 3)
if __name__ == '__main__':
unittest.main()
更多推荐
所有评论(0)