我正在编写一个python-3.10程序,用于预测大量对象的各种属性的时间序列。我目前选择的用于在代码内部收集结果然后写入文件的数据结构是数组的字典的嵌套字典。例如,对于具有3个属性的时间序列的两个对象:
properties = {'obj1':{'time':np.arange(10),'x':np.random.randn(10),'vx':np.random.randn(10)},
'obj2': {'time':np.arange(15),'x':np.random.randn(15),'vx':np.random.randn(15)}}
我喜欢这种嵌套字典格式的原因是因为它易于访问——外部键是对象名称,内部键是属性名称。每个内部键对应的元素是numpy数组,以时间函数的形式给出某些属性的值。我的实际代码生成了一个包含~100,000个对象(外部键)的字典,每个对象有~100个属性(内部键),记录了~1000次(numpy float数组)。
我注意到,当我在我自己的巨大的属性字典(或它的子集)上做np.savez('filename.npz',**properties)
时,它需要一段时间,输出文件大小是几个GB(可能是因为np。Savez在底层调用pickle(因为我的嵌套字典不是数组)。
是否有更有效的数据结构广泛适用于我的用例?是否值得从我的嵌套字典切换到pandas dataframe、numpy ndarray或记录数组,或者某种类表对象的列表?如果能够以二进制输出格式保存/加载文件就好了,这种格式保留了从对象名称到它们的dict/array/table/dataframe属性的映射,当然还有每个属性时间序列数组的名称。
让我们看看您的obj2
值,一个字典:
In [307]: dd={'time':np.arange(15),'x':np.random.randn(15),'vx':np.random.randn(15)}
In [308]: dd
Out[308]:
{'time': array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]),
'x': array([-0.48197915, 0.15597792, 0.44113401, 1.38062753, -1.21273378,
-1.27120008, 1.53072667, 1.9799255 , 0.13647925, -1.37056793,
-2.06470784, 0.92314969, 0.30885371, 0.64860014, 1.30273519]),
'vx': array([-1.60228105, -1.49163002, -1.17061046, -0.09267467, -0.94133092,
1.86391024, 1.006901 , -0.16168439, 1.5180135 , -1.16436363,
-0.20254291, -1.60280149, -1.91749387, 0.25366602, -1.61993012])}
很容易创建一个数据框架:
In [309]: df = pd.DataFrame(dd)
In [310]: df
Out[310]:
time x vx
0 0 -0.481979 -1.602281
1 1 0.155978 -1.491630
2 2 0.441134 -1.170610
3 3 1.380628 -0.092675
4 4 -1.212734 -0.941331
5 5 -1.271200 1.863910
6 6 1.530727 1.006901
7 7 1.979926 -0.161684
8 8 0.136479 1.518014
9 9 -1.370568 -1.164364
10 10 -2.064708 -0.202543
11 11 0.923150 -1.602801
12 12 0.308854 -1.917494
13 13 0.648600 0.253666
14 14 1.302735 -1.619930
我们也可以从那个框架中创建结构化数组。我也可以直接从字典中创建数组,定义相同的复合dtype。但既然我已经有了框架,我就走这条路。结构化数组和重数组之间的区别很小。
In [312]: arr = df.to_records()
In [313]: arr
Out[313]:
rec.array([( 0, 0, -0.48197915, -1.60228105),
( 1, 1, 0.15597792, -1.49163002),
( 2, 2, 0.44113401, -1.17061046),
( 3, 3, 1.38062753, -0.09267467),
( 4, 4, -1.21273378, -0.94133092),
( 5, 5, -1.27120008, 1.86391024),
( 6, 6, 1.53072667, 1.006901 ),
( 7, 7, 1.9799255 , -0.16168439),
( 8, 8, 0.13647925, 1.5180135 ),
( 9, 9, -1.37056793, -1.16436363),
(10, 10, -2.06470784, -0.20254291),
(11, 11, 0.92314969, -1.60280149),
(12, 12, 0.30885371, -1.91749387),
(13, 13, 0.64860014, 0.25366602),
(14, 14, 1.30273519, -1.61993012)],
dtype=[('index', '<i8'), ('time', '<i4'), ('x', '<f8'), ('vx', '<f8')])
现在让我们比较pickle字符串:
In [314]: import pickle
In [315]: len(pickle.dumps(dd))
Out[315]: 561
In [316]: len(pickle.dumps(df)) # df.to_pickle makes a 1079 byte file
Out[316]: 1052
In [317]: len(pickle.dumps(arr)) # arr.nbytes is 420
Out[317]: 738 # np.save writes a 612 byte file
和其他编码-一个列表:
In [318]: alist = list(dd.items())
In [319]: alist
Out[319]:
[('time', array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])),
('x',
array([-0.48197915, 0.15597792, 0.44113401, 1.38062753, -1.21273378,
-1.27120008, 1.53072667, 1.9799255 , 0.13647925, -1.37056793,
-2.06470784, 0.92314969, 0.30885371, 0.64860014, 1.30273519])),
('vx',
array([-1.60228105, -1.49163002, -1.17061046, -0.09267467, -0.94133092,
1.86391024, 1.006901 , -0.16168439, 1.5180135 , -1.16436363,
-0.20254291, -1.60280149, -1.91749387, 0.25366602, -1.61993012]))]
In [320]: len(pickle.dumps(alist))
Out[320]: 567