听起来您对 JSON 的唯一真正问题是您编码 NumPy 数组(和 Pandas 表)的方式。JSON 不适合您的用例——不是因为它处理 NumPy 数据的速度很慢,而是因为它是一种基于文本的格式,而且您有很多数据更容易以非基于文本的格式进行编码。
因此,我将在下面向您展示解决所有 JSON 问题的方法……但我建议使用不同的格式。
两种主要的“二进制 JSON”格式BJSON和BSON旨在提供 JSON 的大部分优点(简单、安全、动态/无模式、可遍历等),同时还可以直接嵌入二进制数据。(在这种情况下,它们也是二进制而不是文本格式对您来说并不重要。)我相信Smile也是如此,但我从未使用过它。
这意味着,与 JSON 可以轻松挂钩任何可以简化为字符串、浮点数、列表和字典的方式一样,BJSON 和 BSON 可以轻松挂钩任何可以简化为字符串、浮点数、列表、字典的内容,和字节串。因此,当我展示如何将 NumPy 编码/解码为字符串时,同样的事情适用于字节字符串,但最后没有所有额外的步骤。
BJSON 和 BSON 的缺点是它们不是人类可读的,并且没有那么广泛的支持。
我不知道您当前是如何对数组进行编码的,但是从时间上看,我怀疑您正在使用该tolist
方法或类似的方法。那肯定会很慢,而且很大。如果您在任何地方存储除f8
值以外的任何内容,它甚至会丢失信息(因为 JSON 理解的唯一数字是 IEEE 双精度数)。解决方案是编码为字符串。
NumPy 有一种文本格式,它会更快,而且不会有损,但仍然可能比你想要的更慢和更大。
它还具有二进制格式,这很棒……但没有足够的信息来恢复您的原始数组。
所以,让我们看看有什么pickle
用途,你可以通过__reduce__
在任何对象上调用方法来看到:基本上,它是类型、形状、dtype、一些告诉 NumPy 如何解释原始数据的标志,然后是二进制格式原始数据。您实际上可以__reduce__
自己对数据进行编码——事实上,这样做可能是值得的。但是,为了便于说明,让我们做一些更简单的事情,并理解它只能在ndarray
.
def numpy_default(obj):
if isinstance(obj, np.ndarray):
return {'_npdata': obj.tostring(),
'_npdtype': obj.dtype.name,
'_npshape': obj.shape}
else:
return json.dumps(obj)
def dumps(obj):
return json.dumps(obj, default=numpy_default)
def numpy_hook(obj):
try:
data = obj['_npdata']
except AttributeError:
return obj
return np.fromstring(data, obj['_npdtype']).reshape(obj['_npshape'])
def loads(obj):
return json.loads(obj, object_hook=numpy_hook)
唯一的问题是它np.tostring
给了你'bytes'
对象,Python 3json
不知道如何处理。
如果您使用 BJSON 或 BSON 之类的东西,您可以在此处停止。但是对于 JSON,您需要字符串。
您可以通过使用映射每个单字节字符的任何编码(例如 Latin-1: change obj.tostring()
toobj.tostring().decode('latin-1')
和data = obj['_npdata']
to )“解码”字节来轻松解决这个问题,如果不巧的话data = obj['_npdata'].encode('latin-1')
。通过 UTF-8 编码伪造的 Latin-1 字符串会浪费一些空间,但这还不错。
不幸的是,Python 将使用 Unicode 转义序列对每个非 ASCII 字符进行编码。ensure_ascii=False
您可以通过设置转储和加载来关闭strict=False
它,但它仍会将控制字符编码为 6 字节序列。这会使随机数据的大小加倍,而且效果会更糟——例如,一个全零数组将大 6 倍!
曾经有一个技巧可以解决这个问题,但是在 3.3 中,它不起作用。您可以做的最好的事情是分叉或猴子补丁json
包,这样它就可以让您在给定时传递控制字符ensure_ascii=False
,您可以这样做:
json.encoder.ESCAPE = re.compile(r'"')
这很hacky,但它有效。
无论如何,希望这足以让你开始。