Le tableau NumPy n'est pas sérialisable JSON


248

Après avoir créé un tableau NumPy et l'avoir enregistré en tant que variable de contexte Django, je reçois l'erreur suivante lors du chargement de la page Web:

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

Qu'est-ce que ça veut dire?


19
Cela signifie que quelque part, quelque chose essaie de vider un tableau numpy en utilisant le jsonmodule. Mais ce numpy.ndarrayn'est pas un type qui jsonsait gérer. Vous devrez soit écrire votre propre sérialiseur, soit (plus simplement) simplement passer list(your_array)à ce qui écrit le json.
mgilson

24
Remarque list(your_array)ne fonctionnera pas toujours car elle renvoie des nombres entiers, pas des nombres natifs. Utilisez your_array.to_list()plutôt.
ashishsingal

18
une note sur le commentaire de @ ashishsingal, ce devrait être your_array.tolist (), pas to_list ().
vega

Réponses:


289

Je "jsonify" régulièrement np.arrays. Essayez d'abord d'utiliser la méthode ".tolist ()" sur les tableaux, comme ceci:

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

Afin de "déconsidérer" l'utilisation du tableau:

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

3
Pourquoi ne peut-il être stocké que sous forme de liste de listes?
Nikhil Prabhu

Je ne sais pas, mais je m'attends à ce que les types np.array aient des métadonnées qui ne correspondent pas à json (par exemple, ils spécifient le type de données de chaque entrée comme float)
travellingbones

2
J'ai essayé votre méthode, mais il semble que le programme soit resté tolist().
Harvett

3
@frankliuao J'ai trouvé que la raison en est que cela tolist()prend énormément de temps lorsque les données sont volumineuses.
Harvett

4
@NikhilPrabhu JSON est la notation d'objet Javascript, et ne peut donc représenter que les constructions de base du langage javascript: objets (analogues aux dictés python), tableaux (analogues aux listes python), nombres, booléens, chaînes et nulls (analogues aux python Nones ). Les tableaux Numpy ne font pas partie de ces éléments et ne peuvent donc pas être sérialisés en JSON. Certains peuvent être convertis sous une forme de type JSO (liste de listes), ce que fait cette réponse.
Chris L.Barnes

226

Stockez en JSON un numpy.ndarray ou toute composition de liste imbriquée.

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)

Sortira:

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

Pour restaurer à partir de JSON:

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

Sortira:

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

26
Cela devrait être bien plus haut dans le tableau, c'est la manière généralisable et correctement abstraite de le faire. Merci!
thclark

2
Existe-t-il un moyen simple de récupérer le ndarray de la liste?
DarksteelPenguin

5
@DarksteelPenguin que vous recherchez numpy.asarray()?
aeolus

3
Cette réponse est excellente et peut facilement être étendue pour sérialiser les valeurs numpy float32 et np.float64 en json également:if isinstance(obj, np.float32) or isinstance(obj, np.float64): return float(obj)
Bensge

Cette solution vous évite de convertir manuellement chaque tableau numpy en liste.
eduardosufan

44

Vous pouvez utiliser Pandas :

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

6
Génial! Et je pense que pour 2D np.array, ce sera quelque chose comme pd.DataFrame(your_array).to_json('data.json', orient='split').
nix

2
Sauvé la journée. Merci
anurag

40

J'ai trouvé la meilleure solution si vous avez imbriqué des tableaux numpy dans un dictionnaire:

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)

Merci à ce gars .


Merci pour la réponse utile! J'ai écrit les attributs dans un fichier json, mais j'ai maintenant du mal à relire les paramètres de la régression logistique. Existe-t-il un «décodeur» pour ce fichier json enregistré?
TTZ

Bien sûr, pour lire le jsonverso, vous pouvez utiliser ceci with open(path, 'r') as f: data = json.load(f):, qui renvoie un dictionnaire avec vos données.
tsveti_iko

C'est pour lire le jsonfichier, puis pour désérialiser sa sortie, vous pouvez utiliser ceci:data = json.loads(data)
tsveti_iko

J'ai dû ajouter ceci pour gérer le type de données des octets .. en supposant que tous les octets sont des chaînes utf-8. elif isinstance (obj, (bytes,)): return obj.decode ("utf-8")
Soichi Hayashi

+1. Pourquoi avons-nous besoin de la ligne "return json.JSONEncoder.default (self, obj)" à la fin de "def default (self, obj)"?
Hans

23

Utilisez le json.dumps defaultkwarg:

par défaut doit être une fonction qui est appelée pour des objets qui ne peuvent pas être sérialisés autrement.

Dans la defaultfonction, vérifiez si l'objet provient du module numpy, si c'est le cas, utilisez-le ndarray.tolistpour un ndarrayou utilisez-le .itempour tout autre type spécifique à 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)

