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

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

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

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

我现在正试图在Python 3.6中提出类似的功能。到目前为止,我已经在线查看了一些帖子,但这些帖子并没有完全解决我想做的事情。

一些参考文章包括:

  • 保留磁盘上的numpy数组的最佳方法

  • 将pandas数据框保存为pickle和csv有什么区别?

  • 在笔记本中上传大型csv文件以使用python pandas的最快方法是什么?

  • 如何查看npz文件中的数据对象内容?

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

例如:

1
2
3
4
5
6
7
8
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的文件中?

我特别感兴趣的是,您可以通过示例了解您可能提出的任何建议的特殊利弊。依赖性越少越好。


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

优点:可移植(可在Python之外读取),本机压缩,内存不足功能,元数据支持。

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

根据我的经验,为了性能和可移植性,请避免pytables / HDFStore来存储数字数据。您可以使用h5py提供的直观界面。

存储一个数组

1
2
3
4
5
6
7
8
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安装可能无法提供其他压缩过滤器。

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

添加一些属性

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

存储字典

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

内存不足

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

没有什么可以替代阅读h5py文档,它暴露了大多数C API,但您应该从上面看到有很大的灵活性。


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

将数据帧存储到h5时,您可以选择存储元数据字典,这可以是您自己的注释,也可以是不需要存储在数据帧中的实际元数据(我也使用它来设置标记,例如{'is_agl':True,'scale_factor':100,'already_corrected':False等。}。在这方面,使用numpy数组和数据帧之间没有区别。对于完整的解决方案,请参阅我原来的问题和解决方案在这


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

要在ndarray中包含元数据,文档中有一个示例。
您基本上必须子类化ndarray并添加字段infometadata或其他。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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函数或使用其他解决方案。


jpp的答案非常全面,只是想提一下,因为pandas v22实木复合地板是非常方便和快速的选择,与csv几乎没有任何弊端(也许接受咖啡休息)。

读木地板

写拼花

在撰写本文时,您还需要

1
pip install pyarrow

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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')

然后产生一张桌子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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'}

要把它带回熊猫:

1
tab.to_pandas()

你说这个问题的原因是:

... 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进行研究和数据分析项目。它提供了如此丰富的环境来记录您的工作流程,并在您进行分析和研究时捕获细节,想法和想法。它可以轻松共享,并且可以进行演示。

这是一个有趣的Jupyter笔记本库,涵盖了许多行业和学科,以展示笔记本电脑的许多功能和用例。它可能会扩展您的视野,而不是试图设计一种方法来将简单的文本片段标记到您的数据中。


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

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

各种数据的小集合
当然,你的npz有效。实际上你正在做的非常类似于创建一个字典,其中包含你想要保存的所有内容并且腌制该字典。

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

就个人而言,我会说如果你不存储Numpy数组我会使用pickle,甚至实现一个快速的MyNotes类,它基本上是一个字典来保存其中的东西,你可能需要一些额外的功能。

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

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

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

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