在键值上匹配 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=]
我有一个脚本可以从 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=]