Tablica NumPy nie jest serializowalna w JSON

247

Po utworzeniu tablicy NumPy i zapisaniu jej jako zmiennej kontekstowej Django podczas ładowania strony pojawia się następujący błąd:

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

Co to znaczy?

Karnivaurus
źródło
19
Oznacza to, że gdzieś coś próbuje zrzucić tablicę numpy za pomocą jsonmodułu. Ale numpy.ndarraynie jest typem, który jsonwie, jak sobie z tym poradzić. Musisz albo napisać własny serializator, albo (prościej) po prostu przejść list(your_array)do tego, co pisze json.
mgilson
24
Uwaga list(your_array)nie zawsze będzie działać, ponieważ zwraca liczby całkowite numpy, a nie wartości wewnętrzne natywne. Użyj your_array.to_list()zamiast tego.
ashishsingal
18
uwaga na temat komentarza @ ashishsingal, powinna to być twoja_tablica.tolist (), a nie to_list ().
vega

Odpowiedzi:

289

Regularnie „jsonify” np. Tablice. Najpierw spróbuj użyć metody „.tolist ()” na tablicach:

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

Aby „odrzucić” tablicę, użyj:

obj_text = codecs.open(file_path, 'r', encoding='utf-8').read()
b_new = json.loads(obj_text)
a_new = np.array(b_new)
kości podróżne
źródło
3
Dlaczego może być przechowywany tylko jako lista list?
Nikhil Prabhu
Nie wiem, ale spodziewam np.array typów metadanych, które nie pasuje do JSON (np one określić typ danych każdego wpisu jak float)
travelingbones
2
Próbowałem twojej metody, ale wygląda na to, że program utknął tolist().
Harvett,
3
@frankliuao znalazłem powód, który tolist()zajmuje dużo czasu, gdy dane są duże.
Harvett,
4
@NikhilPrabhu JSON jest JavaScript Object Notation, a zatem może reprezentować tylko podstawowe konstrukcje z języka javascript: obiekty (analogiczne do python dicts), tablice (analogiczne do list python), liczby, booleany, łańcuchy i null (analogiczne do python Nones ). Tablice Numpy nie są żadną z tych rzeczy, dlatego nie można ich przekształcić do postaci szeregowej w JSON. Niektóre można przekonwertować na formę podobną do JSO (lista list), co właśnie robi ta odpowiedź.
Chris L. Barnes,
225

Przechowuj jako JSON numpy.ndarray lub dowolną kompozycję listy zagnieżdżonej.

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)

Wyjdzie:

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

Aby przywrócić z JSON:

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

Wyjdzie:

[[1 2 3]
 [4 5 6]]
(2, 3)
karlB
źródło
26
Powinno to być znacznie wyżej, jest to ogólny i właściwie abstrakcyjny sposób robienia tego. Dzięki!
thclark
2
Czy istnieje prosty sposób na odzyskanie ndarray z listy?
DarksteelPenguin
5
@DarksteelPenguin, którego szukasz numpy.asarray()?
aeolus
3
Ta odpowiedź jest świetna i można ją łatwo rozszerzyć, aby serializować wartości numpy float32 i np.float64 jako json:if isinstance(obj, np.float32) or isinstance(obj, np.float64): return float(obj)
Bensge
To rozwiązanie pozwala uniknąć ręcznego rzutowania każdej tablicy numpy na listę.
eduardosufan
44

Możesz użyć pand :

import pandas as pd
pd.Series(your_array).to_json(orient='values')
John Zwinck
źródło
6
Wspaniały! I myślę, że dla 2D np. Tablica będzie to coś podobnego pd.DataFrame(your_array).to_json('data.json', orient='split').
nix
2
Uratowałem dzień. Dzięki
anurag
40

Znalazłem najlepsze rozwiązanie, jeśli zagnieździłeś tablice numpy w słowniku:

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)

Dzięki temu facetowi .

tsveti_iko
źródło
Dziękuję za pomocną odpowiedź! Zapisałem atrybuty do pliku json, ale mam teraz problemy z odczytaniem parametrów regresji logistycznej. Czy istnieje „dekoder” dla tego zapisanego pliku Json?
TTZ
Oczywiście, aby przeczytać odwrotną jsonstronę, możesz użyć tego with open(path, 'r') as f: data = json.load(f):, który zwraca słownik z Twoimi danymi.
tsveti_iko,
To jest do odczytu jsonpliku, a następnie do deserializacji jego danych wyjściowych, możesz użyć tego:data = json.loads(data)
tsveti_iko
Musiałem to dodać, aby obsłużyć typ danych bajtów .. zakładając, że wszystkie bajty są ciągiem utf-8. elif isinstance (obj, (bytes,)): return obj.decode ("utf-8")
Soichi Hayashi
+1. Dlaczego potrzebujemy wiersza „return json.JSONEncoder.default (self, obj)” na końcu „def default (self, obj)”?
Hans
23

Użyj json.dumps defaultkwarga:

domyślnie powinna być funkcja wywoływana dla obiektów, których inaczej nie można by serializować.

