pandas apply lambda_如何将 Pandas 循环代码加速 7 万倍,附代码

(关注AI新视野,私信‘资料’二字,免费获取50G人工智能视频教程!)

f7805aacabe73bc7a5db48b101dbd5c5.png

如果您使用 Python 和 Pandas 进行数据分析,那么很快就会接触到循环。 然而,即使对于较小的 DataFames 来说,使用标准循环也是非常耗时的,对于较大的 DataFrames 来说,可能需要很长的时间。 当你第一次等待超过半个小时来执行代码时,那么本文是你所需要的。

标准循环

Datatrames 是 pandas 对象,具有行和列。 如果使用循环,您将遍历整个对象。 Python 没有利用任何内置函数,所以速度非常慢。 在本文的示例数据中,有65列和1140行,包含了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'
fe0545e1f02e494c0277ff71caf01424.png

因为在数据里有英超的每一场比赛,所以必须检查我们感兴趣的球队(阿森纳)是否踢球,如果是这样的话,他们是主队还是客队。 正如你所看到的,这个循环非常慢,花了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
4e11feb7cf10301d7e41e7018b550da2.png

该代码运行时间为68毫秒,比标准循环快321倍。 但是,许多人建议不要使用它,因为仍然有更快的选项,而且 iterrows ()不保留行之间的 dtype。 这意味着,如果您在 dataframedtypes 上使用 iterrows () ,那么可以更改它,这可能会导致很多问题。 保留 dtypes,也可以使用 itertuple ()。

Apply ()方法ーー快811倍

Apply 本身并不快,但当与 DataFrames 结合使用时,就具有优势。 这取决于 apply 表达式的内容。 如果它可以在 Cython 空间中执行,那么 apply 要快得多(这里就是这种情况)。

我们可以在 Lambda 函数中使用 apply。 我们所要做的就是指定这个轴。 在这种情况下,我们必须使用 axis 1,因为我们希望执行按列操作:

2a83ff3fc63ee4c3dd86dc3f124aa54b.png

这段代码甚至比以前的方法更快,完成时间为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 series作为输入创建新列:

40c97f5a4289dc80ff51f40ceb2fa0e0.png

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

Numpy 矢量化ーー快71803倍

在前面的示例中,我们将 Pandas series传递给函数。 通过添加.values 接收到一个 Numpy 数组:

b80912e9a1c6543d3e5add8357adb0fc.png

Numpy 数组是如此之快,代码运行了0,305毫秒,比开始时使用的标准循环快了71803倍。

总结

如果您使用 Python、 Pandas 和 Numpy 进行数据分析,那么总会有一些空间可以改进您的代码。 我们比较了五种不同的方法,并根据一些计算将一个新列添加到我们的 DataFrame 中。 我们注意到了速度方面的巨大差异:

da7b4e5fba191928ed82467fd80c6c05.png

如果你能从这篇文章中学到两条规则,我会很高兴:

  1. 如果确定需要使用循环,则应始终选择 apply 方法。
  2. 否则,矢量化总是更好的,因为它更快。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值