【问题标题】:Quickly update django model objects from pandas dataframe从 pandas 数据框中快速更新 django 模型对象
【发布时间】:2020-09-28 21:17:53
【问题描述】:

我有一个记录交易的 Django 模型。我只需要更新一些交易的一些字段(两个)。

为了更新,要求用户提供额外数据,我使用 pandas 使用这些额外数据进行计算。

我使用 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'])

这大约将所需时间减半。

如何进一步提高性能?

【问题讨论】:

    标签: django-models django-views celery-task django-3.0


    【解决方案1】:

    我有点面临同样的问题(几乎相同数量的记录 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())
    

    结论:

    • 就我而言,最耗时的是批量更新,因此重新创建对象为我解决了这个问题(从使用 bulk_update 的 19 秒到使用 delete + bulk_create 的 10 秒)
    • 就您而言,使用我的方法可能会缩短所有其他操作的时间。

    【讨论】:

      【解决方案2】:

      我一直在寻找这个问题的答案,但没有找到任何权威、惯用的解决方案。所以,这就是我决定自己使用的东西:

      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[] 是如何实现的,但是如果它需要为每次使用搜索整个DataFrame 而不是仅仅进行哈希查找,它可能会很慢。出于这个原因,并且只是通过执行单个迭代循环来做简单的事情,我将所有模型实例拉入df,然后使用 Stackoverflow 答案中的recommendation 对 DataFrames 进行迭代以遍历感兴趣的压缩列.

      我查看了 Django 中select_for_update 的文档,我看不出它提供了性能改进,但您可能正在使用它来锁定事务并以原子方式进行所有更改。根据文档,bulk_update 应该比单独保存每个对象更快。

      就我而言,我只更新了 3500 个项目。我对各个步骤做了一些时间安排,得出了以下结论:

      • 3.05 秒查询和构建 DataFrame
      • 2.79 毫秒将实例加入df
      • 5.79 毫秒运行 for 循环并更新实例
      • 1.21 秒即可批量更新更改

      所以,我认为您需要分析您的代码以了解实际需要时间,但这可能是 Django 问题而不是 Pandas 问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-06
        • 1970-01-01
        • 1970-01-01
        • 2016-12-18
        相关资源
        最近更新 更多