簡體   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