Python中基于键值匹配合并多源列表数据

Python中基于键值匹配合并多源列表数据

本教程详细探讨了如何在python中高效地将多个列表(其中包含字典对象)的数据进行合并。通过匹配特定键的值,我们将演示如何从源列表中提取所需信息,并将其整合到目标列表中,最终生成一个包含所有相关属性的统一数据结构。教程涵盖了直接循环迭代和基于字典预处理的优化方法,并提供了代码示例及性能考量。

1. 数据合并场景概述

在数据处理中,我们经常会遇到需要将分散在不同列表中的相关信息聚合到一起的场景。例如,你可能有一个主数据列表,以及多个辅助列表,这些辅助列表包含了主数据中某些字段的补充或“原始”值。我们的目标是根据共同的键(如name或address)将这些辅助信息合并到主数据项中。

假设我们有以下三组数据:

  • listA: 包含name及其对应的original_name。
  • listB: 包含address及其对应的original_address。
  • dataList: 主数据列表,包含id, created_at, name, address。
listA = [   {     "name": "name sample 1",     "original_name" : "original name sample 1",   },   {     "name": "name sample 2",     "original_name" : "original name sample 2",   } ]  listB = [   {     "address": "address sample 1",     "original_address" : "original address sample 1",   },   {     "address": "address sample 2",     "original_address" : "original address sample 2",   } ]  dataList = [   {     "id": "1",     "created_at": "date 1",     "name": "name sample 1",     "address": "address sample 1",   },   {     "id": "2",     "created_at": "date 2",     "name": "name sample 2",     "address": "address sample 2",   } ]

我们期望的最终结果finalList应该在dataList的每个字典中添加original_name和original_address字段,这些值分别来自listA和listB,通过匹配name和address键来获取。

finalList = [   {     "id": "1",     "created_at": "date 1",     "name": "name sample 1",     "original_name" : "original name sample 1",     "address": "address sample 1",     "original_address" : "original address sample 1",   },   {     "id": "2",     "created_at": "date 2",     "name": "name sample 2",     "original_name" : "original name sample 2",     "address": "address sample 2",     "original_address" : "original address sample 2",   } ]

2. 解决方案一:基于嵌套循环的直接合并

一种直观的方法是使用嵌套循环。首先,我们复制一份dataList以避免修改原始数据。然后,遍历listA和listB中的每个条目,并在finalList中查找匹配的项,找到后更新其属性。

立即学习Python免费学习笔记(深入)”;

from copy import deepcopy  listA = [   {"name": "name sample 1", "original_name" : "original name sample 1"},   {"name": "name sample 2", "original_name" : "original name sample 2"} ]  listB = [   {"address": "address sample 1", "original_address" : "original address sample 1"},   {"address": "address sample 2", "original_address" : "original address sample 2"} ]  dataList = [   {"id": "1", "created_at": "date 1", "name": "name sample 1", "address": "address sample 1"},   {"id": "2", "created_at": "date 2", "name": "name sample 2", "address": "address sample 2"} ]  finalList = deepcopy(dataList) # 使用 deepcopy 确保不影响原始 dataList  # 合并 listA 和 listB,以便一次性处理 # 这种方式会遍历 finalList 多次,效率较低,但逻辑直观 for entry in listA + listB:     if "name" in entry: # 处理来自 listA 的数据         for data_item in finalList:             if data_item.get('name') == entry['name']: # 使用 .get() 避免 KeyError                 data_item['original_name'] = entry['original_name']     elif "address" in entry: # 处理来自 listB 的数据         for data_item in finalList:             if data_item.get('address') == entry['address']:                 data_item['original_address'] = entry['original_address']  print("原始 dataList:", dataList) print("合并后的 finalList:", finalList)

代码解析:

  1. from copy import deepcopy: 导入deepcopy函数,用于创建dataList的完全独立副本。这是为了确保对finalList的修改不会影响到dataList。
  2. finalList = deepcopy(dataList): 初始化finalList。
  3. for entry in listA + listB:: 将listA和listB合并成一个临时列表进行迭代。
  4. if “name” in entry: 和 elif “address” in entry:: 根据entry中存在的键来判断它来自listA还是listB,并执行相应的匹配和赋值操作。
  5. 内层循环 for data_item in finalList:: 遍历finalList中的每个字典,查找匹配的name或address。
  6. data_item.get(‘name’) == entry[‘name’]: 使用.get()方法访问键,可以避免在键不存在时抛出KeyError,使代码更健壮。

注意事项: 这种方法在数据量较小时易于理解和实现。然而,其时间复杂度较高。如果dataList有N个元素,listA有M个元素,listB有P个元素,那么查找和更新original_name的操作是M * N,查找和更新original_address的操作是P * N。总时间复杂度近似为 O((M+P)*N),在大规模数据处理时效率低下。

