保存到文件时Dask崩溃?

时间:2020-12-30 01:07:39

标签: python pandas dask dask-distributed

我正在尝试对数据集进行 onehot 编码,然后按特定列分组,这样我就可以为该列中的每个项目获取一行,并汇总该特定行的 onehot 列是正确的。它似乎正在处理小数据,并且使用 dask 似乎适用于大数据集,但是当我尝试保存文件时遇到了问题。我试过 CSV 和镶木地板文件。我想保存结果,然后我可以稍后分块打开它。

这是显示问题的代码(以下脚本生成 2M 行和多达 30k 个唯一值以进行 onehot 编码)。

import pandas as pd
import numpy as np
import dask.dataframe as dd
from dask.distributed import Client, LocalCluster, wait

sizeOfRows = 2000000
columnsForDF = 30000 
partitionsforDask = 500 
print("partition is ", partitionsforDask)


cluster = LocalCluster()
client = Client(cluster)
print(client)



df = pd.DataFrame(np.random.randint(0,columnsForDF,size=(sizeOfRows, 2)), columns=list('AB'))
ddf = dd.from_pandas(df, npartitions=partitionsforDask)
# ddf = ddf.persist()
wait(ddf)

# %%time
# need to globally know the categories before one hot encoding
ddf = ddf.categorize(columns=["B"])
one_hot = dd.get_dummies(ddf, columns=['B'])
print("starting groupby")
# result = one_hot.groupby('A').max().persist() # or to_parquet/to_csv/compute/etc.
# result = one_hot.groupby('A', sort=False).max().to_csv('./daskDF.csv', single_file = True)
result = one_hot.groupby('A', sort=False).max().to_parquet('./parquetFile')
wait(result)

它似乎可以工作,直到将 groupby 转换为 csv 或 parquet。在这一点上,我收到许多关于工人超过 95% 内存的错误,然后程序退出并出现“killedworker”异常:

distributed.nanny - WARNING - Worker exceeded 95% memory budget. Restarting
KilledWorker: ("('dataframe-groupby-max-combine-3ddcd8fc854613101b4bdc7fccde32cd', 1, 0, 0)", <Worker 'tcp://127.0.0.1:33815', name: 6, memory: 0, processing: 22>)

监控我的机器,我从来没有接近超过内存,我的驱动器空间超过 300 GB 从未使用过(在此过程中没有创建文件,尽管它在 groupby 部分)。

我能做什么?

更新 - 我想我会添加一个奖项。我在使用 .to_csv 时也遇到了同样的问题,因为其他人也遇到了类似的问题,我希望它对广大受众有价值。

1 个答案:

答案 0 :(得分:2)

让我们首先考虑最终结果:它将是一个包含 30'000 列和 30'000 行的数据框。此对象将占用大约 6.7 GB 的内存。 (使用 dtype 可以减少内存占用,而且并非所有组合都可能出现在数据中,但为了简单起见,让我们忽略这些点)

现在,假设我们只有两个分区,每个分区包含所有可能的虚拟变量组合。这意味着每个工作人员至少需要 6.7 GB 来存储 .groupby().max() 对象,但最后一步需要 13.4 GB,因为最终工作人员需要找到这两个对象的 .max。自然,如果您有更多分区,最终工作线程的内存需求将会增加。有一种方法可以通过在相关函数中指定 dask 来控制 split_every。例如,如果您指定 .max(split_every=2),则任何 worker 最多将收到 2 个对象(split_every 的默认值为 8)。

在处理 500 个分区的早期,每个分区可能只包含可能的虚拟值的一个子集,因此内存要求很低。但是,随着 dask 继续计算最终结果,它将组合具有不同虚拟值组合的对象,因此内存需求将在管道末端增加。

原则上,您也可以使用 resources 来限制一个 worker 一次将执行的任务数量,但如果 worker 没有足够的内存来处理这些任务,这将无济于事。

>

有哪些潜在的解决方法?至少有几个选项:

  • 使用拥有更多资源的工人;

  • 简化任务(例如,根据可能类别的子集将任务拆分为多个子任务);

  • 使用 delayed/futures 开发自定义工作流程,对数据进行排序并实施自定义优先级,确保工作人员在进行最终聚合之前完成工作子集。

如果工作内存是一个约束,那么子集必须非常细粒度。例如,在极限情况下,仅将一个可能的虚拟变量组合子集将具有非常低的内存要求(初始数据加载和过滤器仍将需要足够的内存来容纳一个分区),但当然这是一个极端的例子,会产生数十个数千个任务,因此建议使用更大的类别组(平衡任务数量和内存要求)。要查看示例,您可以查看此相关的 answer