如何用pyarrow编写Parquet元数据?

时间:2018-08-31 21:15:29

标签: python parquet pyarrow

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

镶木地板似乎支持file-wide metadata,但是我找不到如何通过pyarrow编写它。我能找到的最接近的东西是how to write row-group metadata,但这似乎有点过头了,因为文件中所有行组的元数据都相同。

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

2 个答案:

答案 0 :(得分:3)

此示例显示如何使用PyArrow使用文件元数据和列元数据创建Parquet文件。

假设您具有以下CSV数据:

movie,release_year
three idiots,2009
her,2013

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

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"})

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

t2 = table.cast(my_schema)

pq.write_table(t2, 'movies.parquet')

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

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

s.metadata # => {b'great_music': b'reggaeton'}
s.metadata[b'great_music'] # => b'reggaeton'

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

parquet_file.schema.field('release_year').metadata[b'portuguese'] # => b'ano'

有关更多信息,请参见this blog post

答案 1 :(得分:2)

Pyarrow将文件范围的元数据映射到名为field in the table's schema的元数据。遗憾的是,目前尚无文档。

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

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

我们需要回答的最后一件事是所有pyarrow对象都是不可变的。因此,无法将字段简单地添加到架构中。 Pyarrow确实具有模式实用程序方法with_metadata,该方法返回模式对象的克隆,但是具有您自己的元数据,但是它将替换现有的元数据,并且不会追加到现有的元数据中。表对象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)

将此表另存为实木复合地板文件后,它将包含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字符串”键是很痛苦的。解决方案是做熊猫模式所做的相同的事情。首先将您的元数据转换为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 }

现在,您可以使用任意标准JSON类型以所需的任何方式嵌套任意数量的元数据字段,并且将所有这些元数据字段命名为单个键/值对(在本例中为“ Record Metadata” “)。