W defaultfunkcji sprawdź, czy obiekt pochodzi z modułu numpy, jeśli tak, użyj albo ndarray.tolista, ndarrayalbo użyj .itemdowolnego innego typu 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)
moshevi
źródło
Jaka jest tam rola linii type(obj).__module__ == np.__name__: ? Czy nie wystarczy sprawdzić instancję?
Ramon Martinez
@RamonMartinez, aby wiedzieć, że obiekt jest obiektem numpy, w ten sposób mogę użyć .itemdo prawie każdego obiektu numpy. defaultfunkcja jest wywoływana dla wszystkich json.dumpsprób serializacji nieznanych typów . nie tylko numpy
moshevi
5

To nie jest domyślnie obsługiwane, ale możesz sprawić, że będzie działać dość łatwo! Jest kilka rzeczy, które chcesz zakodować, jeśli chcesz odzyskać dokładnie te same dane:

  • Same dane, które można uzyskać obj.tolist()za pomocą wspomnianych @travelingbones. Czasami może to być wystarczająco dobre.
  • Typ danych Uważam, że jest to ważne w niektórych przypadkach.
  • Wymiar (niekoniecznie 2D), który można wyprowadzić z powyższego, zakładając, że dane wejściowe są rzeczywiście zawsze „prostokątną” siatką.
  • Kolejność pamięci (główny wiersz lub kolumna). To często nie ma znaczenia, ale czasami ma (np. Wydajność), więc dlaczego nie zapisać wszystkiego?

Ponadto tablica numpy może stanowić część struktury danych, np. Masz listę z pewnymi macierzami. W tym celu możesz użyć niestandardowego kodera, który zasadniczo robi powyższe.

To powinno wystarczyć do wdrożenia rozwiązania. Lub możesz użyć sztuczek json, które właśnie to robią (i obsługują różne inne typy) (zastrzeżenie: zrobiłem to).

pip install json-tricks

Następnie

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))
znak
źródło
3

Miałem podobny problem z zagnieżdżonym słownikiem z kilkoma 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
JLT
źródło
3

Możesz także użyć defaultargumentu na przykład:

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

json.dump(data, default=myconverter)
steco
źródło
1

Ponadto, kilka bardzo interesujących informacji na temat list vs. tablic w Python ~> Python List vs. Array - kiedy używać?

Można zauważyć, że kiedy przekonwertuję moje tablice na listę przed zapisaniem go w pliku JSON, w moim wdrożeniu i tak właśnie teraz, po przeczytaniu tego pliku JSON do użytku później, mogę nadal używać go w formie listy (jako zamiast konwertować go z powrotem do tablicy).

I faktycznie wygląda ładniej (moim zdaniem) na ekranie jako lista (rozdzielana przecinkami) w porównaniu do tablicy (rozdzielana przecinkami) w ten sposób.

Korzystając z powyższej metody .tolist () w @ travelbones, używałem jako takiej (wyłapałem kilka znalezionych błędów):

ZAPISZ SŁOWNIK

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

CZYTAJ SŁOWNIK

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

Mam nadzieję że to pomoże!

ntk4
źródło
1

Oto implementacja, która działa dla mnie i usunęła wszystkie nans (zakładając, że są to proste obiekty (lista lub dyktowanie)):

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
Roei Bahumi
źródło
1

To inna odpowiedź, ale może to pomóc osobom, które próbują zapisać dane, a następnie przeczytać je ponownie.
Jest hickle, który jest szybszy niż marynowany i łatwiejszy.
Próbowałem zapisać i odczytać go na zrzutie pikli, ale podczas czytania było wiele problemów i zmarnowałem godzinę i wciąż nie znalazłem rozwiązania, chociaż pracowałem nad własnymi danymi, aby stworzyć bota czatu.

vec_xi vec_ysą tablicami numpy:

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

Następnie wystarczy go przeczytać i wykonać operacje:

data2 = hkl.load( 'new_data_file.hkl' )
KS HARSHA
źródło
1

Może zrobić prostą pętlę ze sprawdzaniem typów:

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()
Robert GRZELKA
źródło
1

użyj NumpyEncodera z powodzeniem przetworzy zrzut jsona. bez rzucania - tablica NumPy nie jest serializowana 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)
krishna kumar mishra
źródło
0

Błąd typu: tablica ([[0.46872085, 0.67374235, 1.0218339, 0.13210179, 0.5440686, 0.9140083, 0.58720225, 0.2199381]], dtype = float32) nie jest serializowalny w JSON

Wyżej wymieniony błąd został zgłoszony, gdy próbowałem przekazać listę danych do model.predict (), gdy oczekiwałem odpowiedzi w formacie 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)

Ale na szczęście udało się znaleźć wskazówkę dotyczącą rozwiązania zgłaszanego błędu. Serializacja obiektów ma zastosowanie tylko w przypadku następnej konwersji. Mapowanie powinno odbywać się w następujący sposób: obiekt - tablica dict - ciąg list - liczba całkowita string - liczba całkowita

Jeśli przewiniesz w górę, aby zobaczyć prognozę numer 10 = load_model.predict (d), gdzie ten wiersz kodu generował dane wyjściowe typu danych typu tablica, kiedy spróbujesz przekonwertować tablicę na format json, nie jest to możliwe

W końcu znalazłem rozwiązanie, konwertując uzyskane dane wyjściowe do listy typów za pomocą następujących wierszy kodu

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

Bhoom! w końcu uzyskał oczekiwany wynik, wprowadź opis zdjęcia tutaj

Poornima Subramani Naidu
źródło