Python/Pandas如何处理数百亿行、数十列的数据?
作者:量子位
使用 Python 和 Pandas 以及循环进行数据分析即将推出。
但其中,即使对于较小的DataFrame,使用标准循环也更耗时。
遇到更大的DataFrame需要更长的时间并且会导致更多的头痛。
现在有些人受不了了。他是来自德国的数据分析师,名叫BenediktDroste。
他表示,在等待了半个多小时代码运行后,他决定寻找更快的替代方案。
在此替代方案中,使用 Numpy 矢量化,与使用标准循环相比,加速率为 71803 倍。
他是如何实现这一目标的?让我们看看 ~
处理 3 年足球比赛数据的标准周期:20.7 秒
DataFrame 是一个具有行和列的 Panda 对象。如果使用循环,则必须循环遍历整个对象。
Python 无法使用任何内置函数并且速度非常慢。在BenediktDroste提供的示例中,这是一个有 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'

在本例中是阿森纳。在实现目标之前,您需要确认阿森纳参加了哪些比赛,无论是主场还是客场。然而,使用标准循环的速度非常慢,执行时间为20.7秒。
那么我们怎样才能更有效率呢?
Pandas 内置函数:iterrows() ー 快 321 倍
在第一个示例中,整个 DataFrame 是循环的。 iterrows() 为每一行返回一个序列,该序列作为索引对迭代 DataFrame,并将感兴趣的列作为序列迭代。这使得它比标准循环更快:
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

代码运行时间为 68 毫秒,比标准循环快 321 倍。但许多人不建议使用它,因为仍然有更快的选项,并且 iterrows() 无法存储行之间的数据类型。
这意味着如果您使用DataFrame dtypes iterrows(),您可以更改它,但这会导致很多问题。
如果需要存储dtypes,也可以使用itertuples()。这里就不详细介绍了,官方文档可以在这里找到:
pandas.DataFrame.itertuples - pandas 0.25.1 文档pandas.pydata.org
apply() 方法 ー811 倍快
自己应用虽然速度不快,但与 DataFrame 结合时有优势。这取决于所应用的表达式的内容。如果应用程序可以像这里一样在 Cython 空间中运行,那么它会快得多。
Apply 可以在 lambda 函数中使用。您所要做的就是指定轴。在本文的示例中,使用轴 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'
现在您可以使用 Panda 列作为输入创建新列:
在这种情况下,您甚至不需要循环。需要做的就是定制该功能的内容。您现在可以将 Panda 列直接传递给函数,从而获得巨大的加速。
Numpy 向量化 — 快 71803 倍
在上面的示例中,Panda 函数传递了一列。当你添加值时,你得到一个 Numpy 数组:
因为参考点的优势,Numpy 数组非常快。代码运行时间仅为 0.305 毫秒,比一开始使用的标准循环快了 71803 倍。
谁更强,一目了然
最后,BenediktDroste总结了上述方案。
他说如果你使用Python、Pandas和Numpy进行数据分析,代码总是有改进的空间。
对比以上五种方法,哪一种更快就一目了然了:
从这个图中可以得出两个结论:
- 1。如果要使用循环,则应始终选择 apply 方法。
- 2。否则,使用矢量化是最好的,因为它更快!
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。