将数组或 DataFrame 与其他信息一起保存在文件中

Saving in a file an array or DataFrame together with other information

统计软件Stata allows short text snippets to be saved within a dataset. This is accomplished either using notes and/or 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 的文件中?

我特别想听听您对示例的任何建议的特定 优点缺点。依赖越少越好

这是一个有趣的问题,尽管我认为它非常开放。

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

各种数据的小集合
当然,您的 npz 有效。实际上,您所做的与创建一个字典非常相似,其中包含您要保存的所有内容并对该字典进行 pickle。

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

就我个人而言,如果你不存储 Numpy 数组,我会使用 pickle,甚至实现一个快速的 MyNotes class,它基本上是一个字典来保存内容,用您可能需要的一些附加功能。

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

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

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

他们都刚刚推出 Ursalabs,实际上就在几周前,这将继续发展这一计划和类似计划。

有很多选择。我将只讨论 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,但您应该从上面看到有很大的灵活性。

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

要在 ndarray 中包含元数据,the documentation 中有一个示例。 您基本上必须对 ndarray 进行子类化并添加一个字段 infometadata 或其他任何字段。

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

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 函数或使用其他解决方案。

你说的是这个问题的原因:

... it allows me to save a variety of information, ranging from reminders and to-do lists, to information about how i generated the data, or even what the estimation method for a particular variable was.

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

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

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

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

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')
然后产生 table

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()