Quel est le rôle de la ligne type(obj).__module__ == np.__name__: là-bas? Ne suffirait-il pas de vérifier l'instance?
Ramon Martinez

@RamonMartinez, pour savoir que l'objet est un objet numpy, de cette façon je peux l'utiliser .itempour presque n'importe quel objet numpy. defaultest appelée pour toutes les json.dumpstentatives de sérialisation de types inconnus . pas seulement engourdi
moshevi

6

Ce n'est pas pris en charge par défaut, mais vous pouvez le faire fonctionner assez facilement! Il y a plusieurs choses que vous voudrez encoder si vous voulez récupérer exactement les mêmes données:

  • Les données elles-mêmes, que vous pouvez obtenir avec obj.tolist()@travelingbones mentionné. Parfois, cela peut suffire.
  • Le type de données. Je pense que c'est important dans bien des cas.
  • La dimension (pas nécessairement 2D), qui pourrait être dérivée de ce qui précède si vous supposez que l'entrée est en effet toujours une grille «rectangulaire».
  • L'ordre de la mémoire (ligne ou colonne principale). Cela n'a pas souvent d'importance, mais parfois (par exemple les performances), alors pourquoi ne pas tout sauvegarder?

De plus, votre tableau numpy pourrait faire partie de votre structure de données, par exemple, vous avez une liste avec quelques matrices à l'intérieur. Pour cela, vous pouvez utiliser un encodeur personnalisé qui fait essentiellement ce qui précède.

Cela devrait être suffisant pour implémenter une solution. Ou vous pouvez utiliser json-tricks qui fait exactement cela (et prend en charge divers autres types) (avertissement: je l'ai fait).

pip install json-tricks

ensuite

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

J'ai eu un problème similaire avec un dictionnaire imbriqué contenant quelques 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

Vous pouvez également utiliser l' defaultargument par exemple:

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

json.dump(data, default=myconverter)

1

En outre, quelques informations très intéressantes sur les listes et les tableaux en Python ~> Liste Python vs tableau - quand l'utiliser?

On peut noter qu'une fois que j'ai converti mes tableaux en liste avant de l'enregistrer dans un fichier JSON, dans mon déploiement en ce moment en tout cas, une fois que j'ai lu ce fichier JSON pour l'utiliser plus tard, je peux continuer à l'utiliser sous forme de liste (comme plutôt que de le reconvertir en tableau).

ET semble en fait plus agréable (à mon avis) à l'écran comme une liste (virgule séparée) par rapport à un tableau (non virgule séparée) de cette façon.

En utilisant la méthode .tolist () de @ travellingbones ci-dessus, j'ai utilisé en tant que telle (en détectant quelques erreurs que j'ai également trouvées):

SAUVEGARDER LE DICTIONNAIRE

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

LIRE LE DICTIONNAIRE

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

J'espère que cela t'aides!


1

Voici une implémentation qui fonctionne pour moi et a supprimé tous les nans (en supposant qu'il s'agit d'un objet simple (liste ou 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

Il s'agit d'une réponse différente, mais cela pourrait aider les personnes qui tentent de sauvegarder des données, puis de les relire.
Il y a le hickle qui est plus rapide que le cornichon et plus facile.
J'ai essayé de l'enregistrer et de le lire dans un dépotoir, mais pendant la lecture, il y avait beaucoup de problèmes et j'ai perdu une heure et je n'ai toujours pas trouvé de solution même si je travaillais sur mes propres données pour créer un chat bot.

vec_xet vec_ysont des tableaux numpy:

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

Ensuite, il vous suffit de le lire et d'effectuer les opérations:

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

1

Peut faire simple pour boucle avec vérification des types:

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

utilisez NumpyEncoder, il traitera le vidage json avec succès. sans lancer - Le tableau NumPy n'est pas sérialisable 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) n'est pas sérialisable JSON

L'erreur mentionnée ci-dessus a été levée lorsque j'ai essayé de transmettre la liste des données à model.predict () alors que j'attendais la réponse au format 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)

Mais heureusement, j'ai trouvé le conseil pour résoudre l'erreur qui se produisait La sérialisation des objets n'est applicable que pour la conversion suivante Le mappage devrait être de la manière suivante objet - tableau dict - liste chaîne - chaîne entière - chaîne entière

Si vous faites défiler vers le haut pour voir la ligne numéro 10 prédiction = modèle_chargé.prédict (d) où cette ligne de code générait la sortie de type type de données de tableau, lorsque vous essayez de convertir un tableau au format json, ce n'est pas possible

Enfin, j'ai trouvé la solution simplement en convertissant la sortie obtenue en liste de types en suivant les lignes de code

prediction = modèle_chargé.prédict (d)
listtype = prediction.tolist () return jsonify (listtype)

Boom! a finalement obtenu la sortie attendue, entrez la description de l'image ici

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.