Python: Pandas - 嵌套循环需要很长时间才能完成。如何加快速度?

Posted

技术标签:

【中文标题】Python: Pandas - 嵌套循环需要很长时间才能完成。如何加快速度?【英文标题】:Python: Pandas - Nested Loop takes very long to complete. How to speed up? 【发布时间】:2021-08-15 17:20:56 【问题描述】:

我正在自学 Python 和 Pandas 以支持我的日常工作。经过大量试验和错误,我构建了以下功能。该函数将 (i) 引用为“数据集”的数据框、(ii) 国家名称列表和 (iii) 唯一法人实体 ID 列表作为参数。 (该功能有效。)

数据集是一个包含 300,000 多行和大约 30 列的大型数据框——它是总账的转储。关键列是“LE_ID”和“COUNTRY”,它们分别包含 (i) 相关法人实体的唯一 ID 和 (ii) 该法人实体所在国家/地区的名称。并非所有行都是唯一的,大约有 5000 个 LE_ID 填充了 300,000 多行。

我想将此数据集“拆分”为 XLS 文件,这些文件按国家、大纲、每个选项卡、每个 LE_ID 的分类帐详细信息。下面的函数实现了这一点。但是速度太快了——在我最近的笔记本电脑上完成需要 40 分钟。

功能:

def SplitDatasetByCountry(dataset, countries, ids):
        
    for country in countries:

        ## output
        output_folder = Path('/Users/XXXXXX/Desktop/TOOL/Reports')
        output_filename = 'Data__for_' + str(country) + '_.xlsx'
        output = output_folder / output_filename
    
        ## writer
        writer = pd.ExcelWriter(output)
        workbook = writer.book

        ## country logic
        x = dataset.loc[dataset['COUNTRY'] == country]
        
        ids_for_entities_in_country = x['LE_ID'].to_list()
        unique_ids = list(set(ids_for_entities_in_country))

        for id in ids:

            if id in unique_ids:
                y = x.loc[x['LE_ID'] == id]
                y.to_excel(writer, sheet_name=str(id))
                
            else:
                pass
        
        writer.save()
        workbook.close()

如果有任何建议可以加快速度,我将不胜感激。我认为我过度迭代,这导致了这个问题,但我不知道如何解决这个问题。昨天这个函数的版本有点快,但我最终得到了损坏的 XLS 文件——可能是因为我不小心让代码编写器多次访问同一个 xls 文件。

我从这里的社区了解到,列表推导是首选,但我无法找出正确的语法来组织我的函数。我宁愿继续迭代,但消除当前(看似?)冗余迭代。

感谢您的意见

5 月 28 日第一次更新:

数据集有以下字段

LE_ID               object
LEGAL_ENTITY_NAME   object
COUNTRY             object
GL_ACCOUNT                              object
BOOK_AMT                                int64
ADJUSTED_TAX                            float64

5 月 28 日第二次更新:

根据 cmets 中的建议修改代码,完美运行:

def SplitDatasetByCountry(dataset):

for country, country_df in dataset.groupby('COUNTRY'):
      
    ## output
    output_folder = Path('/Users/XXXX/Desktop/Reports')
    output_filename = 'Data_for_' + str(country) + '_.xlsx'
    output = output_folder / output_filename

    ## writer
    writer = pd.ExcelWriter(output)
    workbook = writer.book
    
    ## country logic            
    country_expenses = function_A(country_df)
    country_income = function_B(country_df)
    expense = country_expenses.groupby('LE_ID')['BOOK_AMT'].sum()
    income = country_income..groupby('LE_ID')['BOOK_AMT'].sum()
    expense.to_excel(writer, sheet_name='Country Expense')
    income.to_excel(writer, sheet_name='Country Income')
    
    for le_id, le_id_df in country_df.groupby('LE_ID'):
        le_id_df.to_excel(writer, sheet_name=str(le_id))
                    
    writer.save()
    workbook.close()

【问题讨论】:

【参考方案1】:

您能否在数据框进入您的功能之前分享它的格式?

如果该格式足够好,那么只需根据国家/地区拆分数据框,那么 id 就足够了:

list_of_dfs = [dataset[["COUNTRY"] == country] for country in dataset["COUNTRY"].unique()]

我不明白你想用 id 完成什么,但我至少可以指出,你可以像上面那样在 Series 上使用 .unique() 直接获得唯一性,参见 pandas' doc:

unique_ids = dataset["LE_ID"].unique()

【讨论】:

感谢您向我指出 .unique() 方法——这真的很有帮助。我已经用数据集的关键字段更新了我上面的帖子。你能看看——我相信格式可能“不够好”——正如你所写的——因为你提供的列表理解导致了一个关键错误(关键)。使用您建议的列表理解是否正确,项目“== country”引用“COUNTRY”列中的任何值,即不是对另一个变量的引用?【参考方案2】:

您需要先按国家分组,然后按 LED_ID 分组并将结果写入文件。

for country, country_df in dataset.groupby('COUNTRY'):
    ## output
    output_folder = Path('/Users/XXXXXX/Desktop/TOOL/Reports')
    output_filename = 'Data__for_' + str(country) + '_.xlsx'
    output = output_folder / output_filename
    ## writer
    with pd.ExcelWriter(output) as writer:
        for led_id, led_df in country_df.groupby("LED_ID"):
            led_df.to_excel(writer, sheet_name=str(led_id))

【讨论】:

感谢 Alexander 的建议,它完美无缺。而我的原始版本需要 40 多分钟才能完成(无疑是由于重复迭代),而您的版本在几分钟内完成。我需要进一步研究 groupby 方法,因为我不太明白发生了什么。感谢您为我指明正确的方向。在上面的原始帖子中上传了修改后的代码以供将来参考。请注意附加函数 A 和 B,它们在数据帧上发挥了一些单独的魔力,并且也被打印到它们自己的工作表上。【参考方案3】:

有两个术语需要理解。

矢量化和传统循环。

如果你使用 for 循环,甚至嵌套 for 循环。时间复杂度会增加 以 O(n^2) 的顺序。

但如果你使用向量化,你的算法可能会运行得更快。

尝试使用 numpy 求和或点函数来解决问题,而不是 for 循环,这样会更快。

This the example for implementing vectorization techniques

【讨论】:

以上是关于Python: Pandas - 嵌套循环需要很长时间才能完成。如何加快速度?的主要内容,如果未能解决你的问题,请参考以下文章

Python中使用pandas数据框和嵌套for循环的基于项目的协作过滤器的瓶颈

如何减少python中嵌套循环的时间

无法在嵌套循环中使用 pandas 附加更大的数据帧。如何更改为 numpy 向量化?

如何使用熊猫来加快这个嵌套循环的速度?

嵌套 for 循环的 Pandas 在创建的不同数据帧上插入多个数据

Python Pandas 嵌套 MongoDB