相关文章推荐
谦虚好学的火柴  ·  Spark ...·  1 月前    · 
近视的橙子  ·  python ...·  4 周前    · 
飘逸的饭卡  ·  dataframe split ...·  2 天前    · 
知识渊博的打火机  ·  列表及其操作·  1 年前    · 
逆袭的电梯  ·  【转】.net core webapi ...·  1 年前    · 
一行代码将Pandas加速4倍

一行代码将Pandas加速4倍







文章发布于公号【数智物语】 (ID:decision_engine),关注公号不错过每一篇干货。




作者 | George Seif
编译 | ronghuaiyang
出品 | AI公园(ID:AI_Paradise)


导读:虽然Pandas的功能非常强大,但是对于大数据集来说,确实是很慢的。


虽然 panda 是 Python 中用于数据处理的库,但它并不是真正为了速度而构建的。了解一下新的库 Modin,Modin 是为了分布式 panda 的计算来加速你的数据准备而开发的。


Pandas 是处理 Python 数据的首选库。它易于使用,并且在处理不同类型和大小的数据时非常灵活。它有大量的函数,使得操纵数据变得轻而易举。




随着时间的推移,各种Python包的流行程度


但是有一个缺点:对于较大的数据集来说,panda“慢”。


默认情况下,panda 使用单个 CPU 内核作为单个进程执行其函数。这对于较小的数据集工作得很好,因为你可能不会注意到速度上的差异。但是,随着数据集越来越大,计算量越来越大,如果只使用单个 cpu 核,速度会受到很大的影响。它在数据集上同一时间只能计算一次,但该数据集可以有数百万甚至数十亿行。


然而,大多数用于数据科学的现代机器都有至少 2 个 CPU 核。这意味着,以 2 个 CPU 核为例,在使用 pandas 时,50%或更多的计算机处理能力在默认情况下不会执行任何操作。当你使用 4 核(现代 Intel i5)或 6 核(现代 Intel i7)时,情况会变得更糟。pandas 的设计初衷并不是为了有效利用这种计算能力。


Modin 是一个新的库,通过在系统所有可用的 CPU 核上自动分配计算来加速 pandas。有了它,对于任何尺寸的 pandas 数据数据集,Modin 声称能够以 CPU 内核的数量得到近乎线性的加速。


让我们看看它是如何工作的,并通过一些代码示例进行说明。


01Modin 如何用 Pandas 并行计算


给定 pandas 中的 DataFrame ,我们的目标是以尽可能快的方式对其执行某种计算或处理。可以用*.mean()取每一列的平均值,用groupby对数据进行分组,用drop_duplicates()*删除所有重复项,或者使用其他任何内置的 pandas 函数。


在前一节中,我们提到了 pandas 如何只使用一个 CPU 核进行处理。自然,这是一个很大的瓶颈,特别是对于较大的 DataFrames,计算时就会表现出资源的缺乏。


理论上,并行计算就像在每个可用的 CPU 核上的不同数据点上应用计算一样简单。对于一个 pandas 的 DataFrame,一个基本的想法是将 DataFrame 分成几个部分,每个部分的数量与你拥有的 CPU 内核的数量一样多,并让每个 CPU 核在一部分上运行计算。最后,我们可以聚合结果,这是一个计算上很 cheap 的操作。




多核系统如何更快地处理数据。对于单核进程(左),所有10个任务都放在一个节点上。对于双核进程(右图),每个节点承担5个任务,从而使处理速度加倍。


这正是 Modin 所做的。它将 DataFrame 分割成不同的部分,这样每个部分都可以发送到不同的 CPU 核。Modin 在行和列之间划分 DataFrame。这使得 Modin 的并行处理可扩展到任何形状的 DataFrame。


想象一下,如果给你一个列多行少的 DataFrame。有些库只执行跨行分区,在这种情况下效率很低,因为我们的列比行多。但是对于 Modin 来说,由于分区是跨两个维度进行的,所以并行处理对于所有形状的数据流都是有效的,不管它们是更宽的(很多列)、更长的(很多行),还是两者都有。




panda的DataFrame(左)存储为一个块,只发送到一个CPU核。Modin的DataFrame(右)跨行和列进行分区,每个分区可以发送到不同的CPU核上,直到用光系统中的所有CPU核。


