如何用pyarrow编写Parquet元数据?

gol*_*bor 8 python parquet pyarrow

我使用pyarrow来创建和分析具有生物学信息的Parquet表,我需要存储一些元数据,例如,数据来自哪个样本,如何获取和处理。

Parquet似乎支持文件范围的元数据,但是我无法找到如何通过pyarrow写入它。我能找到的最接近的东西是如何编写行组元数据,但这似乎是一个过大的选择,因为文件中所有行组的元数据都是相同的。

有什么方法可以用pyarrow编写文件范围的Parquet元数据吗?

Pow*_*ers 8

此示例演示如何使用 PyArrow 创建包含文件元数据和列元数据的 Parquet 文件。

假设您有以下 CSV 数据:

movie,release_year
three idiots,2009
her,2013
Run Code Online (Sandbox Code Playgroud)

将 CSV 读入 PyArrow 表并使用列/文件元数据定义自定义架构:

movie,release_year
three idiots,2009
her,2013
Run Code Online (Sandbox Code Playgroud)

创建一个新表并将my_schema其写为 Parquet 文件:

import pyarrow.csv as pv
import pyarrow.parquet as pq
import pyarrow as pa

table = pv.read_csv('movies.csv')

my_schema = pa.schema([
    pa.field("movie", "string", False, metadata={"spanish": "pelicula"}),
    pa.field("release_year", "int64", True, metadata={"portuguese": "ano"})],
    metadata={"great_music": "reggaeton"})
Run Code Online (Sandbox Code Playgroud)

读取 Parquet 文件并获取文件元数据:

t2 = table.cast(my_schema)

pq.write_table(t2, 'movies.parquet')
Run Code Online (Sandbox Code Playgroud)

获取与该列关联的元数据release_year

s = pq.read_table('movies.parquet').schema

s.metadata # => {b'great_music': b'reggaeton'}
s.metadata[b'great_music'] # => b'reggaeton'
Run Code Online (Sandbox Code Playgroud)


Pac*_*ace 7

Pyarrow 将文件范围的元数据映射到表模式中名为 metadata的字段。遗憾的是,还没有(还)这方面的文档。

Parquet 元数据格式和 Pyarrow 元数据格式都将元数据表示为键/值对的集合,其中键和值都必须是字符串。这是不幸的,因为如果它只是一个 UTF-8 编码的 JSON 对象,它会更灵活。此外,由于这些是std::stringC++ 实现对象,因此它们是 Python 中的“b 字符串”(字节)对象。

Pyarrow 目前将自己的一些信息存储在元数据字段中。它有一个内置键b'ARROW:schema'和另一个内置键b'pandas'。在 pandas 的情况下,该值是一个用 UTF-8 编码的 JSON 对象。这允许命名空间。“pandas”模式可以根据需要拥有任意数量的字段,并且它们都在“pandas”下命名。Pyarrow 使用“pandas”模式来存储有关表具有什么样的索引以及列使用什么类型的编码的信息(当给定数据类型有多个可能的 Pandas 编码时)。我不确定b'ARROW:schema'代表什么。它似乎以某种我不认识的方式编码,我还没有真正玩过它。我认为它的目的是记录与“熊猫”模式类似的东西。

要回答您的问题,我们需要知道的最后一件事是所有 pyarrow 对象都是不可变的。所以没有办法简单地向模式添加字段。Pyarrow 确实具有模式实用程序方法with_metadata,该方法返回模式对象的克隆,但带有您自己的元数据,但这会替换现有的元数据并且不会附加到它。在 Table 对象上也有实验方法,replace_schema_metadata但这也替换而不更新。所以如果你想保留现有的元数据,你必须做更多的工作。把这一切放在一起,我们得到......

custom_metadata = {'Sample Number': '12', 'Date Obtained': 'Tuesday'}
existing_metadata = table.schema.metadata
merged_metadata = { **custom_metadata, **existing_metadata }
fixed_table = table.replace_schema_metadata(merged_metadata)
Run Code Online (Sandbox Code Playgroud)

一旦这个表被保存为一个文件,实木复合地板将包括键/值元数据字段(在文件级)Sample NumberDate Obtained

另外,请注意replace_schema_metadatawith_metadata方法可以接受常规的python 字符串(如我的示例中所示)。但是,它会将这些转换为“b 字符串”,因此如果您想访问架构中的字段,则必须使用“b 字符串”。例如,如果您刚刚阅读了一张表格并想获得您必须使用的样本编号,table.schema.metadata[b'Sample Number']并且table.schema.metadats['Sample Number']会给您一个KeyError.

当您开始使用它时,您可能会意识到经常必须Sample Number来回映射到整数是一种痛苦。此外,如果您的元数据在您的应用程序中表示为一个大型嵌套对象,则将此对象映射到字符串/字符串对的集合可能会很痛苦。此外,不断记住“b string”键是一种痛苦。解决方案是做与熊猫模式相同的事情。首先将元数据转换为 JSON 对象。然后将 JSON 对象转换为“b 字符串”。

custom_metadata_json = {'Sample Number': 12, 'Date Obtained': 'Tuesday'}
custom_metadata_bytes = json.dumps(custom_metadata_json).encode('utf8')
existing_metadata = table.schema.metadata
merged_metadata = { **{'Record Metadata': custom_metadata_bytes}, **existing_metadata }
Run Code Online (Sandbox Code Playgroud)

现在,您可以拥有任意数量的元数据字段,以您想要的任何方式嵌套,使用任何标准 JSON 类型,并且它们都将被命名为单个键/值对(在本例中名为“记录元数据”)。