Pandas/PyArrow中如何基于自然键将分类映射存入Parquet元数据
实现方案
完全可以实现,不需要额外存储冗余的描述列,核心通过Parquet原生的字典编码分类类型+自定义字段元数据即可满足需求。
- 分类存储用PyArrow的
pa.dictionary类型:这是Parquet原生支持的字典编码格式,内部仅用整数索引做存储压缩,上层读写时暴露的逻辑值始终是你传入的原始自然字符串编码,不会出现你不想要的0/1数值编码问题,存储效率和普通分类类型完全一致。 - 编码-描述映射存字段元数据:PyArrow的字段定义支持写入自定义键值对元数据,元数据会随Parquet文件一同持久化,不需要额外加冗余列,也不需要单独维护外部映射配置。
完整写入实现代码
import pandas as pd import pyarrow as pa import pyarrow.parquet as pq import json df = pd.DataFrame( { 'Name': ['John', 'James', 'Jack', 'Jonathan', 'Jerry', 'Joseph'], 'EmployeeCategory': ["SR","C","M","SR","M",pd.NA] }) employeeCategoryMap = { "SR": "Senior", "C": "Consultant", "M": "Manager" } # 定义分类类型:小基数分类用int8做索引足够,值类型设为字符串保留原始自然编码 cat_type = pa.dictionary(index_type=pa.int8(), value_type=pa.string(), ordered=False) # 构造Schema,给分类字段加自定义元数据存储描述映射 schema = pa.schema([ pa.field('Name', pa.string()), pa.field( 'EmployeeCategory', cat_type, metadata={ b'category:description_map': json.dumps(employeeCategoryMap).encode('utf-8') } ) ]) tbl = pa.Table.from_pandas(df, schema) pq.write_table(tbl, 'test.parquet')
读取与使用方式
读取文件时可以直接拿到原始编码值和元数据里的映射关系,不需要额外解析:
# 读取Parquet文件 read_tbl = pq.read_table('test.parquet') # 验证取值:返回的就是原始自然编码,无0/1数值 print(read_tbl['EmployeeCategory'].to_pylist()) # 输出: ['SR', 'C', 'M', 'SR', 'M', None] # 从字段元数据中取出描述映射 cat_metadata = read_tbl.schema.field('EmployeeCategory').metadata desc_map = json.loads(cat_metadata[b'category:description_map'].decode('utf-8')) print(desc_map) # 输出: {'SR': 'Senior', 'C': 'Consultant', 'M': 'Manager'} # 需要转pandas分类类型时,直接用映射构造即可 pdf = read_tbl.to_pandas() pdf['EmployeeCategory'] = pd.Categorical( pdf['EmployeeCategory'], categories=list(desc_map.keys()) )
自定义元数据的键建议加专属前缀(比如示例里的
category:前缀),避免和PyArrow、Pandas自动生成的内置元数据键冲突。元数据只接受字节类型值,所以示例中用JSON序列化映射表后再做UTF-8编码,读取时反向解码即可。
这种实现完全匹配你的需求:虽然没有采用Kimball方法论推荐的代理键模式,但全程保留了原始自然编码,映射关系随文件存储不会丢失语义,存储效率也和普通分类列一致。写入的Parquet文件对其他兼容Parquet字典编码的计算引擎也友好,读取时不会出现编码错乱的问题。
内容的提问来源于stack exchange,提问作者zaidwaqi
相关产品推荐
相关产品推荐

