dask read_拼花地板,带pyarrow记忆放大

dask read_拼花地板,带pyarrow记忆放大,dask,pyarrow,fastparquet,Dask,Pyarrow,Fastparquet,我用dask写和读拼花地板。我使用fastparquet引擎写作,使用pyarrow引擎阅读。 我的工人有1GB内存。使用fastparquet时,内存使用情况很好,但当我切换到pyarrow时,它会爆炸并导致工作程序重新启动。 下面我有一个可复制的示例,它在1gb内存限制的工作进程上使用pyarrow失败。 实际上,我的数据集比这个大得多。使用pyarrow的唯一原因是,与fastparquet(大约7x-8x)相比,它在扫描时提高了速度 dask:0.17.1 pyarrow:0.9.0.p

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

dask:0.17.1

pyarrow:0.9.0.post1

快速拼花地板: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()

编辑:我的原始设置运行spark作业,使用fastparquet将数据并行写入分区。因此,元数据文件是在最里面的分区而不是父目录中创建的。因此,使用glob path而不是父目录(读取父目录时,fastparquet的速度要快得多,而使用glob path扫描时,pyarrow的速度要快得多)

我建议在
read\u parquet
调用中选择所需的列

df = dd.read_parquet('/path/to/*.parquet', engine='pyarrow', columns=['b'])

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

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

使用您的示例数据

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)
在写入之前,将列
b
c
转换为分类

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个(快速拼花地板,类别)


您还可以在加载数据时进行筛选。例如,按特定列进行筛选
df=dd.read_拼花('/path/to/*.parquet',engine='fastparquet',filters=[(列,'operation','SOME_VALUE')))


想象一下像
=
这样的操作,我确实需要存储的所有列。但这是正常的行为吗?我的生产环境具有配置了5gb限制的工作节点。Fastparquet的最大使用容量为3gb,而pyarrow只是突破了这一限制,您可以尝试限制内存或工作线程数。我不知道arrow的内存占用有多大。dataframe的大小是1.6GB,所以在本例中实际上您使用了太多的分区。如果您使用pyarrow编写,您会得到同样的体验吗?请注意,当您使用fastparquet编写时,您会得到一个元数据文件,这样您就可以在不使用glob部分的情况下从
tmpdir
读取数据,而且速度应该会更快。实际上,元数据文件对我来说是个问题。整个过程如下:分区是使用spark作业并行编写的。因此元数据文件是在最里面的分区中创建的,因此不能使用父目录读取所有数据。这就是为什么使用glob-path(fastparquet在加载父目录时更快,但在加载glob-path时pyarrow可以轻松获胜)。有没有办法不用dask中的元数据文件来读取fastparquet?我用不同的分区测试了不同的设置,结果如下:1.fastparquet使用父目录路径更快(不过需要元数据文件)。内存使用也很好。读取全局路径时速度较慢。2.使用全局路径读取时,Pyarrow速度更快。父目录读取很困难,因为模式不匹配(列顺序不同,在写入df时很容易修复)。确实会导致高内存使用率我的产品数据非常庞大,大小约为50000000行,共17列。我不能使用父目录来加载数据(如上所述),因此必须进行全局路径读取。
In [42]: %timeit df_partitioned.count().compute()
863 ms ± 78.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)