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 分享