如何用openpyxl合并Excel中相同日期关联的多个ID
问题
我正在用openpyxl编写程序,读取Excel文件并生成新文件,将所有数据按正确顺序和格式整理。需求是从原文件的同一行中提取若干日期,在新文件里为每个日期生成一行,但当存在多个相同日期时,要把对应ID放在同一行。
日期和ID由另一个函数返回,格式如下:
l = [(ID1, date1), (ID2, date2), (ID3, date3)]
举个例子,如果输入是:
l = [(ID1, 20160101), (ID2, 20180101), (ID3, 20160101)]
期望输出的Excel行是:
- 第1行:20160101 - ID1, ID3
- 第2行:20180101 - ID2
我目前的代码会给每个日期(哪怕重复)都生成新行,代码如下:
def gestRid(fimport, fimpianto): for i in range(2, fimport.max_row + 1): if dateRid(fimpianto, i): for tupla in dateRid(fimpianto, i): cod, data = tupla ultima_riga = fimport.max_row + 1 # ultima_riga == last_row for j in range(1, fimport.max_column + 1): #Copies row at the end of worksheet fimport.cell(row = ultima_riga, column = j).value = fimport.cell(row = i, column = j).value fimport.cell(row = ultima_riga, column = 101).value = fimport.cell(row = i, column = 101).value + 1 fimport.cell(row = ultima_riga, column = 1).value = fimport.cell(row = ultima_riga - 1, column = 1).value + 1 fimport.cell(row = i, column = 46).value = (datetime.strptime(str(data), '%Y%m%d').date() - timedelta(days = 1)).strftime('%d/%m/%Y') fimport.cell(row = ultima_riga, column = 45).value = datetime.strptime(str(data), '%Y%m%d').strftime('%d/%m/%Y') fimport.cell(row = ultima_riga, column = 48).value = fimport.cell(row = ultima_riga, column = 45).value[6:] #Copies id in the first free cell if not fimport.cell(row = ultima_riga, column = 78).value: fimport.cell(row = ultima_riga, column = 78).value = cod elif not fimport.cell(row = ultima_riga, column = 82).value: fimport.cell(row = ultima_riga, column = 82).value = cod else: fimport.cell(row = ultima_riga, column = 86).value = cod
最优实现方法
核心思路是先按日期分组收集ID,再根据分组结果生成Excel行,避免重复创建行。
步骤1:按日期分组ID
用字典存储日期和对应的ID列表,遍历dateRid返回的元组列表,把相同日期的ID归到同一组:
from collections import defaultdict # 处理当前行的日期和ID,按日期分组 date_id_map = defaultdict(list) for tupla in dateRid(fimpianto, i): cod, data = tupla date_id_map[data].append(cod)
这样date_id_map的结构为{日期: [ID1, ID2,...]},示例中会生成{20160101: [ID1, ID3], 20180101: [ID2]}。
步骤2:根据分组生成Excel行
遍历分组后的字典,为每个日期生成一行,并把该日期下的所有ID填入对应单元格:
def gestRid(fimport, fimpianto): from collections import defaultdict from datetime import datetime, timedelta for i in range(2, fimport.max_row + 1): tupla_list = dateRid(fimpianto, i) if not tupla_list: continue # 按日期分组ID date_id_map = defaultdict(list) for tupla in tupla_list: cod, data = tupla date_id_map[data].append(cod) # 遍历分组,生成行 for data, cod_list in date_id_map.items(): ultima_riga = fimport.max_row + 1 # 复制原行数据到新行 for j in range(1, fimport.max_column + 1): fimport.cell(row=ultima_riga, column=j).value = fimport.cell(row=i, column=j).value # 更新行的特定字段 fimport.cell(row=ultima_riga, column=101).value = fimport.cell(row=i, column=101).value + 1 fimport.cell(row=ultima_riga, column=1).value = fimport.cell(row=ultima_riga - 1, column=1).value + 1 # 处理日期格式 date_obj = datetime.strptime(str(data), '%Y%m%d').date() fimport.cell(row=i, column=46).value = (date_obj - timedelta(days=1)).strftime('%d/%m/%Y') fimport.cell(row=ultima_riga, column=45).value = date_obj.strftime('%d/%m/%Y') fimport.cell(row=ultima_riga, column=48).value = date_obj.strftime('%Y') # 直接取年份,避免切片出错 # 把多个ID填入对应单元格 for idx, cod in enumerate(cod_list): if idx == 0: fimport.cell(row=ultima_riga, column=78).value = cod elif idx == 1: fimport.cell(row=ultima_riga, column=82).value = cod elif idx == 2: fimport.cell(row=ultima_riga, column=86).value = cod # 若ID超过3个,可扩展列或拼接成字符串 else: existing = fimport.cell(row=ultima_riga, column=78).value or "" fimport.cell(row=ultima_riga, column=78).value = f"{existing}, {cod}"
关键改进点
- 减少重复调用:将
dateRid(fimpianto, i)的结果存入变量,避免循环内重复调用提升性能。 - 分组逻辑替代逐元组处理:每个日期仅生成一行,彻底解决重复日期多生成行的问题。
- ID填充更清晰:遍历ID列表依次填充对应列,超过数量时可灵活处理(如字符串拼接)。
- 日期处理更可靠:用
strftime('%Y')直接获取年份,替代原代码中容易出错的切片操作。
内容的提问来源于stack exchange,提问作者Mattooo
相关产品推荐
相关产品推荐