3. 解决方案二:利用字典预处理优化合并效率

为了提高效率,特别是当listA、listB或dataList的数据量较大时,我们可以利用哈希表的O(1)平均查找时间特性。核心思想是将listA和listB预处理成字典(哈希表),以name和address作为键,方便快速查找对应的original_name和original_address。

from copy import deepcopy  listA = [   {"name": "name sample 1", "original_name" : "original name sample 1"},   {"name": "name sample 2", "original_name" : "original name sample 2"} ]  listB = [   {"address": "address sample 1", "original_address" : "original address sample 1"},   {"address": "address sample 2", "original_address" : "original address sample 2"} ]  dataList = [   {"id": "1", "created_at": "date 1", "name": "name sample 1", "address": "address sample 1"},   {"id": "2", "created_at": "date 2", "name": "name sample 2", "address": "address sample 2"} ]  # 1. 预处理 listA 和 listB 为字典,以便快速查找 name_map = {item['name']: item['original_name'] for item in listA} address_map = {item['address']: item['original_address'] for item in listB}  # 2. 创建 finalList 的副本 finalList = deepcopy(dataList)  # 3. 遍历 finalList,根据映射关系添加新字段 for data_item in finalList:     # 查找并添加 original_name     name_key = data_item.get('name')     if name_key in name_map:         data_item['original_name'] = name_map[name_key]     # else: 可以选择处理未找到匹配的情况,例如设置默认值或跳过      # 查找并添加 original_address     address_key = data_item.get('address')     if address_key in address_map:         data_item['original_address'] = address_map[address_key]     # else: 可以选择处理未找到匹配的情况  print("原始 dataList:", dataList) print("优化合并后的 finalList:", finalList)

代码解析:

  1. 预处理映射表:
    • name_map = {item[‘name’]: item[‘original_name’] for item in listA}: 使用字典推导式将listA转换为一个字典name_map,其中键是name,值是original_name。
    • address_map = {item[‘address’]: item[‘original_address’] for item in listB}: 同样地,将listB转换为address_map。
    • 这一步的时间复杂度分别为O(M)和O(P)。
  2. 遍历主列表并合并:
    • for data_item in finalList:: 只需遍历finalList一次。
    • name_key = data_item.get(‘name’): 获取当前数据项的name值。
    • if name_key in name_map:: 在name_map中进行O(1)查找。如果找到,则将对应的original_name添加到data_item中。
    • 对address进行类似操作。
    • 这一步的时间复杂度为O(N)。

性能优势: 这种优化方法的总时间复杂度为O(M + P + N),远优于嵌套循环的O((M+P)*N),尤其是在数据量大时,性能提升显著。

4. 进一步考虑与最佳实践

在实际应用中,除了上述两种方法,还需要考虑一些额外因素:

  • 键的唯一性: 上述优化方法假设listA和listB中用于匹配的键(name和address)是唯一的。如果存在重复键,字典推导式将只保留最后一个匹配项。如果需要处理非唯一键,可能需要将字典的值设为列表或其他数据结构来存储所有匹配项。
  • 缺失数据处理:
    • 如果dataList中的某个name或address在name_map或address_map中找不到匹配项,上述代码会跳过赋值。
    • 根据业务需求,你可能希望:
      • 为这些字段设置默认值(例如data_item[‘original_name’] = None)。
      • 记录下哪些数据项未能匹配。
      • 直接从finalList中移除未能完全匹配的项。
  • 内存使用:
    • deepcopy会创建原始列表的完整副本,这会增加内存消耗。如果原始dataList非常大且不需要保留,可以直接在dataList上进行修改,或者使用浅拷贝list(dataList)然后修改内部字典,但这需要更小心地管理引用。
    • 创建name_map和address_map也会占用额外内存,但通常是值得的,因为它们提供了显著的性能优势。
  • 代码可读性 优化后的方法虽然性能更好,但可能比简单的嵌套循环略微复杂。在选择方法时,应权衡性能需求和代码可读性。对于小规模数据,直观的嵌套循环可能更合适;对于大规模数据,预处理优化是首选。

5. 总结

本教程介绍了在Python中根据键值匹配合并多个列表数据字典的两种主要方法:

  1. 嵌套循环直接合并: 简单直观,适用于数据量较小的情况。其时间复杂度为O((M+P)*N)。
  2. 字典预处理优化合并: 通过将辅助列表转换为哈希表,显著提高查找效率,适用于大规模数据。其时间复杂度为O(M + P + N)。

在实际开发中,推荐使用字典预处理的优化方法,因为它在处理大量数据时能提供更好的性能。同时,务必考虑数据中键的唯一性、缺失数据处理以及内存消耗等因素,以构建健壮且高效的数据处理流程。

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享