Dask的Merge操作性能对比

3,673 阅读

在前面的博客中,我们已经对Dask做了一点简单的介绍了,在这篇博客中我们来对比一下DaskDataFrame在不同条件下的运算性能,主要是连接操作的性能(merge)。

Dask中的DataFrame实际是多个pandas的DataFrame的集合,merge操作是最常见的操作之一。但是,不同情况下的merge操作对性能的影响很大。本节主要考虑以下因素来比较连接操作的性能。

1、DataFrame中列的数量 2、join的字段是否是index 3、join的字段是整形还是字符串 4、DataFrame中partition的数量 5、关联的列是否有序

这篇博文中的项目已经在Github中释放了,链接如下: https://github.com/df19900725/datalearner_python_exp

为了对比,我们用generate_table_data.py来生成数据。

一、列的数量的影响

Dask可以读取文件夹下所有的文件,但是,你也可以选择读取所有的列,还是读取指定的列。这里我们生成两个数据:

第一个数据是10个文件,每个文件有100,0000行,每一行有50个字段。 第二个数据是2个文件,每个文件500,0000行,每一行是5个字段。

我们使用如下语句进行关联:

df3 = df1.merge(df2, on="id", how="left")
df3.to_csv(output_dir)

最后发现,读取所有的字段进行关联需要947秒,而仅仅读取2个字段进行关联只需要69秒。因此,尽量只读取你需要的数据进行关联,这将加快你的处理速度。

二、join的字段是否被设置成index

通常情况下,DataFrame格式的数据包含了index,也就是每一行的行名,index可以是根据需要生成的行号,也可以是指定的某一列,如ID列作为index。关联的时候,是以index进行关联还是以某一列关联效率是不同的。因为Dask中设置了index之后,这一列的数据将会进行排序和分割,形成不同的divisions,divisions包括每个partition索引的最小值和最后一个partition索引的最大值。这对关联和group都很有帮助。

在这个实验中,我们做了如下几个实验,注意,这些数据集的ID都是排序好的,同时,数据二,也就是要关联的数据的index设置与数据一是保持一致的:

| 实验组 | 数据 | 文件数 | 字段数 | 文件行数 | 字段类型|index | 耗时(秒) | ------------ | ------------ | | 实验一 | 数据集一 | 1 | 10 | 1000,0000 | string | N |72 | 实验一 | 数据集一 | 1 | 10 | 1000,0000 | string | Y |57 | 实验一 | 数据集二 |2 | 5 | 50,0000 | string | \ |
| | 实验二 | 数据集一 | 1 | 10 | 1000,0000 | integer | N |54 | 实验二 | 数据集一 | 1 | 10 | 1000,0000 | integer | Y |33 | 实验二 | 数据集二 | 2 | 5 | 500000 | integer | \ |
| | 实验三 | 数据集一 | 10 | 10 | 100,0000 | string | N |66 | 实验三 | 数据集一 | 10 | 10 | 100,0000 | string | Y |53 | 实验三 | 数据集二 | 2 | 5 | 50,0000 | string | \ |
| | 实验四 | 数据集一 | 10 | 10 | 100,0000 | integer | N |43 | 实验四 | 数据集一 | 10 | 10 | 100,0000 | integer | Y |30 | 实验四 | 数据集二 | 2 | 5 | 500000 | integer | \ | \

从以上四组实验中我们都可以发现,不管在什么情况下,把要关联的字段设置为index会加速关联的处理。

三、关联的字段是整型还是字符串

关联的时候需要设置以那一列关联,或者是以index关联,这一列的类型是字符串还是整型也将影响关联的效率。还是上述四组实验,我们更换一下对比的顺序:

