A matriz NumPy não é JSON serializável


247

Depois de criar uma matriz NumPy e salvá-la como uma variável de contexto do Django, recebo o seguinte erro ao carregar a página da web:

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

O que isto significa?


19
Isso significa que em algum lugar, algo está tentando despejar uma matriz numpy usando o jsonmódulo. Mas numpy.ndarraynão é um tipo que jsonsaiba como lidar. Você precisará escrever seu próprio serializador ou (simplesmente) passar list(your_array)para o que estiver escrevendo o json.
mgilson

24
A nota list(your_array)nem sempre funciona, pois retorna entradas numpy, não entradas nativas. Use em your_array.to_list()vez disso.
ashishsingal

18
uma observação sobre o comentário de @ ashishsingal, deve ser your_array.tolist (), não to_list ().
Vega

Respostas:


289

Eu regularmente "jsonify" np.arrays. Tente usar o método ".tolist ()" nas matrizes primeiro, assim:

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 "desjsonificar" a matriz, use:

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

2
Por que ele só pode ser armazenado como uma lista de listas?
Nikhil Prabhu

Eu não sei, mas espero tipos np.array têm metadados que não se encaixa em JSON (por exemplo, eles especificam o tipo de dados de cada entrada como float)
travelingbones

1
Eu tentei o seu método, mas parece que o programa travou tolist().
Harvett 31/01

2
@frankliuao Encontrei o motivo é que tolist()leva uma quantidade enorme de tempo quando os dados são grandes.
Harvett 7/01/19

3
@NikhilPrabhu JSON é Javascript Object Notation e, portanto, pode representar apenas as construções básicas da linguagem javascript: objetos (análogos aos dicionários de python), matrizes (análogas às listas de python), números, booleanos, strings e nulos (análogos a python Nones) ) Matrizes numpy não são nenhuma dessas coisas e, portanto, não podem ser serializadas no JSON. Alguns podem ser convertidos para um formulário semelhante ao JSO (lista de listas), que é o que esta resposta faz.
Chris L. Barnes

224

Armazene como JSON um numpy.ndarray ou qualquer composição de lista aninhada.

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)

Saída:

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

Para restaurar a partir do JSON:

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

Saída:

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

26
Isso deve ser bem mais alto, é a maneira generalizada e adequadamente abstrata de fazer isso. Obrigado!
thclark

1
Existe uma maneira simples de recuperar o ndarray da lista?
DarksteelPenguin 23/02

4
@DarksteelPenguin você está procurando numpy.asarray()?
aeolus 04/04

2
Essa resposta é grande e pode ser facilmente estendido para valores float32 e np.float64 serialize numpy como JSON também:if isinstance(obj, np.float32) or isinstance(obj, np.float64): return float(obj)
Bensge

Esta solução evita que você converta manualmente todas as matrizes numpy a serem listadas.
eduardosufan 11/03

43

Você pode usar o Pandas :

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

6
Ótimo! E acho que para np.array 2D será algo parecido pd.DataFrame(your_array).to_json('data.json', orient='split').
Nix

2
Salvou o dia. Obrigado
anurag 02/03

38

Encontrei a melhor solução se você aninhar matrizes numpy em um dicionário:

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)

Graças a esse cara .


Obrigado pela resposta útil! Eu escrevi os atributos em um arquivo json, mas agora estou tendo problemas para ler os parâmetros da Regressão logística. Existe um 'decodificador' para este arquivo json salvo?
TTZ

Obviamente, para ler o jsonverso, você pode usar o seguinte with open(path, 'r') as f: data = json.load(f):, que retorna um dicionário com seus dados.
Tsveti_iko 20/08/19

Isso é para ler o jsonarquivo e depois para desserializar a sua saída você pode usar isto:data = json.loads(data)
tsveti_iko

Eu tive que adicionar isso para lidar com o tipo de dados bytes .. assumindo que todos os bytes são utf-8 string. elif isinstance (obj, (bytes,)): retorna obj.decode ("utf-8")
Soichi Hayashi

+1. Por que precisamos da linha "return json.JSONEncoder.default (self, obj)" no final de "def default (self, obj)"?
Hans

22

Use o json.dumps defaultkwarg:

o padrão deve ser uma função que é chamada para objetos que não poderiam ser serializados.

