【问题标题】:Saving in a file an array or DataFrame together with other information将数组或 DataFrame 与其他信息一起保存在文件中
【发布时间】:2018-09-19 06:42:57
【问题描述】:

统计软件Stata 允许将短文本sn-ps 保存在数据集中。这可以使用notes 和/或characteristics 完成。

这对我来说很有价值,因为它允许我保存各种信息,从提醒和待办事项列表到有关我如何生成数据的信息,甚至是特定变量的估计方法曾是。

我现在正在尝试在 Python 3.6 中提供类似的功能。到目前为止,我已经在网上查看并查阅了许多帖子,但是这些帖子并没有完全解决我想要做的事情。

一些参考帖子包括:

对于一个小的NumPy 数组,我得出结论,函数numpy.savez()dictionary 的组合可以将所有相关信息充分存储在一个文件中。

例如:

a = np.array([[2,4],[6,8],[10,12]])
d = {"first": 1, "second": "two", "third": 3}

np.savez(whatever_name.npz, a=a, d=d)
data = np.load(whatever_name.npz)

arr = data['a']
dic = data['d'].tolist()

但是,问题仍然存在:

是否有更好的方法可以将其他信息片段合并到包含 NumPy 数组或(大)Pandas DataFrame 的文件中?

我特别有兴趣了解您可能通过示例提出的任何建议的特定优点缺点。依赖项越少越好。

