【问题标题】:HDF5 string serialization details in pandas?熊猫中的 HDF5 字符串序列化详细信息?
【发布时间】:2013-06-07 20:17:54
【问题描述】:

我是 Saddle (saddle.github.io) 的作者,它提供的功能在精神上类似于 pandas(但在 JVM 上的 Scala 中)。我试图确保 pandas 的 DataFrame 的 HDF5 序列化格式与 Saddle 的可互操作。我目前正在 Saddle 中实现字符串数组序列化。所以我的问题是 pandas DataFrame 如何序列化字符串。如果我在 pandas 中创建一个 HDF5 文件如下:

from pandas import *
h = HDFStore('tmp.h5')
f = DataFrame({0: [1,2,3], 1: ["a", "b", "c"], 2: [1.5, 2.5, 3.5]})
h.put("f1", f)
h.close()

h5dump 生成的 tmp.h5 文件,我看到字符串块 (block2_values) 存储为数据类型 H5T_VLEN 和属性

 ATTRIBUTE "CLASS" {
    DATATYPE  H5T_STRING {
          STRSIZE 8;
          STRPAD H5T_STR_NULLTERM;
          CSET H5T_CSET_ASCII;
          CTYPE H5T_C_S1;
       }
    DATASPACE  SCALAR
    DATA {
    (0): "VLARRAY"
    }
 }

这暗示了一个 ASCII 字符集;但是,我看到的编码字节似乎与 ASCII 不对应(即“a”、“b”、“c”)。另外,我很好奇 STRSIZE 8 是从哪里来的。任何人都可以阐明通过 pandas -> pytables -> hdf5 发生的字符串序列化的实现细节吗? (我也很高兴有任何指向 pandas/pytables 中的代码的指针,我可以开始深入挖掘自己:)