Na defaultfunção, verifique se o objeto é do módulo numpy, se for o caso, use ndarray.tolistpara um ndarrayou use .itempara qualquer outro 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)

Qual é o papel da linha type(obj).__module__ == np.__name__: lá? Não seria suficiente verificar a instância?
Ramon Martinez

@RamonMartinez, para saber que o objeto é um objeto numpy, desta forma eu posso usar .itempara quase qualquer objeto numpy. defaultA função é chamada para todos os tipos desconhecidos que json.dumpstentam serializar. não apenas
entorpecido

5

Isso não é suportado por padrão, mas você pode fazê-lo funcionar facilmente! Há várias coisas que você deseja codificar se desejar os mesmos dados exatamente de volta:

  • Os dados em si, que você pode obter obj.tolist()como os @travelingbones mencionados. Às vezes isso pode ser bom o suficiente.
  • O tipo de dados. Eu sinto que isso é importante em alguns casos.
  • A dimensão (não necessariamente 2D), que pode ser derivada do exposto acima, se você assumir que a entrada é realmente sempre uma grade 'retangular'.
  • A ordem da memória (principal da linha ou coluna). Isso geralmente não importa, mas às vezes sim (por exemplo, desempenho), por que não salvar tudo?

Além disso, sua matriz numpy pode fazer parte da sua estrutura de dados, por exemplo, você possui uma lista com algumas matrizes. Para isso, você pode usar um codificador personalizado, que basicamente faz o acima.

Isso deve ser suficiente para implementar uma solução. Ou você pode usar o json-tricks, que faz exatamente isso (e suporta vários outros tipos) (aviso: eu fiz isso).

pip install json-tricks

Então

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

Eu tive um problema semelhante com um dicionário aninhado com alguns numpy.ndarrays nele.

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

Você também pode usar o defaultargumento, por exemplo:

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

json.dump(data, default=myconverter)

1

Além disso, algumas informações muito interessantes ainda mais sobre listas vs. matrizes em Python ~> Lista Python vs. Matriz - quando usar?

Pode-se observar que, depois de converter minhas matrizes em uma lista antes de salvá-las em um arquivo JSON, na minha implantação agora mesmo assim, depois de ler o arquivo JSON para uso posterior, posso continuar usando-o em uma forma de lista (como em vez de convertê-lo novamente em uma matriz).

E, na verdade, parece melhor (na minha opinião) na tela como uma lista (vírgula separada) versus uma matriz (sem vírgula separada) dessa maneira.

Usando o método .tolist () de @ travelingbones acima, eu tenho usado como tal (capturando alguns erros que encontrei também):

SALVAR DICIONÁRIO

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

LEIA O DICIONÁRIO

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 isto ajude!


1

Aqui está uma implementação que funciona para mim e removeu todas as nans (assumindo que sejam objetos simples (lista 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

Essa é uma resposta diferente, mas isso pode ajudar a ajudar as pessoas que estão tentando salvar dados e depois lê-los novamente.
Há hickle que é mais rápido que pickle e mais fácil.
Tentei salvar e ler no pickle dump, mas durante a leitura houve muitos problemas e perdi uma hora e ainda não encontrei solução, embora estivesse trabalhando nos meus próprios dados para criar um bot de bate-papo.

vec_xe vec_ysão matrizes numpy:

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

Então você acabou de ler e executar as operações:

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

1

Pode ser simples para loop com tipos de verificação:

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 que processará o json dump com sucesso. sem jogar - o array NumPy não é JSON serializável

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ão é serializador JSON

O erro acima mencionado foi lançado quando tentei passar a lista de dados para model.predict () quando esperava a resposta no 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)

Mas, felizmente, encontrei a dica para resolver o erro que estava lançando. A serialização dos objetos é aplicável apenas à conversão a seguir O mapeamento deve ser da seguinte maneira object - dict array - list string - string integer - integer

Se você rolar para cima para ver o número da linha 10 prediction = loaded_model.predict (d) em que essa linha de código estava gerando a saída do tipo array datatype, ao tentar converter a matriz para o formato json, não é possível

Finalmente, encontrei a solução convertendo a saída obtida para a lista de tipos seguindo as linhas de código

predição = loading_model.predict (d)
listtype = prediction.tolist () retorna jsonify (listtype)

Bhoom! finalmente obteve a saída esperada, insira a descrição da imagem aqui

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.