dask read_parquet与pyarrow内存爆炸

时间:2018-06-15 10:13:19

标签: dask pyarrow fastparquet

我正在使用dask来编写和阅读镶木地板。我正在使用fastparquet引擎和使用pyarrow引擎阅读。 我的工作人员有1 GB的内存。使用fastparquet时,内存使用情况很好,但是当我切换到pyarrow时,它会爆炸并导致工作人员重新启动。 我有一个可重现的示例,在1gb内存限制的工作者上使用pyarrow失败。 实际上,我的数据集比这要大得多。使用pyarrow的唯一原因是,与fastparquet(大约7x-8x左右)相比,扫描速度提高了我的速度提升

dask:0.17.1

pyarrow:0.9.0.post1

fastparquet:0.1.3

import dask.dataframe as dd
import numpy as np
import pandas as pd

size = 9900000
tmpdir = '/tmp/test/outputParquet1'

d = {'a': np.random.normal(0, 0.3, size=size).cumsum() + 50,
    'b': np.random.choice(['A', 'B', 'C'], size=size),
    'c': np.random.choice(['D', 'E', 'F'], size=size),
    'd': np.random.normal(0, 0.4, size=size).cumsum() + 50,
    'e': np.random.normal(0, 0.5, size=size).cumsum() + 50,
    'f': np.random.normal(0, 0.6, size=size).cumsum() + 50,
    'g': np.random.normal(0, 0.7, size=size).cumsum() + 50}
df = dd.from_pandas(pd.DataFrame(d), 200)
df.to_parquet(tmpdir, compression='snappy', write_index=True, 
         engine='fastparquet')

#engine = 'pyarrow' #fails due to worker restart
engine = 'fastparquet' #works fine
df_partitioned = dd.read_parquet(tmpdir + "/*.parquet", engine=engine)
print(df_partitioned.count().compute())
df_partitioned.query("b=='A'").count().compute()

编辑:我的原始设置运行了火花作业,使用fastparquet将数据并行写入分区。所以元数据文件是在最里面的分区而不是父目录中创建的.Hence使用glob路径而不是父目录(fastparquet在父目录读取时快得多,而pyarrow在使用glob路径扫描时获胜)

3 个答案:

答案 0 :(得分:0)

我建议您在read_parquet电话

中选择所需的列
df = dd.read_parquet('/path/to/*.parquet', engine='pyarrow', columns=['b'])

这将允许您有效地只读取您需要的几列而不是一次读取所有列。

答案 1 :(得分:0)

我的非内存限制系统的某些计时结果

使用您的示例数据

In [17]: df_partitioned = dd.read_parquet(tmpdir, engine='fastparquet')

In [18]: %timeit df_partitioned.count().compute()
2.47 s ± 114 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [19]: df_partitioned = dd.read_parquet(tmpdir, engine='pyarrow')

In [20]: %timeit df_partitioned.count().compute()
1.93 s ± 96.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

在撰写之前将列bc转换为分类

In [30]: df_partitioned = dd.read_parquet(tmpdir, engine='fastparquet')

In [31]: %timeit df_partitioned.count().compute()
1.25 s ± 83.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [32]: df_partitioned = dd.read_parquet(tmpdir, engine='pyarrow')

In [33]: %timeit df_partitioned.count().compute()
1.82 s ± 63 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

使用fastparquet直接,单线程

In [36]: %timeit fastparquet.ParquetFile(tmpdir).to_pandas().count()
1.82 s ± 19 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

有20个分区而不是200个(fastparquet,categories)

In [42]: %timeit df_partitioned.count().compute()
863 ms ± 78.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

答案 2 :(得分:0)

您也可以在加载数据时进行过滤,例如按特定列 df = dd.read_parquet('/path/to/*.parquet', engine='fastparquet', filters=[(COLUMN, 'operation', 'SOME_VALUE')])

想象一下==><等操作。