【问题讨论】:

    标签: pandas saddle


    【解决方案1】:

    您选择了一个表面上看起来很简单,但实际上在幕后相当复杂的例子。这最终存储了 3 个不同的数据块(每个 dtype 1 个),并且每个存储和索引以及数据。

    您存储的对象是我所说的Storer 格式,这意味着 numpy 数组是一次性写入的,因此一旦写入它们就无法更改。在此处查看文档:http://pandas.pydata.org/pandas-docs/dev/io.html#hdf5-pytables

    PyTables 文档在这里:http://pytables.github.io/usersguide/libref/declarative_classes.html#the-atom-class-and-its-descendants

    不幸的是,这些字符串以这种特殊的存储格式存储为 python pickle,所以我不知道您是否可以跨平台解码它们。

    您将更轻松地阅读 Table 对象,该对象使用更基本的类型存储,易于导出(例如,文档中有一节介绍导出到 R)。

    尝试阅读这种格式:

    In [2]: df = DataFrame({0: [1,2,3], 1: ["a", "b", "c"], 2: [1.5, 2.5, 3.5]})
    
    In [4]: h = pd.HDFStore('tmp.h5')
    
    In [6]: h.put('df',df, table=True)
    
    In [7]: h.close()
    

    使用 PyTables ptdump -avd tmp.h5 实用程序,这会产生以下结果。如果您正在阅读

    / (RootGroup) ''
      /._v_attrs (AttributeSet), 4 attributes:
       [CLASS := 'GROUP',
        PYTABLES_FORMAT_VERSION := '2.0',
        TITLE := '',
        VERSION := '1.0']
    /df (Group) ''
      /df._v_attrs (AttributeSet), 12 attributes:
       [CLASS := 'GROUP',
        TITLE := '',
        VERSION := '1.0',
        data_columns := [],
        index_cols := [(0, 'index')],
        levels := 1,
        nan_rep := b'nan',
        non_index_axes := b"(lp1\n(I1\n(lp2\ncnumpy.core.multiarray\nscalar\np3\n(cnumpy\ndtype\np4\n(S'i8'\nI0\nI1\ntRp5\n(I3\nS'<'\nNNNI-1\nI-1\nI0\ntbS'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\ntRp6\nag3\n(g5\nS'\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\ntRp7\nag3\n(g5\nS'\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\ntRp8\natp9\na.",
        pandas_type := b'frame_table',
        pandas_version := b'0.10.1',
        table_type := b'appendable_frame',
        values_cols := ['values_block_0', 'values_block_1', 'values_block_2']]
    /df/table (Table(3,)) ''
      description := {
      "index": Int64Col(shape=(), dflt=0, pos=0),
      "values_block_0": Float64Col(shape=(1,), dflt=0.0, pos=1),
      "values_block_1": Int64Col(shape=(1,), dflt=0, pos=2),
      "values_block_2": StringCol(itemsize=1, shape=(1,), dflt=b'', pos=3)}
      byteorder := 'little'
      chunkshape := (2621,)
      autoindex := True
      colindexes := {
        "index": Index(6, medium, shuffle, zlib(1)).is_csi=False}
      /df/table._v_attrs (AttributeSet), 19 attributes:
       [CLASS := 'TABLE',
        FIELD_0_FILL := 0,
        FIELD_0_NAME := 'index',
        FIELD_1_FILL := 0.0,
        FIELD_1_NAME := 'values_block_0',
        FIELD_2_FILL := 0,
        FIELD_2_NAME := 'values_block_1',
        FIELD_3_FILL := b'',
        FIELD_3_NAME := 'values_block_2',
        NROWS := 3,
        TITLE := '',
        VERSION := '2.6',
        index_kind := b'integer',
        values_block_0_dtype := b'float64',
        values_block_0_kind := b"(lp1\ncnumpy.core.multiarray\nscalar\np2\n(cnumpy\ndtype\np3\n(S'i8'\nI0\nI1\ntRp4\n(I3\nS'<'\nNNNI-1\nI-1\nI0\ntbS'\\x02\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\ntRp5\na.",
        values_block_1_dtype := b'int64',
        values_block_1_kind := b"(lp1\ncnumpy.core.multiarray\nscalar\np2\n(cnumpy\ndtype\np3\n(S'i8'\nI0\nI1\ntRp4\n(I3\nS'<'\nNNNI-1\nI-1\nI0\ntbS'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\ntRp5\na.",
        values_block_2_dtype := b'string8',
        values_block_2_kind := b"(lp1\ncnumpy.core.multiarray\nscalar\np2\n(cnumpy\ndtype\np3\n(S'i8'\nI0\nI1\ntRp4\n(I3\nS'<'\nNNNI-1\nI-1\nI0\ntbS'\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\ntRp5\na."]
      Data dump:
    [0] (0, [1.5], [1], [b'a'])
    [1] (1, [2.5], [2], [b'b'])
    [2] (2, [3.5], [3], [b'c'])
    

    最好离线与我联系以进一步讨论。

    【讨论】:

    • “不幸的是,这些字符串以这种特定格式存储为 python 泡菜......” 会保持这种状态,还是有计划在这里使用 msgpack。我之所以问,是因为我已经看到了您正在处理的 msgpack 分支。
    • msgpack 是一种独立的序列化格式,与 PyTables 无关。我指的泡菜是 PyTables 保存可变长度字符串的一种内联方式。
    • 我认为这是熊猫特有的,不知道这是 pytables 行为。如果 pytables 使用 msgpack,其他语言读取数据会更容易,但显然它们的目标是 python。
    • 在大多数情况下,PyTables 是完全跨平台的,但这种特殊情况并非如此,因为它使用可变长度的字符串;我上面的表格示例是跨平台的,因为它使用固定长度的字符串。
    • 是的,非常感谢这个例子。但是,表格格式的读/写速度比“熊猫”格式要慢得多。我想像往常一样,没有简单的解决方案能带来所有好处。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多