上面的图是一个简单的例子。Modin 实际上使用了一个“分区管理器”,它可以根据操作的类型改变分区的大小和形状。例如,可能有一个操作需要整个行或整个列。在这种情况下,“分区管理器”将以它能找到的最优方式执行分区和分配到 CPU 核上。它是非常灵活的。


为了在执行并行处理时完成大量繁重的工作,Modin 可以使用 Dask 或 Ray。它们都是使用 Python api 的并行计算库,你可以选择一个或另一个在运行时与 Modin 一起使用。Ray 目前是最安全的一个,因为它更稳定 —— Dask 后端是实验性的。


已经有足够的理论了。让我们来看看代码和速度基准测试!


02Modin 速度基准测试


安装 Modin 的最简单的方法是通过 pip。下面的命令安装 Modin、Ray 和所有相关的依赖项:


pip install modin[ray]


对于我们下面的例子和 benchmarks,我们使用了 Kaggle 的 CS:GO Competitive Matchmaking Data。CSV 的每一行都包含了 CS:GO 比赛中的一轮数据。


现在,我们尝试使用最大的 CSV 文件(有几个),esea_master_dmg_demo .part1.csv,它有 1.2GB。有了这样的体量,我们应该能够看到 pandas 有多慢,以及 Modin 是如何帮助我们加速的。对于测试,我使用一个 i7-8700k CPU,它有 6 个物理内核和 12 个线程。


我们要做的第一个测试是使用 read_csv()读取数据。Pandas 和 Modin 的代码是完全一样的。


### Read in the data with Pandas
import pandas as pd
s = time.time()
df = pd.read_csv("esea_master_dmg_demos.part1.csv")
e = time.time()
print("Pandas Loading Time = {}".format(e-s))
### Read in the data with Modin
import modin.pandas as pd
s = time.time()
df = pd.read_csv("esea_master_dmg_demos.part1.csv")
e = time.time()
print("Modin Loading Time = {}".format(e-s))


为了测量速度,我导入了time模块,并在read_csv()之前和之后放置了一个time()。panda 将数据从 CSV 加载到内存需要 8.38 秒,而 Modin 需要 3.22 秒。这是 2.6 倍的加速。对于只修改 import 语句来说,这不算太寒酸!


让我们在 DataFrame 上做一些更复杂的处理。连接多个 DataFrames 是 panda 中的一个常见操作 — 我们可能有几个或多个包含数据的 CSV 文件,然后必须一次读取一个并连接它们。我们可以使用 panda 和 Modin 中的*pd.concat()*函数轻松做到这一点。


我们希望 Modin 能够很好地处理这种操作,因为它要处理大量的数据。代码如下所示。


import pandas as pd
df = pd.read_csv("esea_master_dmg_demos.part1.csv")
s = time.time()
df = pd.concat([df for _ in range(5)])
e = time.time()
print("Pandas Concat Time = {}".format(e-s))
import modin.pandas as pd
df = pd.read_csv("esea_master_dmg_demos.part1.csv")
s = time.time()
df = pd.concat([df for _ in range(5)])
e = time.time()
print("Modin Concat Time = {}".format(e-s))


在上面的代码中,我们将 DataFrame 与自身连接了 5 次。pandas 在 3.56 秒内完成了连接操作,而 Modin 在 0.041 秒内完成,速度提高了 86.83 倍!看起来,即使我们只有 6 个 CPU 核心,DataFrame 的分区也有助于提高速度。


用于 DataFrame 清洗的 panda 函数是*.fillna()*函数。此函数查找 DataFrame 中的所有 NaN 值,并将它们替换为你选择的值。panda 必须遍历每一行和每一列来查找 NaN 值并替换它们。这是一个应用 Modin 的绝佳机会,因为我们要多次重复一个非常简单的操作。


import pandas as pd
df = pd.read_csv("esea_master_dmg_demos.part1.csv")
s = time.time()
df = df.fillna(value=0)
e = time.time()
print("Pandas Concat Time = {}".format(e-s))
import modin.pandas as pd
df = pd.read_csv("esea_master_dmg_demos.part1.csv")
s = time.time()
df = df.fillna(value=0)
e = time.time()