如何在保留矩阵尺寸的同时序列化numpy数组?

blz*_*blz 36 python json numpy apache-storm

numpy.array.tostring似乎没有保留有关矩阵维度的信息(请参阅此问题),要求用户发出呼叫numpy.array.reshape.

有没有办法将numpy数组序列化为JSON格式,同时保留这些信息?

注意: 数组可能包含整数,浮点数或布尔值.期望转置数组是合理的.

注2: 这样做的目的是使用streamparse将numpy数组传递给Storm拓扑,以防这些信息最终变得相关.

use*_*ica 41

pickle.dumps或者numpy.save编码重建任意NumPy数组所需的所有信息,即使存在字节序问题,非连续数组或奇怪的元组dtypes.字节序问题可能是最重要的; 你不想array([1])突然变成array([16777216])因为你在大端机器上加载你的阵列.pickle可能是更方便的选择,虽然save有其自身的好处,在npy格式的基本原理.

pickle选项:

import pickle
a = # some NumPy array
serialized = pickle.dumps(a, protocol=0) # protocol 0 is printable ASCII
deserialized_a = pickle.loads(serialized)
Run Code Online (Sandbox Code Playgroud)

numpy.save使用二进制格式,它需要写入文件,但你可以通过以下方式解决StringIO:

a = # any NumPy array
memfile = StringIO.StringIO()
numpy.save(memfile, a)
memfile.seek(0)
serialized = json.dumps(memfile.read().decode('latin-1'))
# latin-1 maps byte n to unicode code point n
Run Code Online (Sandbox Code Playgroud)

并反序列化:

memfile = StringIO.StringIO()
memfile.write(json.loads(serialized).encode('latin-1'))
memfile.seek(0)
a = numpy.load(memfile)
Run Code Online (Sandbox Code Playgroud)

  • 在python 3中,我不得不用`io.BytesIO()`替换`StringIO.StringIO()`,如[这里暗示](http://stackoverflow.com/a/36187468/777285). (5认同)
  • 你能解释为什么包括`json.dumps(memfile.read().decode('latin-1'))` (3认同)
  • @FGreg:它将原始字节序列化为 JSON,因为提问者要求提供 JSON 输出。我不记得为什么我 * 没有 * 在 `pickle` 选项中添加类似的内容;它可能与字节串与 unicode 字符串问题有关。 (2认同)

dan*_*451 13

编辑:正如人们可以在问题的评论中读到这个解决方案处理"正常"numpy数组(浮点数,整数,布尔...)而不是多类型结构化数组.

用于序列化任何维度和数据类型的numpy数组的解决方案

据我所知,你不能简单地序列化任何数据类型和任何维度的numpy数组......但你可以将它的数据类型,维度和信息存储在列表表示中,然后使用JSON序列化它.

需要进口:

import json
import base64
Run Code Online (Sandbox Code Playgroud)

对于编码,您可以使用(nparray是任何数据类型和任何维度的一些numpy数组):

json.dumps([str(nparray.dtype), base64.b64encode(nparray), nparray.shape])
Run Code Online (Sandbox Code Playgroud)

在此之后,您将获得数据的JSON转储(字符串),其中包含其数据类型和形状的列表表示以及数据/内容base64编码.

解码这样做的工作(encStr是编码JSON字符串,从什么地方装):

# get the encoded json dump
enc = json.loads(encStr)

# build the numpy data type
dataType = numpy.dtype(enc[0])

# decode the base64 encoded numpy array data and create a new numpy array with this data & type
dataArray = numpy.frombuffer(base64.decodestring(enc[1]), dataType)

# if the array had more than one data set it has to be reshaped
if len(enc) > 2:
     dataArray.reshape(enc[2])   # return the reshaped numpy array containing several data sets
Run Code Online (Sandbox Code Playgroud)

由于许多原因,JSON转储是高效且交叉兼容的,但如果要存储和加载任何类型任何维度的 numpy数组,则只需使用JSON就会导致意外结果.

此解决方案存储和加载numpy数组,无论其类型或维度如何,并且还可以正确地恢复它(数据类型,维度......)

几个月前我尝试了几种解决方案,这是我遇到的唯一高效,多功能的解决方案.

  • 已投票,因为这是一个有用的答案。两只较小但相关的nitpicks。首先,我建议将数组数据写为格式化的文本。这样,它就易于阅读,并且可以避免可能出现的字节序问题。其次,我将dtype和形状都放在数据之前(作为数据头)。 (2认同)

Reb*_*ebs 5

我发现 Msgpack-numpy 中的代码很有帮助。 https://github.com/lebedov/msgpack-numpy/blob/master/msgpack_numpy.py

我稍微修改了序列化的 dict 并添加了 base64 编码以减少序列化的大小。

通过使用与 json 相同的接口(提供负载、转储),您可以提供 json 序列化的替代品。

可以扩展相同的逻辑以添加任何自动的非平凡序列化,例如日期时间对象。


编辑 我写了一个通用的、模块化的、解析器来完成这个以及更多。 https://github.com/someones/jaweson


我的代码如下:

np_json.py

from json import *
import json
import numpy as np
import base64

def to_json(obj):
    if isinstance(obj, (np.ndarray, np.generic)):
        if isinstance(obj, np.ndarray):
            return {
                '__ndarray__': base64.b64encode(obj.tostring()),
                'dtype': obj.dtype.str,
                'shape': obj.shape,
            }
        elif isinstance(obj, (np.bool_, np.number)):
            return {
                '__npgeneric__': base64.b64encode(obj.tostring()),
                'dtype': obj.dtype.str,
            }
    if isinstance(obj, set):
        return {'__set__': list(obj)}
    if isinstance(obj, tuple):
        return {'__tuple__': list(obj)}
    if isinstance(obj, complex):
        return {'__complex__': obj.__repr__()}

    # Let the base class default method raise the TypeError
    raise TypeError('Unable to serialise object of type {}'.format(type(obj)))


def from_json(obj):
    # check for numpy
    if isinstance(obj, dict):
        if '__ndarray__' in obj:
            return np.fromstring(
                base64.b64decode(obj['__ndarray__']),
                dtype=np.dtype(obj['dtype'])
            ).reshape(obj['shape'])
        if '__npgeneric__' in obj:
            return np.fromstring(
                base64.b64decode(obj['__npgeneric__']),
                dtype=np.dtype(obj['dtype'])
            )[0]
        if '__set__' in obj:
            return set(obj['__set__'])
        if '__tuple__' in obj:
            return tuple(obj['__tuple__'])
        if '__complex__' in obj:
            return complex(obj['__complex__'])

    return obj

# over-write the load(s)/dump(s) functions
def load(*args, **kwargs):
    kwargs['object_hook'] = from_json
    return json.load(*args, **kwargs)


def loads(*args, **kwargs):
    kwargs['object_hook'] = from_json
    return json.loads(*args, **kwargs)


def dump(*args, **kwargs):
    kwargs['default'] = to_json
    return json.dump(*args, **kwargs)


def dumps(*args, **kwargs):
    kwargs['default'] = to_json
    return json.dumps(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

然后,您应该能够执行以下操作:

import numpy as np
import np_json as json
np_data = np.zeros((10,10), dtype=np.float32)
new_data = json.loads(json.dumps(np_data))
assert (np_data == new_data).all()
Run Code Online (Sandbox Code Playgroud)