【问题标题】:Pyarrow why and when should I use a stream buffer writer?Pyarrow 为什么以及何时应该使用流缓冲区编写器?
【发布时间】:2021-04-26 21:35:13
【问题描述】:

我需要从自定义二进制文件(使用多处理和随机访问)中读取大量数据,以便执行计算并存储到 parquet 数据集。 我知道列数,但我不知道要存储的项目数。

因此,文件被切割成N逻辑部分,每个部分都是由块读取器功能处理的。 所以我应该进入这些函数:

  1. 使用python类型List创建x列(通过追加动态分配)并在块的末尾创建一个记录批?
  2. 与 (1) 相同,但每个 y 记录创建一个记录批次。这里我们按批次控制商品的数量。这个过程可能会更慢,因为我们必须在每次迭代中设置一个条件 (if no_items == y:…)
  3. 从 (1) 开始,最后将记录批次存储到 RecordBatchStreamWriter 以返回缓冲区
  4. 从 (2) 开始,最后将记录批次存储到 RecordBatchStreamWriter 以返回缓冲区
  5. 其他……

我读过ipcmemoryarrow-streaming-columnar,但仍不清楚如何处理巨大的随机访问文件。

感谢您的见解

【问题讨论】:

    标签: python pyspark parquet pyarrow apache-arrow


    【解决方案1】:

    如果您想要的输出是 parquet 数据集并且您的输入格式是自定义二进制文件,那么记录批处理 API 可能对您没有帮助,除非您将计算分散到多个进程(而不是线程)。您提到了多处理,所以有可能是这种情况,但我只是想确保您不是指多线程。

    另外,当您说 parquet 数据集时,我不确定您是指一个巨大的 parquet 文件还是一个公共目录结构中的一些 parquet 文件。通常一个“parquet 数据集”是多个 parquet 文件。

    这个任务有很大的灵活性,所以你如何去做可能取决于你以后要如何读取数据。

    输出

    1. 多个文件。您创建多个 parquet 文件,其中每个 parquet 文件包含数据的所有列,但仅包含 X 行。如果您可以在数据中的某些列(例如测量日期/制造日期/设备 ID/实验条件)上对批次进行分区,那么您将能够非常有效地使用这些作为以后的查询参数(例如,如果您稍后按测量日期查询,您可以通过文件名和目录名找出要加载的文件)。在这种情况下,您将需要 dataset APIs。您也可以始终使用这种方法按行号进行分区,尽管您希望在这种情况下与单个文件案例相比具有同样多的优势(使用仅支持整个文件 parquet 读取的 3rd 方 parquet 工具仍然会更容易)。

    2. 单个文件。您可以创建一个巨大的单个 parquet 文件,该文件被分成许多“行组”。这将允许您稍后读取单个行组,或者通过一次处理单个行组来进行内存有效的批处理。这里的缺点是您只能按“行号”进行批处理,因此如果您以后想通过时间戳列之类的内容进行查找,那么您可能必须读取/扫描所有数据(除非您知道某种映射方式行号的时间戳)。如果您想要这种方法,您将需要使用 ParquetWriter 来编写单独的行组。

    输入

    在您可以写入上述任何内容之前,您需要以自定义格式阅读并将数据转换为箭头格式。如果您纯粹在 python 中执行此操作,这可能会很昂贵,但由于它是一种自定义格式,您不太可能使用内置的 Arrow 实用程序来读取数据。如果您的自定义格式具有用于读取数据的任何类型的本机库,那么使用这些库读取数据、提取相关数据缓冲区并使用箭头元数据包装它们以便箭头可以访问它们可能会更快。使用 python 来操作生成的 Arrow 元数据对象(例如表/分块数组等)应该非常简单/快速。

    如果您选择在 python 中解析文件,那么这样做的成本可能会很大,以至于您选择哪个选项 1-4 并不重要。理想情况下,尽管您希望能够将输入文件读入您​​打算写入的块中。希望从输入文件中读取固定大小的一批行(所有列)会很容易。

    如果您希望输出格式按一列或多列分区(例如按测试标准分区),那么它会更复杂。如果没有很好的方法来了解测试标准如何映射到行,您可能必须将整个文件作为单个表读入内存,进行多次传递,或者拥有多个文件(例如,您可以拥有每个测试标准而不是一个文件每个测试标准 20 个,只需将输入文件读取为 20 个批次)。

    IPC

    既然您提到了多处理和记录批处理读取器/写入器,我将稍微谈谈如何使用它们。如果您决定跨多个进程(甚至可能是多个服务器)读取文件并且希望这些进程进行通信,那么您可以使用记录批处理读取器/写入器。例如,假设您有一个进程专门用于读取自定义文件并将其解析为箭头格式。然后,该过程可以使用记录批编写器将这些文件发送到您的下一个过程,该过程将使用记录批阅读器将它们读入。然后,第二个过程可以,例如,将记录批次写入一些拼花文件的工作。记录批读写器允许您以箭头格式写入,以便在进程之间快速传输。

    总结

    如果不详细了解您正在阅读的文件以及您的最终处理/查询目标,就很难准确地说出该怎么做。我认为您的问题的简单答案是您阅读输入的方式将取决于您最终希望如何创建输出。

    【讨论】:

    • 感谢@Pace 提供有据可查的答案。事实上,我想在 python 中使用多处理作为 GIL 限制的解决方法。我的自定义格式是通过库 pysam 用于生物信息学的 bcf 文件:pysam.readthedocs.io/en/latest/api.html#vcf-files
    • @Pace,很好奇,我理解您的回答,因为您发现可以以流方式写入分区数据集,您能否详细说明如何做到这一点?在我看来,整个数据集必须在内存中(或某处,但已完全加载),以便 Pyarrow 知道在哪里完全存储分区数据集。否则,它需要附加到已经创建的 Parquet 文件中,我认为 Pyarrow 不会这样做。你能纠正我吗?
    猜你喜欢
    • 2014-04-21
    • 1970-01-01
    • 2010-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-22
    • 1970-01-01
    • 2023-03-08
    相关资源
    最近更新 更多