【攻略】教你如何一步步将 Pandas循环提速7万多倍!

量子位 报道 | 公众号 QbitAI

用Python和Pandas进行数据分析,就算是较小的DataFrame,使用标准循环也比较耗时。遇到较大的DataFrame时,需要的时间会更长。

现在,一位名叫Benedikt Droste的德国的数据分析师给出了一个解决方案:使用Numpy向量化,与使用标准循环相比,速度提升了71803倍。

【攻略】教你如何一步步将 Pandas循环提速7万多倍!_第1张图片

标准循环处理3年足球赛数据:20.7秒

Benedikt Droste的提供的示例是一个包含65列和1140行的Dataframe,包含了2016-2019赛季的足球赛结果。

需要解决的问题是:创建一个新的列,用于指示某个特定的队是否打了平局。可以这样开始:

def soc_loop(leaguedf,TEAM,):
    leaguedf[ Draws ] = 99999
    for row in range(0, len(leaguedf)):
        if ((leaguedf[ HomeTeam ].iloc[row] == TEAM) & (leaguedf[ FTR ].iloc[row] ==  D )) | 
            ((leaguedf[ AwayTeam ].iloc[row] == TEAM) & (leaguedf[ FTR ].iloc[row] ==  D )):
            leaguedf[ Draws ].iloc[row] =  Draw
        elif ((leaguedf[ HomeTeam ].iloc[row] == TEAM) & (leaguedf[ FTR ].iloc[row] !=  D )) | 
            ((leaguedf[ AwayTeam ].iloc[row] == TEAM) & (leaguedf[ FTR ].iloc[row] !=  D )):
            leaguedf[ Draws ].iloc[row] =  No_Draw
        else:
            leaguedf[ Draws ].iloc[row] =  No_Game

在这个案例中是阿森纳,在实现目标之前要确认阿森纳参加了哪些场比赛,是主队还是客队。但使用标准循环非常慢,执行时间为20.7秒。

方案一

Pandas 内置函数: iterrows ()ー快321倍

在第一个示例中,循环遍历了整个DataFrame。iterrows()为每一行返回一个Series,它以索引对的形式遍历DataFrame,以Series的形式遍历感兴趣的列。这使得它比标准循环更快:

def soc_iter(TEAM,home,away,ftr):
    #team, row[ HomeTeam ], row[ AwayTeam ], row[ FTR ]
    if [((home == TEAM) & (ftr ==  D )) | ((away == TEAM) & (ftr ==  D ))]:
        result =  Draw
    elif [((home == TEAM) & (ftr !=  D )) | ((away == TEAM) & (ftr !=  D ))]:
        result =  No_Draw
    else:
        result =  No_Game
    return result

【攻略】教你如何一步步将 Pandas循环提速7万多倍!_第2张图片

代码运行时间为68毫秒,比标准循环快321倍。但是,许多人建议不要使用它,因为仍然有更快的选项,而且iterrows()不能跨行保存dtype。这意味着,如果你在DataFrame dtypes上使用iterrows(),可以更改它,但这会导致很多问题。一定要保存dtypes的话,你还可以使用itertuples()。

方案二

apply ()方法ー快811倍

apply 本身并不快,但与DataFrame结合使用时,取决于 apply 表达式的内容。如果可以在 Cython 空间中执行,那么apply要快得多,这里的示例就是这种情况。

在本文的示例中,想要执行按列操作,要使用 axis 1:

这段代码甚至比之前的方法更快,完成时间为27毫秒。

方案三

Pandas向量化—快9280倍

此外,也可以利用向量化的优点来创建非常快的代码。重点是避免像之前的示例中的Python级循环,并使用优化后的C语言代码,这将更有效地使用内存。只需要稍微修改一下函数:

def soc_iter(TEAM,home,away,ftr):
    df[ Draws ] =  No_Game
    df.loc[((home == TEAM) & (ftr ==  D )) | ((away == TEAM) & (ftr ==  D )),  Draws ] =  Draw
    df.loc[((home == TEAM) & (ftr !=  D )) | ((away == TEAM) & (ftr !=  D )),  Draws ] =  No_Draw

现在,可以用 Pandas 列作为输入创建新列:

在这种情况下,甚至不需要循环。所要做的就是调整函数的内容。现可以直接将Pandas 列传递给函数,从而获得巨大的速度增益。

方案四

Numpy向量化—快71803倍

在上面的示例中,将将Pandas 列传递给函数。通过添加.values,可以得到一个Numpy数组:

因为引用了局部性的好处,Numpy数组的速度非常快,代码运行时间仅为0.305毫秒,比一开始使用的标准循环快71803倍。

在对上述五种方法进行比较之后,哪个更快一目了然:

【攻略】教你如何一步步将 Pandas循环提速7万多倍!_第3张图片

从这个图中,可以得出两个结论:

  • 如果要使用循环,则应始终选择apply方法。

  • 否则,使用向量化是最好的,因为它更快!

原文链接:
https://towardsdatascience.com/how-to-make-your-pandas-loop-71-803-times-faster-805030df4f06

【攻略】教你如何一步步将 Pandas循环提速7万多倍!_第4张图片

推荐阅读
为了追到小姐姐,我用 Python 制作了一个机器人
青出于蓝而胜于蓝,这是一款脱胎于Jupyter Notebook的新型编程环境
【中文教程】简单粗暴入门TensorFlow 2.0 | 北大学霸出品
写 Python 到底用什么编辑器好?鹅厂程序猿吵翻了



你可能感兴趣的:(【攻略】教你如何一步步将 Pandas循环提速7万多倍!)