我很惊讶地发现,如果使用numpy.savez
将相同的numpy对象保存到文件,则创建的文件不具有确定性。
例如,
import numpy
x = numpy.random.rand(1000, 1000)
numpy.savez('foo.npz', x)
numpy.savez('bar.npz', x)
然后
md5sum foo.npz bar.npz
d1b8b7d2000055b8bf62dddc4a5c77b5 foo.npz
1c6e13bb9efca3ec144e81b88b6cdc75 bar.npz
阅读this它看起来与npz zip文件中的时间戳有关。
出于测试目的,我想验证我的代码创建的数据文件是否相同。我通常使用pickle文件的校验和来执行此操作,例如
import cPickle as pickle
with open('foo.pkl', 'wb') as f:
pickle.dump(x, f, protocol=2)
with open('bar.pkl', 'wb') as f:
pickle.dump(x, f, protocol=2)
然后
md5sum foo.pkl bar.pkl
3139d9142d57bdde0970013f39b4854f foo.pkl
3139d9142d57bdde0970013f39b4854f bar.pkl
使用numpy.savez
做同样的事情是否有任何解决方法?
答案 0 :(得分:2)
如果您确实没有将关键字参数传递给np.savez
(即实际上只是序列化您的数据,而不想以后根据键引用这些项),您可以将多个数组转储到同一个数据库中文件np.save
:
import numpy as np
import time
def mysavez(outfile,*args):
with open(outfile,'wb') as outf:
for arg in args:
np.save(outf,arg)
x = np.random.rand(1000,1000)
# control group
np.savez('foo.npz', *[x]*5)
time.sleep(2) # make sure there's a difference in timestamp
np.savez('bar.npz', *[x]*5)
# new one
mysavez('foo.nopz', *[x]*5)
time.sleep(2) # make sure there's a difference in timestamp
mysavez('bar.nopz', *[x]*5)
生成的新文件具有相同的哈希值,它们甚至具有与原始文件完全相同的大小:
$ md5sum foo.npz bar.npz
4d21c47903b4ffab945f619ad5b6f471 foo.npz
f9af863c6178765d6dc32a5fa2f63623 bar.npz
$ md5sum foo.nopz bar.nopz
c8504f0d8cc53956100912efb02573b0 foo.nopz
c8504f0d8cc53956100912efb02573b0 bar.nopz
$ du {foo,bar}.n*pz
39064 foo.nopz
39064 foo.npz
39064 bar.nopz
39064 bar.npz
只要您从文件中顺序读取变量,就不会发现功能差异。当然,你需要一个myload
来生成保存的数组,直到它们全部消失(或者更加奇特并保存一个初始整数标题,告诉你保存到文件中的数组的数量)。这种方法无疑是愚蠢的,但它可能会根据您的确切用例来削减它。
如果你做想要使用键访问你保存的变量,你仍然可以考虑编写一个辅助函数来测试哪个读取“生产”.npz
文件,迭代它们的命令键,使用上面的mysavez
函数按顺序保存它们,然后计算这些“扁平”pickle文件的哈希值。当然,您可能不需要np.save
:pickle
可以为您做同样的事情(尽管cpickle
might not)。
答案 1 :(得分:0)
这可以通过numpy记录/结构来实现,唯一的限制是每个数组的第一维必须相同。
下面是用于使其与numpy记录一起使用的代码段,因此您甚至可以使用kwargs。
import numpy as np
import time
import hashlib
def md5(fname):
hash_md5 = hashlib.md5()
with open(fname, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
def mysavez(outfile,**kwargs):
# sort the keys
_sorted_keys = list(kwargs.keys())
_sorted_keys.sort()
# len of first element ... and check if it is same for all elements
_len = kwargs[_sorted_keys[0]].shape[0]
for k, v in kwargs.items():
if v.shape[0] != _len:
raise Exception(
f"While creating numpy struct all arrays must have same length."
f"invalid shape {v.shape} for item {k}"
)
# create numpy record buffer
npy_record = np.zeros(
_len,
dtype=[
(k, kwargs[k].dtype, kwargs[k].shape[1:])
for k in _sorted_keys
],
)
# fill up the elements
for k, v in kwargs.items():
npy_record[k] = v
# save
with open(outfile, 'wb') as outf:
np.save(outf, npy_record)
a = np.random.rand(1000,1000)
b = np.random.rand(1000,1000)
# new one
mysavez('foo.nopz', a=a, b=b)
time.sleep(2) # make sure there's a difference in timestamp
mysavez('bar.nopz', a=a, b=b)
# check hash
print('foo.nopz', md5('foo.nopz'))
print('bar.nopz', md5('bar.nopz'))
输出:
foo.nopz b7759b6a60f135c393954e530fb5604b
bar.nopz b7759b6a60f135c393954e530fb5604b