我使用pyarrow创建和分析具有生物信息的Parquet表,我需要存储一些元数据,例如数据来自哪个样本,它是如何获得和处理的。
Parquet似乎支持文件范围内的元数据,但我找不到如何通过pyarrow写入它。我能找到的最接近的东西是如何写入行组元数据,但这似乎有点过分,因为我的元数据对文件中的所有行组都是相同的。
有没有办法写文件范围的Parquet元数据与pyarrow?
PyArrow将文件范围的元数据映射到表架构中名为元数据的字段。遗憾的是,这里还没有留档。
Parquet元数据格式和Pyarrow元数据格式都将元数据表示为键/值对的集合
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)
一旦这个表被保存为一个组合文件,它将包括键/值元数据字段(在文件级别)为样本号
和日期获得
。
另外,请注意replace_schema_metadata
和with_metadata
方法可以接受常规python字符串(就像在我的示例中一样)。但是,它会将这些转换为“b字符串”,因此如果您想访问架构中的字段,则必须使用“b字符串”。例如,如果您刚刚读取了一个表并想要获取示例编号,则必须使用table. schema.元数据[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类型,它们都将被命名为单个键/值对(在本例中称为“记录元数据”)。
此示例说明如何使用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'