¿Cómo puedo serializar una matriz numpy mientras conservo las dimensiones de la matriz?


numpy.array.tostring no parece preservar la información sobre las dimensiones de la matriz (ver esta pregunta), lo que requiere que el usuario emita una llamada a numpy.array.reshape.

¿Hay alguna manera de serializar una matriz numpy en formato JSON mientras se preserva esta información?

Nota: Los arrays pueden contener ints, floats o bools. Es razonable esperar una matriz transpuesta.

Nota 2: esto se está haciendo con la intención de pasar la matriz numpy a través de una topología de tormenta utilizar streamparse, en caso de que dicha información resulte relevante.

Author: Community, 2015-06-07

7 answers

pickle.dumps o numpy.save codifica toda la información necesaria para reconstruir un array NumPy arbitrario, incluso en presencia de problemas de endianidad, arrays no contiguos o tipos de tupla extraños. Los problemas de endianidad son probablemente los más importantes; no quieres que array([1]) se convierta repentinamente en array([16777216]) porque cargaste tu matriz en una máquina big-endian. {[7] } es probablemente la opción más conveniente, aunque save tiene sus propios beneficios, dados en el npy formato justificación.

La opción pickle:

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

numpy.save utiliza un formato binario, y necesita escribir en un archivo, pero puede evitarlo con 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

Y deserializar:

memfile = StringIO.StringIO()
memfile.write(json.loads(serialized).encode('latin-1'))
memfile.seek(0)
a = numpy.load(memfile)
 30
Author: user2357112,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-03 22:24:18

EDIT: Como se puede leer en los comentarios de la pregunta, esta solución trata con matrices numpy "normales" (floats, ints, bools ...) y no con arrays estructurados multitipo.

Solución para serializar una matriz numpy de cualquier dimensión y tipo de datos

Por lo que sé, no se puede serializar simplemente una matriz numpy con cualquier tipo de datos y cualquier dimensión...pero puede almacenar su tipo de datos, dimensión e información en una representación de lista y luego serializarla usando JSON.

Importaciones necesarias :

import json
import base64

Para codificar se puede usar (nparray es una matriz numpy de cualquier tipo de datos y cualquier dimensionalidad):

json.dumps([str(nparray.dtype), base64.b64encode(nparray), nparray.shape])

Después de esto se obtiene un volcado JSON (cadena) de sus datos, que contiene una representación de lista de su tipo de datos y forma, así como los arrays datos/contenidos codificados en base64.

Y para decodificar esto hace el trabajo (encStr es la cadena JSON codificada, cargada desde algún lugar):

# 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

JSON los volcados son eficientes y compatibles por muchas razones, pero solo tomar JSON conduce a resultados inesperados si desea almacenar y cargar matrices numpy de cualquier tipo y cualquier dimensión.

Esta solución almacena y carga matrices numpy independientemente del tipo o dimensión y también los restaura correctamente (tipo de datos, dimensión,...)

Probé varias soluciones hace meses y esta fue la única solución eficiente y versátil que encontré.

 6
Author: daniel451,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-07 21:55:13

Encontré útil el código en Msgpack-numpy. https://github.com/lebedov/msgpack-numpy/blob/master/msgpack_numpy.py

Modifiqué ligeramente el diccionario serializado y agregué la codificación base64 para reducir el tamaño serializado.

Mediante el uso de la misma interfaz que json (proporcionando carga(s),volcado(s)), puede proporcionar un reemplazo directo para la serialización json.

Esta misma lógica se puede extender para agregar cualquier serialización automática no trivial, como datetime objeto.


EDITAR He escrito un analizador genérico modular que hace esto y más. https://github.com/someones/jaweson


Mi código es el siguiente:

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)

Usted debe ser capaz de hacer lo siguiente:

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()
 3
Author: Rebs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-15 02:51:49

Si necesita ser legible por humanos y sabes que esto es una matriz numpy:

import numpy as np; 
import json;

a = np.random.normal(size=(50,120,150))
a_reconstructed = np.asarray(json.loads(json.dumps(a.tolist())))
print np.allclose(a,a_reconstructed)
print (a==a_reconstructed).all()

Tal vez no sea el más eficiente a medida que los tamaños de las matrices crecen, pero funciona para matrices más pequeñas.

 0
Author: Chris.Wilson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-01 17:19:12

Msgpack tiene el mejor rendimiento de serialización: http://www.benfrederickson.com/dont-pickle-your-data /

Use msgpack-numpy. Véase https://github.com/lebedov/msgpack-numpy

Instalarlo:

pip install msgpack-numpy

Entonces:

import msgpack
import msgpack_numpy as m
import numpy as np

x = np.random.rand(5)
x_enc = msgpack.packb(x, default=m.encode)
x_rec = msgpack.unpackb(x_enc, object_hook=m.decode)
 0
Author: thayne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-19 18:14:59

Intente traitschema https://traitschema.readthedocs.io/en/latest /

"Cree esquemas serializables y comprobados usando traits y Numpy. Un caso de uso típico implica guardar varias matrices Numpy de forma y tipo variables."

 0
Author: SemanticBeeng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-11 13:43:13

Intenta usar numpy.array_repr o numpy.array_str.

 -4
Author: Ken,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-07 20:28:54