【问题讨论】:

    标签: python pandas numpy stata hdf5


    【解决方案1】:

    有很多选择。我将只讨论 HDF5,因为我有使用这种格式的经验。

    优势:便携(可以在 Python 之外读取)、原生压缩、内存不足功能、元数据支持。

    缺点:依赖单一的低级C API,作为单个文件可能会损坏数据,删除数据不会自动减小大小。

    根据我的经验,为了性能和便携性,避免 pyTables / HDFStore 存储数字数据。您可以改用h5py 提供的直观界面。

    存储一个数组

    import h5py, numpy as np
    
    arr = np.random.randint(0, 10, (1000, 1000))
    
    f = h5py.File('file.h5', 'w', libver='latest')  # use 'latest' for performance
    
    dset = f.create_dataset('array', shape=(1000, 1000), data=arr, chunks=(100, 100),
                            compression='gzip', compression_opts=9)
    

    压缩和分块

    有很多压缩选择,例如blosclzf 分别是压缩和解压缩性能的不错选择。注意gzip 是原生的;默认情况下,您的 HDF5 安装可能不附带其他压缩过滤器。

    分块是另一种选择,当它与您从内存中读取数据的方式保持一致时,可以显着提高性能。

    添加一些属性

    dset.attrs['Description'] = 'Some text snippet'
    dset.attrs['RowIndexArray'] = np.arange(1000)
    

    存储字典

    for k, v in d.items():
        f.create_dataset('dictgroup/'+str(k), data=v)
    

    内存不足访问

    dictionary = f['dictgroup']
    res = dictionary['my_key']
    

    阅读 h5py documentation 是无可替代的,它公开了大部分 C API,但您应该从上面看到有很大的灵活性。

    【讨论】:

    • 几点说明: 1) blosc 压缩数据会快得多。 2)如果创建了许多 dsets 并且向后兼容不是一个大问题,则可以使用 f = h5py.File('name.hdf5', libver='latest') 。这应该会大大提高速度(小,但很多 dsets)
    • @max9111, (1) 是 blosc 在 Python 之外本机/可移植,例如您可以在 HDFView / 其他库中查看吗? (2) 谢谢,我会用libver 参数更新。
    • Blosc 过滤器可以安装在系统范围内。 github.com/Blosc/hdf5-blosc 我会提到它,因为它可以比 gzip 快一个数量级,但你是对的,它不是由默认的 HDF5 安装安装的。
    • @max9111 我不熟悉 blosc 过滤器。它只是应用了一些过滤,还是只是一个压缩库?在 pandas.DataFrame.to_hdf() 中有一个 complib 下的“blosc”选项(即内置,不需要额外的包),这只是压缩。一样的吗?
    • 是的,它是一个压缩包和随机过滤器。为了比较,您可以使用 gzip 尝试以下示例并比较性能。 stackoverflow.com/questions/48672130/…(在这个例子中,我使用 pytables 注册了 blosc 过滤器,但这也应该适用于全局安装的 blosc 过滤器,在上面的评论中提到)
    【解决方案2】:

    一种实用的方法是将元数据直接嵌入到 Numpy 数组中。优点是,如您所愿,没有额外的依赖,并且在代码中使用非常简单。 但是,这并不能完全回答您的问题,因为您仍然需要一种机制来保存数据,我建议您使用jpp 使用 HDF5 的解决方案。

    要在ndarray 中包含元数据,the documentation 中有一个示例。 您基本上必须继承 ndarray 并添加一个字段 infometadata 或其他任何内容。

    它会给出(来自上面链接的代码)

    import numpy as np
    
    class ArrayWithInfo(np.ndarray):
    
        def __new__(cls, input_array, info=None):
            # Input array is an already formed ndarray instance
            # We first cast to be our class type
            obj = np.asarray(input_array).view(cls)
            # add the new attribute to the created instance
            obj.info = info
            # Finally, we must return the newly created object:
            return obj
    
        def __array_finalize__(self, obj):
            # see InfoArray.__array_finalize__ for comments
            if obj is None: return
            self.info = getattr(obj, 'info', None)
    

    要通过numpy 保存数据,您需要重载write 函数或使用其他解决方案。

    【讨论】:

      【解决方案3】:

      这是一个有趣的问题,虽然我认为非常开放。

      文本片段
      对于具有文字注释的文本 sn-ps(例如,不是代码而不是数据),我真的不知道您的用例是什么,但我不明白为什么我会偏离使用通常的 with open() as f: ...

      各种数据片段的小集合
      当然,您的 npz 有效。实际上,您所做的与创建一个包含您想要保存的所有内容的字典并腌制该字典非常相似。

      请参阅here,了解有关 pickle 和 npz 之间差异的讨论(但主要是,npz 针对 numpy 数组进行了优化)。

      就个人而言,我会说如果你不存储 Numpy 数组,我会使用 pickle,甚至实现一个快速的 MyNotes 类,它基本上是一个字典,用于在其中保存内容,还有一些你可能想要的附加功能。

      大型对象的集合
      对于我在 HDF5 格式之前使用过的非常大的 np.arrays 或数据帧。好在它已经内置在 pandas 中,你可以直接df.to_hdf5()。它确实需要在pytables 下 - 使用 pip 或 conda 安装应该相当轻松 - 但直接使用 pytables 可能会更痛苦。

      同样,这个想法非常相似:您正在创建一个 HDFStore,它几乎是一个大字典,您可以在其中存储(几乎所有)对象。好处是该格式通过利用相似值的重复以更智能的方式利用空间。当我使用它来存储大约 2GB 的数据帧时,它能够将它减少几乎一个完整的数量级(~250MB)。

      最后一位玩家:feather
      Feather 是 Wes McKinney 和 Hadley Wickham 在 Apache Arrow 框架之上创建的一个项目,用于以二进制格式保存数据,即语言不可知论(因此您可以从 R 和 Python 中阅读)。但是,它仍在开发中,上次我检查他们不鼓励将其用于长期存储(因为规范可能会在未来版本中更改),而不仅仅是将其用于 R 和 Python 之间的通信。

      他们俩都刚刚在几周前推出了Ursalabs,这将继续发展这一举措和类似举措。

      【讨论】:

        【解决方案4】:

        你陈述了这个问题的原因:

        ...它允许我保存一个 各种信息,从提醒和待办事项列表到 关于我如何生成数据的信息,甚至是什么 特定变量的估计方法是

        我可以提出一个不同于 Stata 提供的范式吗?注释和特征似乎非常有限,仅限于文本。相反,您应该将Jupyter Notebook 用于您的研究和数据分析项目。它提供了如此丰富的环境来记录您的工作流程并在您进行分析和研究时捕捉细节、想法和想法。它可以轻松共享,并且可以进行演示。

        这里是a gallery of interesting Jupyter Notebooks,跨越许多行业和学科,展示了笔记本电脑的许多功能和用例。它可能会扩展您的视野,而不仅仅是尝试设计一种将简单的文本 sn-ps 标记到您的数据的方法。

        【讨论】:

          【解决方案5】:

          我同意 JPP 的观点,即 hdf5 存储在这里是一个不错的选择。他的解决方案和我的不同之处在于我使用 Pandas 数据帧而不是 numpy 数组。我更喜欢数据框,因为它允许混合类型、多级索引(甚至日期时间索引,这对我的工作非常重要)和列标签,这有助于我记住不同数据集的组织方式。此外,Pandas 提供了大量内置功能(很像 numpy)。使用 Pandas 的另一个好处是它内置了一个 hdf 创建器(即 pandas.DataFrame.to_hdf),我觉得这很方便

          将数据帧存储到 h5 时,您还可以选择存储元数据字典,这可以是您自己的注释,也可以是不需要存储在数据帧中的实际元数据(我使用它来设置标志同样,例如 {'is_agl': True, 'scale_factor': 100, 'already_corrected': False, etc.}。在这方面,使用 numpy 数组和数据框没有区别。完整的解决方案见@ 987654321@

          【讨论】:

            【解决方案6】:

            jpp 的回答非常全面,只是想提一下,从 pandas v22 parquet 开始,它是非常方便快捷的选择,与 csv 相比几乎没有缺点(接受也许是茶歇)。

            read parquet

            write parquet

            在撰写本文时,您还需要

            pip install pyarrow
            

            在添加信息方面,您拥有附加到数据的元数据

            import pyarrow as pa
            import pyarrow.parquet as pq
            import pandas as pd
            import numpy as np
            
            df = pd.DataFrame(np.random.normal(size=(1000, 10)))
            
            tab = pa.Table.from_pandas(df)
            
            tab = tab.replace_schema_metadata({'here' : 'it is'})
            
            pq.write_table(tab, 'where_is_it.parq')
            
            pq.read_table('where_is_it.parq')
            然后产生一个表
            Pyarrow table
            0: double
            1: double
            2: double
            3: double
            4: double
            5: double
            6: double
            7: double
            8: double
            9: double
            __index_level_0__: int64
            metadata
            --------
            {b'here': b'it is'}
            

            要将这个返回给 pandas:

            tab.to_pandas()
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-04-04
              • 2012-07-02
              • 2019-03-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-02-11
              • 1970-01-01
              相关资源
              最近更新 更多