簡體   English   中英

在鍵值上匹配 2 個字典列表的最快方法

[英]Fastest way to match 2 lists of dicts on a key value

我有一個腳本,它從 CSV (~2.5m) 檢索用戶數據並通過 API (~2m) 從 Salesforce 記錄數據,並根據唯一的user_id匹配它們。

對於每個用戶,我需要相關的record_id (如果存在) 用戶和記錄是一對一的關系,所以user_id應該只出現在 1 條記錄上。

為了嘗試提高性能,兩個列表都按user_id升序排序,如果record['user_id'] > user['user_id']則我打破循環,因為這意味着沒有相關記錄。

它正在工作,但是在嘗試匹配大約 1.5 小時的 2 個數據集時速度很慢。 是否有更快的方法來執行匹配以檢索相關的record_id

以下是數據、當前函數和預期結果的示例:

users = [
    {"user_id": 11111, "name": "Customer A", "age": 34, 'record_id': None},
    {"user_id": 22222, "name": "Customer B", "age": 18, 'record_id': None},
    {"user_id": 33333, "name": "Customer C", "age": 66, 'record_id': None}
]

records = [
    {"user_id": 11111, "record_id": "ABC123"},
    {"user_id": 33333, "record_id": "GHI789"}
]

upload = []
for user in users:
    for record in records:
        if user['user_id'] == record['user_id']:
            user['record_id'] = record['record_id']
            records.remove(record)
            break
        elif record['user_id'] > user['user_id']:
            break
    if user['record_id']:
        upload.append(user)

print(upload)

這輸出:

[
 {'user_id': 11111, 'name': 'Customer A', 'age': 34, 'record_id': 'ABC123'}, 
 {'user_id': 33333, 'name': 'Customer C', 'age': 66, 'record_id': 'GHI789'}
]

創建一個字典,將用戶的 id 映射到其對應的字典。 然后,您可以使用for循環添加相關的record_id字段。 最后,您可以使用列表推導刪除沒有指定record_id的條目。

這不需要任何預處理(例如排序)來獲得加速; 效率提升來自於在大字典中查找比搜索大列表更快的事實:

user_id_mapping = {entry["user_id"]: entry for entry in users}

for record in records:
    if record["user_id"] in user_id_mapping:
        user_id_mapping[record["user_id"]]["record_id"] = record["record_id"]

result = [item for item in user_id_mapping.values() if item["record_id"] is not None]

print(result)

這輸出:

[
 {'user_id': 11111, 'name': 'Customer A', 'age': 34, 'record_id': 'ABC123'}, 
 {'user_id': 33333, 'name': 'Customer C', 'age': 66, 'record_id': 'GHI789'}
]

話雖如此,如果您必須重復執行類似的操作,我建議您使用某種數據庫而不是在 Python 中執行此操作。

您可以使用pandas.read_csv()將 CSV 數據讀入數據框,然后merge其與user_id值上的records合並:

import pandas as pd

users = pd.read_csv('csv file')
records = pd.DataFrame('result of salesforce query')

result = users.drop('record_id', axis=1).merge(records, on='user_id')

如果要保留records中沒有匹配值的users ,請將合並更改為

merge(records, on='user_id', how='left')

要將結果輸出為字典列表,請使用to_dict()

result.to_dict('records')

注意 - 可以將您的 Salesforce 查詢直接執行到數據框中。 例如看這個問答

你的做法不無道理。 但是在使用后刪除record是有代價的。 提前對兩個列表進行排序也是有代價的。 這些成本加起來可能比你想象的要多。

一種可能的方法是不對列表進行排序,而是構建一個record_ids的字典,例如:

rdict = { r['user_id']:r['record_id'] for r in records }
for user in users:
    user_id = user['user_id']
    record_id = rdict.get(user_id)
    if record_id:
        user['record_id'] = record_id
        upload.append(user)

這樣,您只需為構建哈希付出一次代價,其他一切都非常有效。

為了可擴展性,您可以使用 pandas 數據框,如下所示:

result = pd.merge(pd.DataFrame(users), pd.DataFrame(records), on='user_id').to_dict('records')

如果要保留沒有record_id的條目,可以將how="left"添加到merge函數的參數中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM