L'array NumPy non è serializzabile JSON


248

Dopo aver creato un array NumPy e averlo salvato come variabile di contesto Django, ricevo il seguente errore durante il caricamento della pagina Web:

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

Cosa significa questo?


19
Vuol dire che da qualche parte qualcosa sta provando a scaricare un array intorpidito usando il jsonmodulo. Ma numpy.ndarraynon è un tipo che jsonsa come gestire. O dovrai scrivere il tuo serializzatore o (più semplicemente) passare list(your_array)a qualunque cosa stia scrivendo il json.
mgilson,

24
Nota list(your_array)non funzionerà sempre in quanto restituisce intorpiditi ints, non ints nativi. Usa your_array.to_list()invece.
ashishsingal,

18
una nota sul commento di @ ashishsingal, dovrebbe essere your_array.tolist (), non to_list ().
vega

Risposte:


289

"Jsonify" regolarmente np.arrays. Prova a utilizzare prima il metodo ".tolist ()" sugli array, in questo modo:

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

Per "disgiungere" l'array usa:

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

3
Perché può essere memorizzato solo come un elenco di elenchi?
Nikhil Prabhu

Non lo so, ma mi aspetto che i tipi di np.array abbiano metadati che non si adattano a json (ad esempio, specificano il tipo di dati di ogni voce come float)
travelingbones,

2
Ho provato il tuo metodo, ma sembra che il programma si sia bloccato tolist().
Harvett,

3
@frankliuao Ho scoperto che la ragione è che ci tolist()vuole un sacco di tempo quando i dati sono grandi.
Harvett,

4
@NikhilPrabhu JSON è Javascript Object Notation e può quindi rappresentare solo i costrutti di base del linguaggio javascript: oggetti (analoghi ai python dicts), matrici (analoghi agli elenchi di python), numeri, booleani, stringhe e null (analoghi a Python Nones ). Le matrici Numpy non sono nessuna di queste cose e quindi non possono essere serializzate in JSON. Alcuni possono essere convertiti in un modulo simile a JSO (elenco di elenchi), che è ciò che fa questa risposta.
Chris L. Barnes,

226

Archivia come JSON un numpy.ndarray o qualsiasi composizione dell'elenco nidificato.

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)

Uscita:

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

Per ripristinare da JSON:

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

Uscita:

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

26
Questo dovrebbe essere molto più in alto, è il modo generalizzabile e correttamente astratto di farlo. Grazie!
tharkark

2
Esiste un modo semplice per ripristinare ndarray dall'elenco?
DarksteelPenguin,

5
@DarksteelPenguin stai cercando numpy.asarray()?
eolo

3
Questa risposta è ottima e può essere facilmente estesa per serializzare i valori numpy float32 e np.float64 anche come json:if isinstance(obj, np.float32) or isinstance(obj, np.float64): return float(obj)
Bensge

Questa soluzione evita di trasmettere manualmente ogni array numpy da elencare.
eduardosufan,

44

Puoi usare i panda :

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

6
Grande! E penso che per 2D np.array sarà qualcosa di simile pd.DataFrame(your_array).to_json('data.json', orient='split').
nix,

2
Salvato il giorno. Grazie
anurag il

40

Ho trovato la soluzione migliore se hai nidificato array numpy in un dizionario:

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)

Grazie a questo ragazzo .


Grazie per la risposta utile! Ho scritto gli attributi in un file json, ma ora ho problemi a rileggere i parametri per la regressione logistica. Esiste un "decodificatore" per questo file json salvato?
TTZ,

Ovviamente, per leggere il jsonretro puoi usare questo with open(path, 'r') as f: data = json.load(f):, che restituisce un dizionario con i tuoi dati.
tsveti_iko,

Questo è per leggere il jsonfile e poi per deserializzare il suo output puoi usare questo:data = json.loads(data)
tsveti_iko

Ho dovuto aggiungere questo per gestire il tipo di dati byte .. supponendo che tutti i byte siano stringa utf-8. elif isinstance (obj, (bytes,)): return obj.decode ("utf-8")
Soichi Hayashi,

+1. Perché abbiamo bisogno della riga "return json.JSONEncoder.default (self, obj)" alla fine di "def default (self, obj)"?
Hans,

23

Usa il json.dumps defaultkwarg:

il valore predefinito dovrebbe essere una funzione che viene chiamata per oggetti che altrimenti non possono essere serializzati.

Nella defaultfunzione verificare se l'oggetto proviene dal modulo numpy, in tal caso utilizzare ndarray.tolistper a ndarrayo utilizzare .itemper qualsiasi altro tipo specifico 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)

Qual è il ruolo della linea type(obj).__module__ == np.__name__: lì? Non basterebbe verificare l'istanza?
Ramon Martinez,

@RamonMartinez, per sapere che l'oggetto è un oggetto intorpidito, in questo modo posso usare .itemper quasi qualsiasi oggetto intorpidito. defaultLa funzione viene chiamata per tutti i tipi sconosciuti che json.dumpstentano di serializzare. non solo intorpidito
moshevi,

5

Questo non è supportato di default, ma puoi farlo funzionare abbastanza facilmente! Ci sono diverse cose che vorresti codificare se desideri restituire gli stessi dati esatti:

  • I dati stessi, che puoi ottenere obj.tolist()come menzionato da @travelingbones. A volte questo può essere abbastanza buono.
  • Il tipo di dati Penso che questo sia importante in alcuni casi.
  • La dimensione (non necessariamente 2D), che potrebbe essere derivata da quanto sopra se si presume che l'input sia effettivamente sempre una griglia 'rettangolare'.
  • L'ordine di memoria (riga o colonna maggiore). Questo spesso non ha importanza, ma a volte lo fa (ad es. Le prestazioni), quindi perché non salvare tutto?

Inoltre, l'array numpy potrebbe far parte della struttura dei dati, ad esempio hai un elenco con alcune matrici all'interno. Per questo è possibile utilizzare un codificatore personalizzato che sostanzialmente fa quanto sopra.

Questo dovrebbe essere sufficiente per implementare una soluzione. Oppure potresti usare json-tricks che fa proprio questo (e supporta vari altri tipi) (disclaimer: l'ho fatto).

pip install json-tricks

Poi

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

Ho avuto un problema simile con un dizionario nidificato con alcuni numpy.ndarrays.

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

Puoi anche usare l' defaultargomento per esempio:

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

json.dump(data, default=myconverter)

1

Inoltre, alcune informazioni molto interessanti sugli elenchi e sugli array in Python ~> Python List vs. Array - quando utilizzare?

Si può notare che una volta convertito i miei array in un elenco prima di salvarlo in un file JSON, nella mia distribuzione in questo momento in ogni momento, una volta letto quel file JSON per l'uso in seguito, posso continuare a usarlo in un modulo elenco (come al contrario di riconvertirlo in un array).

E in realtà sembra più bello (secondo me) sullo schermo come un elenco (separato da virgola) rispetto a un array (non separato da virgola) in questo modo.

Usando il metodo .tolist () di @ travelingbones sopra, ho usato come tale (cogliendo anche alcuni errori che ho trovato):

SALVA DIZIONARIO

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

LEGGI DIZIONARIO

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')

Spero che questo ti aiuti!


1

Ecco un'implementazione che funziona per me e ha rimosso tutti i nans (supponendo che questi siano oggetti semplici (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

Questa è una risposta diversa, ma ciò potrebbe aiutare le persone che stanno cercando di salvare i dati e poi di rileggerli.
C'è il grillo che è più veloce del sottaceto e più facile.
Ho provato a salvarlo e leggerlo in una discarica, ma durante la lettura c'erano molti problemi e ho perso un'ora e ancora non ho trovato la soluzione, anche se stavo lavorando sui miei dati per creare un bot di chat.

vec_xe vec_ysono matrici intorpidite:

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

Quindi basta leggerlo ed eseguire le operazioni:

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

1

Può essere semplice per il ciclo con i tipi di controllo:

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

utilizzare NumpyEncoder elaborerà correttamente il dump json senza lanciare - l'array NumPy non è serializzabile 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) non è serializzabile JSON

L'errore di cui sopra è stato generato quando ho provato a passare l'elenco dei dati a model.predict () quando mi aspettavo la risposta in 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)

Ma fortunatamente ho trovato il suggerimento per risolvere l'errore che stava generando. La serializzazione degli oggetti è applicabile solo per la seguente conversione La mappatura dovrebbe essere nel modo seguente oggetto - array dict - stringa elenco - stringa intero - intero

Se scorri verso l'alto per vedere la previsione del numero di riga 10 = loaded_model.predict (d) dove questa riga di codice stava generando l'output del tipo di dati tipo array, quando provi a convertire l'array in formato json non è possibile

Alla fine ho trovato la soluzione semplicemente convertendo l'output ottenuto nell'elenco dei tipi seguendo le righe di codice

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

Bhoom! finalmente ottenuto il risultato atteso, inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.