在键值上匹配 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'] 我会中断循环,因为这意味着没有相关记录。

它正在运行,但是在尝试匹配 2 个数据集时速度很慢,耗时约 1.5 小时。是否有更快的方法来执行匹配以检索相关 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 中执行此操作。

你的做法不无道理。但是在使用后删除 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 函数的参数中。

您可以使用 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 查询。例如参见 [​​=22=]