| 实验组 | 数据 | 文件数 | 字段数 | 文件行数 | 字段类型|index | 耗时(秒) | ------------ | ------------ | | 实验一 | 数据集一 | 1 | 10 | 1000,0000 | string | N |72 | 实验一 | 数据集一 | 1 | 10 | 1000,0000 | integer | N |54 | 实验一 | 数据集二 |2 | 5 | 50,0000 | \ | N |
| | 实验二 | 数据集一 | 1 | 10 | 1000,0000 | string | Y |57 | 实验二 | 数据集一 | 1 | 10 | 1000,0000 | string | Y |33 | 实验二 | 数据集二 | 2 | 5 | 500000 | integer | Y |
| | 实验三 | 数据集一 | 10 | 10 | 100,0000 | string | N |66 | 实验三 | 数据集一 | 10 | 10 | 100,0000 | integer | N |43 | 实验三 | 数据集二 | 2 | 5 | 50,0000 | string | Y |
| | 实验四 | 数据集一 | 10 | 10 | 100,0000 | string | Y |53 | 实验四 | 数据集一 | 10 | 10 | 100,0000 | integer | Y |30 | 实验四 | 数据集二 | 2 | 5 | 500000 | integer | Y | \

从这四个实验来看,显然关联的列如果是integer类型,它的关联速度要远快于string类型。它的效果甚至比设置index还要好。

四、DataFrame中的partition数量

Dask中DataFrame的partition数量也会影响关联的速度,一般来说,单个文件如果能被放到内存中,那么几个文件读取就会有几个partition。在上述实验中我们也可以看到,partition的数量适当多一点也会加快运行的速度。但是这也不是绝对的。当partition数量过多的时候,也会消耗调度的资源。因此,分布式情况下,这种情况需要慎重考虑,官方推荐一般一个partition的大小为100MB最合适。但实际我们使用发现不一定,32MB有时候也很好。

五、关联的列是否有序

上述实验都是在有序的ID情况下进行关联得到的结果。但实际上很多时候未必ID是有序的。我们也构造了一个乱序的数据集。实验如下:

| 实验组 | 数据 | 文件数 | 字段数 | 文件行数 | 字段类型|index | 有序|耗时(秒) | ------------ | ------------ | | 实验一 | 数据集一 | 10 | 10 | 100,0000 | string | N |N|62 | 实验一 | 数据集一 | 10 | 10 | 100,0000 | string | N |Y|66 | 实验一 | 数据集二 | 2 | 5 | 50,0000 | string | N |
| | 实验二 | 数据集一 | 10 | 10 | 100,0000 | string | Y |N|120 | 实验二 | 数据集一 | 10 | 10 | 100,0000 | string | Y |Y|53 | 实验二 | 数据集二 | 2 | 5 | 500000 | string | Y |
| | 实验二 | 数据集一 | 10 | 10 | 100,0000 | integer | N |N|47 | 实验二 | 数据集一 | 10 | 10 | 100,0000 | integer | N |Y|43 | 实验二 | 数据集二 | 2 | 5 | 500000 | integer | N |
| | 实验二 | 数据集一 | 10 | 10 | 100,0000 | integer | Y |N|59 | 实验二 | 数据集一 | 10 | 10 | 100,0000 | integer | Y |Y|30 | 实验二 | 数据集二 | 2 | 5 | 500000 | integer | Y | \

如上表所示,当关联的字段是index的时候,乱序要比有序慢很多,但如果是其他的列,那么乱序并不比排序的结果慢。这个应该是由于乱序的话设置index是需要时间的。

六、总结

根据这些实验其实我们也可以看到,如果希望提高dask的merge效率,有几个比较值得操作的是将关联的字段尽量设置为整型的字段,整型的列对关联的效率的提升非常有帮助,其次是要将关联的列设置为index,这会提高dask对dataframe的理解,进而提升关联的速度,最后,还是最好将数据排个序,虽然这是比较耗时的,但对于merge的提升也是客观的。

DataLearner 官方微信

欢迎关注 DataLearner 官方微信,获得最新 AI 技术推送

DataLearner 官方微信二维码