开发者#

本节将重点介绍 pandas 的下游应用。

在 Apache Parquet 格式中存储 pandas DataFrame 对象#

Apache Parquet 格式在文件和列级别提供键值元数据,存储在 Parquet 文件的页脚中

5: optional list<KeyValue> key_value_metadata

其中 KeyValue

struct KeyValue {
  1: required string key
  2: optional string value
}

为了能够忠实地重建 pandas.DataFrame,我们将一个 pandas 元数据键存储在 FileMetaData 中,其值存储为

{'index_columns': [<descr0>, <descr1>, ...],
 'column_indexes': [<ci0>, <ci1>, ..., <ciN>],
 'columns': [<c0>, <c1>, ...],
 'pandas_version': $VERSION,
 'creator': {
   'library': $LIBRARY,
   'version': $LIBRARY_VERSION
 }}

字段 'index_columns' 中的“描述符”值 <descr0> 是字符串(指代列)或字典,其值如下所述。

以及 <c0>/<ci0> 等是包含每列元数据的字典,包括索引列。这具有 JSON 形式

{'name': column_name,
 'field_name': parquet_column_name,
 'pandas_type': pandas_type,
 'numpy_type': numpy_type,
 'metadata': metadata}

有关这些的详细规范,请参见下文。

索引元数据描述符#

RangeIndex 仅能作为元数据存储,无需序列化。其描述符格式如下

index = pd.RangeIndex(0, 10, 2)
{
    "kind": "range",
    "name": index.name,
    "start": index.start,
    "stop": index.stop,
    "step": index.step,
}

其他索引类型必须与 DataFrame 的其他列一起序列化为数据列。这些索引的元数据是一个字符串,指示数据列中字段的名称,例如 '__index_level_0__'

如果索引具有非 None 的 name 属性,并且没有其他列的名称与该值匹配,则可以使用 index.name 值作为描述符。否则(对于未命名索引以及名称与其他列名冲突的索引),应使用与 __index_level_\d+__ 模式匹配的消歧名称。在将命名索引作为数据列的情况下,name 属性始终如上所述存储在列描述符中。

列元数据#

pandas_type 是列的逻辑类型,是以下类型之一:

  • 布尔型:'bool'

  • 整型:'int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64'

  • 浮点型:'float16', 'float32', 'float64'

  • 日期和时间类型:'datetime', 'datetimetz', 'timedelta'

  • 字符串:'unicode', 'bytes'

  • 类别型:'categorical'

  • 其他 Python 对象:'object'

numpy_type 是列的物理存储类型,它是保存数据的基础 NumPy 数组的 str(dtype) 的结果。因此,对于 datetimetz,它是 datetime64[ns];对于类别型,它可以是任何受支持的整数类别类型。

除了以下类型,metadata 字段为 None

  • datetimetz: {'timezone': zone, 'unit': 'ns'},例如 {'timezone', 'America/New_York', 'unit': 'ns'}'unit' 是可选的,如果省略,则假定为纳秒。

  • categorical{'num_categories': K, 'ordered': is_ordered, 'type': $TYPE}

    • 这里 'type' 是可选的,可以是一个嵌套的 pandas 类型规范(但不能是类别型)

  • unicode: {'encoding': encoding}

    • 编码是可选的,如果未提供,则默认为 UTF-8

  • object: {'encoding': encoding}。对象可以序列化并存储在 BYTE_ARRAY Parquet 列中。编码可以是以下之一:

    • 'pickle'

    • 'bson'

    • 'json'

  • timedelta: {'unit': 'ns'}'unit' 是可选的,如果省略,则假定为纳秒。此元数据可以完全省略。

对于这些类型之外的其他类型,'metadata' 键可以省略。如果键不存在,实现可以假定为 None

作为完整元数据的示例

{'index_columns': ['__index_level_0__'],
 'column_indexes': [
     {'name': None,
      'field_name': 'None',
      'pandas_type': 'unicode',
      'numpy_type': 'object',
      'metadata': {'encoding': 'UTF-8'}}
 ],
 'columns': [
     {'name': 'c0',
      'field_name': 'c0',
      'pandas_type': 'int8',
      'numpy_type': 'int8',
      'metadata': None},
     {'name': 'c1',
      'field_name': 'c1',
      'pandas_type': 'bytes',
      'numpy_type': 'object',
      'metadata': None},
     {'name': 'c2',
      'field_name': 'c2',
      'pandas_type': 'categorical',
      'numpy_type': 'int16',
      'metadata': {'num_categories': 1000, 'ordered': False}},
     {'name': 'c3',
      'field_name': 'c3',
      'pandas_type': 'datetimetz',
      'numpy_type': 'datetime64[ns]',
      'metadata': {'timezone': 'America/Los_Angeles'}},
     {'name': 'c4',
      'field_name': 'c4',
      'pandas_type': 'object',
      'numpy_type': 'object',
      'metadata': {'encoding': 'pickle'}},
     {'name': None,
      'field_name': '__index_level_0__',
      'pandas_type': 'int64',
      'numpy_type': 'int64',
      'metadata': None}
 ],
 'pandas_version': '1.4.0',
 'creator': {
   'library': 'pyarrow',
   'version': '0.13.0'
 }}