繁体   English   中英

从镶木地板读取时,dask数据框列重命名较慢

[英]dask dataframe column renames are slow(er) when read from parquet

我发现无论何时从实木复合地板文件中读取数据帧, dask.dataframe.rename显着增加计算时间:

In [1]: import dask.dataframe as dd
   ...: df = dd.demo.daily_stock('GOOG', '2008', '2010', freq='1s', random_state=1234)
   ...: 

In [2]: %time df.close.mean().compute()
CPU times: user 7.73 s, sys: 1.15 s, total: 8.88 s
Wall time: 3.5 s
Out[2]: 452.30345234893554

In [3]: %time df = df.rename(columns={col: col.upper() for col in df.columns}); df.CLOSE.mean().compute()
CPU times: user 8.06 s, sys: 1.21 s, total: 9.27 s
Wall time: 3.81 s

In [4]: df.to_parquet('df', compression='GZIP')
   ...: df = dd.read_parquet('df')
   ...: 

In [5]: %time df.CLOSE.mean().compute()
CPU times: user 4.14 s, sys: 729 ms, total: 4.87 s
Wall time: 2.1 s
Out[5]: 452.30345234893554

In [6]: %time df = df.rename(columns={col: col.lower() for col in df.columns}); df.close.mean().compute()
CPU times: user 9.72 s, sys: 1.89 s, total: 11.6 s
Wall time: 4.81 s

请注意,差异在原始数据帧上很小,但在基于镶木地板的数据帧上超过两倍。

在大型数据集(约20-30GB)上,这个问题被夸大了,我看到mean计算时间从几秒钟延长到了几分钟。

这是我不知道的镶木地板文件固有的东西,还是某种错误?

实木复合地板是一间专卖店。 从镶木地板文件中读取单个列可能比读取整个数据集快得多。 当您执行df.close.mean().compute() Dask会注意到您有一个read_parquet操作,紧随其后的是列访问操作,它可以智能地将它们融合到更智能的内容中,如下所示:

df = dd.read_parquet(filename, columns=['close'])

但是,当您在read_parquet调用和列访问操作之间进行rename操作时,Dask.dataframe不够智能,无法意识到它可以使列访问和重命名成为可能,因此最终您会从Parquet文件中读取所有数据,重命名列,然后丢弃所有列(除一个列)。

缺乏执行有关计算的高级推理的能力正是在数据库或更高级的系统(例如Spark Dataframes)开始赢得Dask.dataframe的地方。 Dask的核心通常处于较低级别,因此可以进行更疯狂的计算,但是却失去了进行除最基本的查询优化之外的任何功能的能力。

因此,在这种情况下,并不是事实导致rename放慢了速度,而是事实以一种非常简单的优化方案rename了。

这可能是因为重命名方法作用于数据帧的每个分区,并且我认为它的开销与dd.rename等效。

考虑一下:

In [45]: %time (dd.demo.daily_stock('GOOG', '2008', '2010', freq='1s',  
random_state=1234).repartition(npartitions=1).rename(columns = {col: 
col.upper() for col in df.columns}).CLOSE.mean().compute())
CPU times: user 11.7 s, sys: 4.65 s, total: 16.3 s
Wall time: 9.23 s
Out[45]: 450.46079905299979

In [46]: %time (dd.demo.daily_stock('GOOG', '2008', '2010', freq='1s',  
random_state=1234).repartition(npartitions=1).close.mean().compute())
CPU times: user 11.3 s, sys: 4.63 s, total: 15.9 s
Wall time: 8.8 s
Out[46]: 450.46079905299979

当partition设置为1时,重命名开销似乎不如您的示例那么明显。

更新1:添加实木复合地板示例

In [103]: data =dd.read_parquet('df').repartition(npartitions=1).rename(columns = {'close':'ClOSE', 'high ':'HIGH', 'low':'LOW', 'open':'OPEN'})

In [104]: %time data.ClOSE.mean().compute()
CPU times: user 9.68 s, sys: 2.84 s, total: 12.5 s
Wall time: 5.72 s
Out[104]: 450.46079905299979

In [105]: data = dd.read_parquet('df').repartition(npartitions=1)

In [106]: %time data.close.mean().compute()
CPU times: user 9.37 s, sys: 2.56 s, total: 11.9 s
Wall time: 5.1 s
Out[106]: 450.46079905299979

更新2:显式添加列

根据以上Matt的回答,避免读取Parquet文件的所有列,如下所示:

%time dd.read_parquet('df',columns =['close']).rename(columns = {'close':'CLOSE'}).CLOSE.mean().com
     ...: pute()
CPU times: user 4.65 s, sys: 801 ms, total: 5.45 s
Wall time: 2.71 s

类似于:

%time dd.read_parquet('df',columns =['close']).close.mean().compute()
CPU times: user 4.46 s, sys: 795 ms, total: 5.25 s
Wall time: 2.51 s
Out[110]: 450.46079905300002

除了 :重命名+任务调度在我的机器上的单个数据分区上有40ms的开销:

In [114]: %timeit -n 3 dd.read_parquet('df',columns =['close']).repartition(npartitions=1).rename(columns = {
     ...: 'close': 'CLOSE'}).CLOSE.mean().compute()
3 loops, best of 3: 2.36 s per loop

In [115]: %timeit -n 3 dd.read_parquet('df',columns =['close']).repartition(npartitions=1).close.mean().compu
     ...: te()
3 loops, best of 3: 2.32 s per loop

应用于大约500个分区,也就是大约20秒。 以防万一,这种事情将来很有用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM