La matriz NumPy no es serializable JSON


247

Después de crear una matriz NumPy y guardarla como una variable de contexto de Django, recibo el siguiente error al cargar la página web:

array([   0,  239,  479,  717,  952, 1192, 1432, 1667], dtype=int64) is not JSON serializable

¿Qué significa esto?


19
Significa que en algún lugar, algo está tratando de volcar una matriz numpy usando el jsonmódulo. Pero numpy.ndarrayno es un tipo que jsonsepa manejar. Necesitará escribir su propio serializador o (más simplemente) simplemente pasar list(your_array)a lo que sea que esté escribiendo el json.
mgilson

24
Note list(your_array)no siempre funcionará, ya que devuelve entradas numpy, no entradas nativas. Usar en su your_array.to_list()lugar.
ashishsingal

18
una nota sobre el comentario de @ ashishsingal, debería ser your_array.tolist (), no to_list ().
vega

Respuestas:


289

Regularmente "jsonify" np.arrays. Intente usar primero el método ".tolist ()" en las matrices, de esta manera:

import numpy as np
import codecs, json 

a = np.arange(10).reshape(2,5) # a 2 by 5 array
b = a.tolist() # nested lists with same data, indices
file_path = "/path.json" ## your path variable
json.dump(b, codecs.open(file_path, 'w', encoding='utf-8'), separators=(',', ':'), sort_keys=True, indent=4) ### this saves the array in .json format

Para "desjonificar" el uso de la matriz:

obj_text = codecs.open(file_path, 'r', encoding='utf-8').read()
b_new = json.loads(obj_text)
a_new = np.array(b_new)

3
¿Por qué solo se puede almacenar como una lista de listas?
Nikhil Prabhu

No sé, pero yo esperaría tipos np.array tiene metadatos que no encaja en JSON (por ejemplo, especifican el tipo de datos de cada entrada como float)
travelingbones

2
Probé tu método, pero parece que el programa no funcionó tolist().
Harvett

3
@frankliuao Descubrí que la razón es que tolist()lleva una gran cantidad de tiempo cuando los datos son grandes.
Harvett

44
@NikhilPrabhu JSON es JavaScript Object Notation y, por lo tanto, solo puede representar las construcciones básicas del lenguaje javascript: objetos (análogos a Python Dicts), matrices (análogas a Python List), números, booleanos, cadenas y nulos (análogos a Python Nones ) Las matrices Numpy no son ninguna de esas cosas, por lo que no se pueden serializar en JSON. Algunos se pueden convertir a una forma similar a JSO (lista de listas), que es lo que hace esta respuesta.
Chris L. Barnes

225

Almacene como JSON un numpy.ndarray o cualquier composición de lista anidada.

class NumpyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.ndarray):
            return obj.tolist()
        return json.JSONEncoder.default(self, obj)

a = np.array([[1, 2, 3], [4, 5, 6]])
print(a.shape)
json_dump = json.dumps({'a': a, 'aa': [2, (2, 3, 4), a], 'bb': [2]}, cls=NumpyEncoder)
print(json_dump)

Saldrá:

(2, 3)
{"a": [[1, 2, 3], [4, 5, 6]], "aa": [2, [2, 3, 4], [[1, 2, 3], [4, 5, 6]]], "bb": [2]}

Para restaurar desde JSON:

json_load = json.loads(json_dump)
a_restored = np.asarray(json_load["a"])
print(a_restored)
print(a_restored.shape)

Saldrá:

[[1 2 3]
 [4 5 6]]
(2, 3)

26
Esto debería ser mucho más alto en el tablero, es la forma generalizada y adecuadamente abstraída de hacer esto. ¡Gracias!
Thclark

2
¿Hay una manera simple de recuperar el ndarray de la lista?
DarksteelPenguin

55
@DarksteelPenguin que estás buscando numpy.asarray()?
Eolo

3
Esta respuesta es excelente y puede ampliarse fácilmente para serializar los valores numpy float32 y np.float64 como json también:if isinstance(obj, np.float32) or isinstance(obj, np.float64): return float(obj)
Bensge

Esta solución evita que eches manualmente cada matriz numpy a la lista.
eduardosufan

44

Puedes usar Pandas :

import pandas as pd
pd.Series(your_array).to_json(orient='values')

66
¡Excelente! Y creo que para 2D np.array será algo así pd.DataFrame(your_array).to_json('data.json', orient='split').
nix

2
Salvó el día. Gracias
anurag

40

Encontré la mejor solución si ha anidado matrices numpy en un diccionario:

import json
import numpy as np

class NumpyEncoder(json.JSONEncoder):
    """ Special json encoder for numpy types """
    def default(self, obj):
        if isinstance(obj, np.integer):
            return int(obj)
        elif isinstance(obj, np.floating):
            return float(obj)
        elif isinstance(obj, np.ndarray):
            return obj.tolist()
        return json.JSONEncoder.default(self, obj)

dumped = json.dumps(data, cls=NumpyEncoder)

with open(path, 'w') as f:
    json.dump(dumped, f)

Gracias a este chico .


Gracias por la útil respuesta! Escribí los atributos en un archivo json, pero ahora tengo problemas para volver a leer los parámetros de Regresión logística. ¿Hay un 'decodificador' para este archivo json guardado?
TTZ

Por supuesto, para leer el jsonreverso puede usar esto:, with open(path, 'r') as f: data = json.load(f)que devuelve un diccionario con sus datos.
tsveti_iko

Eso es para leer el jsonarchivo y luego para deserializar su salida, puede usar esto:data = json.loads(data)
tsveti_iko

Tuve que agregar esto para manejar el tipo de datos de bytes ... suponiendo que todos los bytes sean cadena utf-8. elif isinstance (obj, (bytes,)): return obj.decode ("utf-8")
Soichi Hayashi

+1. ¿Por qué necesitamos la línea "return json.JSONEncoder.default (self, obj)" al final de "def default (self, obj)"?
Hans

23

Usa el json.dumps defaultkwarg:

default debería ser una función que se llama para objetos que de otra manera no se pueden serializar.

En la defaultfunción, compruebe si el objeto es numpy del módulo, si es así, use ndarray.tolistpara ao ndarrayuse .itempara cualquier otro tipo específico de numpy.

import numpy as np

def default(obj):
    if type(obj).__module__ == np.__name__:
        if isinstance(obj, np.ndarray):
            return obj.tolist()
        else:
            return obj.item()
    raise TypeError('Unknown type:', type(obj))

dumped = json.dumps(data, default=default)

¿Cuál es el papel de la línea type(obj).__module__ == np.__name__: allí? ¿No sería suficiente verificar la instancia?
Ramón Martínez

@RamonMartinez, para saber que el objeto es un objeto numpy, de esta manera puedo usarlo .itempara casi cualquier objeto numpy. defaultSe llama a la función para todos los json.dumpsintentos de serialización de tipos desconocidos . no solo numpy
moshevi

5

Por defecto, esto no es compatible, ¡pero puede hacer que funcione con bastante facilidad! Hay varias cosas que querrá codificar si desea recuperar exactamente los mismos datos:

  • Los datos en sí, que puede obtener obj.tolist()como se menciona en @travelingbones. A veces esto puede ser lo suficientemente bueno.
  • El tipo de datos. Siento que esto es importante en algunos casos.
  • La dimensión (no necesariamente 2D), que podría derivarse de lo anterior si supone que la entrada es siempre una cuadrícula 'rectangular'.
  • El orden de la memoria (fila o columna principal). Esto no suele importar, pero a veces sí lo hace (por ejemplo, el rendimiento), entonces, ¿por qué no guardarlo todo?

Además, su matriz numpy podría formar parte de su estructura de datos, por ejemplo, tiene una lista con algunas matrices dentro. Para eso, podría usar un codificador personalizado que básicamente hace lo anterior.

Esto debería ser suficiente para implementar una solución. O podría usar json-tricks que hace exactamente esto (y admite varios otros tipos) (descargo de responsabilidad: lo hice).

pip install json-tricks

Luego

data = [
    arange(0, 10, 1, dtype=int).reshape((2, 5)),
    datetime(year=2017, month=1, day=19, hour=23, minute=00, second=00),
    1 + 2j,
    Decimal(42),
    Fraction(1, 3),
    MyTestCls(s='ub', dct={'7': 7}),  # see later
    set(range(7)),
]
# Encode with metadata to preserve types when decoding
print(dumps(data))

3

Tuve un problema similar con un diccionario anidado con algunos numpy.ndarrays en él.

def jsonify(data):
    json_data = dict()
    for key, value in data.iteritems():
        if isinstance(value, list): # for lists
            value = [ jsonify(item) if isinstance(item, dict) else item for item in value ]
        if isinstance(value, dict): # for nested lists
            value = jsonify(value)
        if isinstance(key, int): # if key is integer: > to string
            key = str(key)
        if type(value).__module__=='numpy': # if value is numpy.*: > to python list
            value = value.tolist()
        json_data[key] = value
    return json_data

3

También podría usar defaultargumentos, por ejemplo:

def myconverter(o):
    if isinstance(o, np.float32):
        return float(o)

json.dump(data, default=myconverter)

1

Además, hay información muy interesante sobre listas vs. arrays en Python ~> Python List vs. Array: ¿cuándo usarlo?

Cabe señalar que una vez que convierto mis matrices en una lista antes de guardarlo en un archivo JSON, en mi implementación ahora mismo de todos modos, una vez que leí ese archivo JSON para usarlo más tarde, puedo seguir usándolo en forma de lista (como opuesto a convertirlo de nuevo a una matriz).

Y en realidad se ve mejor (en mi opinión) en la pantalla como una lista (separada por comas) frente a una matriz (no separada por comas) de esta manera.

Usando el método .tolist () de @ travelingbones anterior, he estado usando como tal (detectando algunos errores que he encontrado también):

GUARDAR DICCIONARIO

def writeDict(values, name):
    writeName = DIR+name+'.json'
    with open(writeName, "w") as outfile:
        json.dump(values, outfile)

LEER DICCIONARIO

def readDict(name):
    readName = DIR+name+'.json'
    try:
        with open(readName, "r") as infile:
            dictValues = json.load(infile)
            return(dictValues)
    except IOError as e:
        print(e)
        return('None')
    except ValueError as e:
        print(e)
        return('None')

¡Espero que esto ayude!


1

Aquí hay una implementación que funcionó para mí y eliminó todos los nans (suponiendo que estos sean objetos simples (list o dict)):

from numpy import isnan

def remove_nans(my_obj, val=None):
    if isinstance(my_obj, list):
        for i, item in enumerate(my_obj):
            if isinstance(item, list) or isinstance(item, dict):
                my_obj[i] = remove_nans(my_obj[i], val=val)

            else:
                try:
                    if isnan(item):
                        my_obj[i] = val
                except Exception:
                    pass

    elif isinstance(my_obj, dict):
        for key, item in my_obj.iteritems():
            if isinstance(item, list) or isinstance(item, dict):
                my_obj[key] = remove_nans(my_obj[key], val=val)

            else:
                try:
                    if isnan(item):
                        my_obj[key] = val
                except Exception:
                    pass

    return my_obj

1

Esta es una respuesta diferente, pero podría ayudar a las personas que intentan guardar datos y luego volver a leerlos.
Hay Hickle que es más rápido que encurtido y más fácil.
Traté de guardarlo y leerlo en pickle dump, pero mientras leía hubo muchos problemas y perdí una hora y aún no encontré la solución, aunque estaba trabajando en mis propios datos para crear un bot de chat.

vec_xy vec_yson matrices numpy:

data=[vec_x,vec_y]
hkl.dump( data, 'new_data_file.hkl' )

Luego solo lo lees y realizas las operaciones:

data2 = hkl.load( 'new_data_file.hkl' )

1

Puede hacer un bucle simple con tipos de comprobación:

with open("jsondontdoit.json", 'w') as fp:
    for key in bests.keys():
        if type(bests[key]) == np.ndarray:
            bests[key] = bests[key].tolist()
            continue
        for idx in bests[key]:
            if type(bests[key][idx]) == np.ndarray:
                bests[key][idx] = bests[key][idx].tolist()
    json.dump(bests, fp)
    fp.close()

1

use NumpyEncoder procesará el volcado json con éxito. Sin tirar - La matriz NumPy no es serializable JSON

import numpy as np
import json
from numpyencoder import NumpyEncoder
arr = array([   0,  239,  479,  717,  952, 1192, 1432, 1667], dtype=int64) 
json.dumps(arr,cls=NumpyEncoder)

0

TypeError: array ([[0.46872085, 0.67374235, 1.0218339, 0.13210179, 0.5440686, 0.9140083, 0.58720225, 0.2199381]], dtype = float32) no es serializable JSON

El error mencionado anteriormente se produjo cuando intenté pasar la lista de datos a model.predict () cuando esperaba la respuesta en formato json.

> 1        json_file = open('model.json','r')
> 2        loaded_model_json = json_file.read()
> 3        json_file.close()
> 4        loaded_model = model_from_json(loaded_model_json)
> 5        #load weights into new model
> 6        loaded_model.load_weights("model.h5")
> 7        loaded_model.compile(optimizer='adam', loss='mean_squared_error')
> 8        X =  [[874,12450,678,0.922500,0.113569]]
> 9        d = pd.DataFrame(X)
> 10       prediction = loaded_model.predict(d)
> 11       return jsonify(prediction)

Pero afortunadamente encontré la pista para resolver el error que estaba lanzando. La serialización de los objetos es aplicable solo para la siguiente conversión La asignación debe ser de la siguiente manera objeto - matriz dict - cadena de lista - entero de cadena - entero

Si se desplaza hacia arriba para ver la línea número 10 prediction = loaded_model.predict (d) donde esta línea de código estaba generando la salida del tipo de datos de matriz, cuando intenta convertir la matriz a formato json no es posible

Finalmente encontré la solución simplemente convirtiendo la salida obtenida a la lista de tipos siguiendo las líneas de código

prediction = loaded_model.predict (d)
listtype = prediction.tolist () return jsonify (listtype)

Bhoom! finalmente obtuve el resultado esperado, ingrese la descripción de la imagen aquí

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.