读取/写入 Parquet 文件而不读入内存(使用 Python)

Mik*_*son 7 python io parquet


我查看了我期望能够满足我的需求的标准文档(Apache ArrowPandas),但我似乎无法弄清楚。

我最了解Python,所以我想使用Python,但这不是严格要求。

问题

我需要将 Parquet 文件从一个位置(URL)移动到另一个位置(Azure 存储帐户,在本例中使用 Azure 机器学习平台,但这与我的问题无关)。

这些文件太大而无法简单地执行pd.read_parquet("https://my-file-location.parquet"),因为这会将整个文件读取到一个对象中。

期待

我认为必须有一种简单的方法来创建文件对象并逐行流式传输该对象 - 或者可能逐列块。就像是

import pyarrow.parquet as pq

with pq.open("https://my-file-location.parquet") as read_file_handle:
    with pq.open("https://my-azure-storage-account/my-file.parquet", "write") as write_filehandle:
        for next_line in read_file_handle{
            write_file_handle.append(next_line)
Run Code Online (Sandbox Code Playgroud)

我知道它会有点不同,因为 Parquet 主要是为了以柱状方式访问。也许我会传递某种配置对象,它指定感兴趣的列,或者可以在一个块或类似的东西中抓取多少行。

但主要的期望是有一种方法可以访问 parquet 文件,而无需将其全部加载到内存中。我怎样才能做到这一点?

FWIW,我确实尝试过只使用 Python 的标准open函数,但我不确定如何使用openURL 位置和字节流。如果可以通过跳过open任何 Parquet 特定的内容来完成此操作,那也很好。

更新

一些评论建议使用类似 bash 的脚本,例如这里。如果没有别的办法我可以使用这个,但它并不理想,因为:

  • 我宁愿将这一切保留在完整的语言 SDK 中,无论是 Python、Go 还是其他语言。如果解决方案转移到带有管道的 bash 脚本中,则需要外部调用,因为最终解决方案不会完全由 bash、Powershell 或任何脚本语言编写。
  • 我真的很想利用 Parquet 本身的一些优势。正如我在下面的评论中提到的,Parquet 是列式存储。因此,如果我有一个包含 11 亿行和 100 列的“数据框”,但我只关心 3 列,我希望能够只下载这 3 列,从而节省大量时间和金钱。

Zéz*_*lle 9

很棒的帖子,基于 @Micah 的回答,我在其中投入了 2 美分,以防您不想阅读文档。一个小片段如下:

import pandas as pd
import numpy as np
from pyarrow.parquet import ParquetFile

# create a random df then save to parquet
df = pd.DataFrame({
    'A': np.arange(10000),
    'B': np.arange(10000),
    'C': np.arange(10000),
    'D': np.arange(10000),
})
df.to_parquet('./test/test')

# ****** below is @Micah Kornfield's answer ******
# 1. open parquet file
batch = ParquetFile('./test/test')
# 2. define generator of batches
record = batch.iter_batches(
    batch_size=10,
    columns=['B', 'C'],
)
# 3. yield pandas/numpy data
print(next(record).to_pandas()) # pandas
print(next(record).to_pydict()) # native python dict
Run Code Online (Sandbox Code Playgroud)


Mic*_*eld 5

这是可能的,但需要一些工作,因为除了柱状 Parquet 之外,还需要一个架构。

大致的工作流程是:

  1. 打开parquet 文件进行读取。

  2. 然后使用iter_batches增量读回行块(您也可以传递要从文件中读取的特定列以节省 IO/CPU)。

  3. 然后你可以进一步改造每pa.RecordBatch一个iter_batches。完成第一批的转换后,您可以获得其架构并创建一个新的ParquetWriter

  4. 对于每个转换后的批次调用write_table。您必须先将其转换为pa.Table.

  5. 关闭文件。

Parquet 需要随机访问,因此无法轻松地从 URI 进行流式传输(如果您通过HTTP FSSpec打开文件,pyarrow 应该支持它),但我认为您可能会在写入时被阻止。

  • 另请参阅 /sf/ask/4472386201/#64469365 批量调整大小对于管理内存非常重要。 (2认同)