Asyncio熊猫与就地

时间:2018-09-14 23:58:47

标签: python python-3.x pandas python-asyncio

我只是read this introduction,但是在实现任何一个示例时都遇到了麻烦(注释的代码是第二个示例):

import asyncio
import pandas as pd
from openpyxl import load_workbook

async def loop_dfs(dfs):
    async def clean_df(df):
        df.drop(["column_1"], axis=1, inplace=True)
        ... a bunch of other inplace=True functions ...
        return "Done"

    # tasks = [clean_df(df) for (table, dfs) in dfs.items()]
    # await asyncio.gather(*tasks)

    tasks = [clean_df(df) for (table, df) in dfs.items()]
    completed, pending = await asyncio.wait(tasks)


def main():
    dfs = {
        sn: pd.read_excel("excel.xlsx", sheet_name=sn)
        for sn in load_workbook("excel.xlsx").sheetnames
    }

    # loop = asyncio.get_event_loop()
    # loop.run_until_complete(loop_dfs(dfs))

    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(loop_dfs(dfs))
    finally:
        loop.close()

main()

我还看到了其他几篇有关熊猫如何不支持asyncio的文章,也许我只是想念一幅大图,但是如果我执行就地操作就没关系了吗? I saw recommendations for Dask但由于没有立即支持阅读excel的功能,因此我想先尝试一下,但我不断获得

RuntimeError: Event loop already running

1 个答案:

答案 0 :(得分:2)

  

我还看到了其他几篇有关熊猫如何不支持asyncio的文章,也许我只是想念一幅大图,但是如果我正确执行就地操作也没关系吗?

就地操作是指那些modify existing data。那是效率问题,而您的目标似乎是并行化,这是完全不同的问题。

Pandas不支持asyncio不仅是因为尚未实现,而且还因为Pandas通常不执行asyncio支持的那种操作:网络和子进程IO。熊猫函数要么使用CPU要么等待磁盘访问,但这两种都不适合异步。 Asyncio允许使用像普通同步代码一样看起来的协程来表达网络通信。在协程内部,await执行了每个阻塞操作(例如,网络读取),如果数据尚不可用,该操作将自动暂停整个任务。在每次暂停时,系统都会切换到下一个任务,从而有效地创建了一个协作的多任务系统。

当尝试调用不支持asyncio的库(例如pandas)时,表面上看起来会起作用,但是您不会获得任何好处,并且代码将串行运行。例如:

async def loop_dfs(dfs):
    async def clean_df(df):
        ...    
    tasks = [clean_df(df) for (table, df) in dfs.items()]
    completed, pending = await asyncio.wait(tasks)

由于clean_df不包含await的单个实例,因此它仅是名称上的协程,它实际上不会暂停执行以允许其他协程运行。因此await asyncio.wait(tasks)将按顺序运行任务,就像您写的一样:

for table, df in dfs.items():
    clean_df(df)

要使事情并行运行(提供的熊猫偶尔会在其运行期间释放GIL),您应该将各个CPU绑定的函数移交给线程池:

async def loop_dfs(dfs):
    def clean_df(df):  # note: ordinary def
        ...
    loop = asyncio.get_event_loop(0
    tasks = [loop.run_in_executor(clean_df, df)
             for (table, df) in dfs.items()]
    completed, pending = await asyncio.wait(tasks)

如果您沿着那条路线走,则首先不需要asyncio,只需使用concurrent.futures。例如:

def loop_dfs(dfs):  # note: ordinary def
    def clean_df(df):
        ...
    with concurrent.futures.ThreadPoolExecutor() as executor:
        futures = [executor.submit(clean_df, df)
                   for (table, df) in dfs.items()]
        concurrent.futures.wait(futures)
  

我想先尝试一下,但我不断收到RuntimeError: Event loop already running

该错误通常表示您已在已经为您运行asyncio的环境(例如jupyter笔记本)中启动了脚本。如果是这种情况,请确保您使用库存python运行脚本,或者查阅笔记本的文档,了解如何更改代码以将协程提交到已经运行的事件循环。