我有一个记录事务的Django模型。我只需要更新某些事务的某些字段(两个(。
为了更新,要求用户提供额外的数据,我使用熊猫来使用这些额外的数据进行计算。
我使用 pandas 脚本的输出来更新原始模型,如下所示:
for i in df.tnsx_uuid:
t = Transactions.objects.get(tnsx_uuid=i)
t.start_bal = df.loc[df.tnsx_uuid==i].start_bal.values[0]
t.end_bal = df.loc[df.tnsx_uuid==i].end_bal.values[0]
t.save()
这是非常缓慢的。最好的方法是什么?
更新: 经过更多的研究,我找到了bulk_update并将代码更改为:
transactions = Transactions.objects.select_for_update()
.filter(tnsx_uuid__in=list(df.tnsx_uuid)).only('start_bal', 'end_bal')
for t in transactions:
i = t.tnsx_uuid
t.start_bal = df.loc[df.tnsx_uuid==i].start_bal.values[0]
t.end_bal = df.loc[df.tnsx_uuid==i].end_bal.values[0]
Transactions.objects.bulk_update(transactions, ['start_bal', 'end_bal'])
这大约使所需时间减少了一半。
如何进一步提高性能?
我一直在寻找这个问题的答案,但没有找到任何权威的惯用解决方案。所以,这是我决定供自己使用的内容:
transaction = Transactions.objects.filter(tnsx_uuid__in=list(df.tnsx_uuid))
# Build a DataFrame of Django model instances
trans_df = pd.DataFrame([{'tnsx_uuid': t.tnsx_uuid, 'object': t} for t in transactions])
# Join the Django instances to the main DataFrame on the index
df = df.join(trans_df.set_index('tnsx_uuid'))
for obj, start_bal, end_bal in zip(df['object'], df['start_bal'], df['end_bal']):
obj.start_bal = start_bal
obj.end_bal = send_bal
Transactions.objects.bulk_update(df['object'], ['start_bal', 'end_bal'])
我不知道DataFrame.loc[]
是如何实现的,但如果它需要搜索每次使用的整个数据帧,而不仅仅是进行哈希查找,它可能会很慢。出于这个原因,并且只是通过执行单个迭代循环来简化事情,我将所有模型实例拉入df
然后使用 Stackoverflow 答案中的建议遍历数据帧来循环访问感兴趣的压缩列。
我查看了 Django 中 select_for_update 的文档,对我来说它是否提供了性能改进并不明显,但您可能正在使用它来锁定事务并以原子方式进行所有更改。根据文档,bulk_update
应该比单独保存每个对象更快。
就我而言,我只更新了 3500 个项目。我对各个步骤做了一些时间安排,并提出了以下内容:
- 3.05 秒查询和构建数据帧
- 2.79 毫秒将实例加入
df
- 5.79 毫秒运行
for
循环并更新实例 - 1.21 秒bulk_update更改
所以,我认为你需要分析你的代码,看看什么实际上需要时间,但它可能是Django问题而不是Pandas问题。
我有点面临同样的问题(几乎相同数量的记录 3500~(,我想补充一点:
- bulk_update的性能似乎比 bulk_create,在我的情况下,允许删除对象,所以 我删除了所有对象,然后重新创建它们,而不是bulk_updating。
- 我使用了与您相同的方法(感谢您的想法(,但进行了一些修改:
a( 我从查询本身创建数据帧:
all_objects_values = all_objects.values('id', 'date', 'amount')
self.df_values = pd.DataFrame.from_records(all_objects_values )
b( 然后我创建对象列而不迭代(我确保这些是有序的(:
self.df_values['object'] = list(all_objects)
c( 为了更新对象值(在我的数据帧中执行操作后(,我迭代行(不确定性能差异(:
for index, row in self.df_values.iterrows():
row['object'].amount= row['amount']
d( 最后,我重新创建所有对象:
MyModel.objects.bulk_create(self.df_values['object'].tolist())
结论:
- 就我而言,最耗时的是批量更新,因此重新创建对象为我解决了这个问题(从 19 秒的 bulk_update 秒到 delete + bulk_create的 10 秒(
- 在您的情况下,使用我的方法可能会缩短所有其他操作的时间。