Масив NumPy не є JSON-серіалізаційним


247

Після створення масиву NumPy та збереження його як змінної контексту Django я отримую таку помилку під час завантаження веб-сторінки:

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

Що це означає?


19
Це означає, що десь щось намагається скинути нумерований масив за допомогою jsonмодуля. Але numpy.ndarrayце не тип, який jsonвміє поводитися. Вам або потрібно буде написати власний серіалізатор, або (простіше кажучи) просто перейти list(your_array)до того, що пише json.
mgilson

24
Примітка list(your_array)не завжди працюватиме, оскільки вона повертає нумеровані вставки, а не рідні вставки. Використовуйте your_array.to_list()замість цього.
ashishsingal

18
примітка про коментар @ ashishsingal, це має бути your_array.tolist (), а не to_list ().
вега

Відповіді:


289

Я регулярно "jsonify" np.arrays. Спробуйте спочатку скористатися методом ".tolist ()" на масивах, як це:

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

Для того, щоб "відключити" масив, використовуйте:

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

3
Чому він може зберігатися лише як список списків?
Нікхіл Прабху

Я не знаю , але я очікую np.array типів мають метадані , які не вписуються у формат JSON (наприклад , вони визначають тип даних кожного запису , як поплавок)
travelingbones

2
Я спробував ваш метод, але, схоже, програма застрягла tolist().
Harvett

3
@frankliuao Я знайшов причину в тому tolist() для великих даних потрібно багато часу.
Харветт

4
@NikhilPrabhu JSON є нотацією Javascript Object, і тому може представляти лише основні конструкції з мови javascript: об'єкти (аналогічні диктовам python), масиви (аналогічні спискам python), числа, булеви, рядки та нулі (аналогічно python Nones ). Numpy масиви - це не одна з цих речей, і тому їх неможливо серіалізувати в JSON. Деякі з них можуть бути перетворені у форму, схожу на JSO (список списків), що і робить ця відповідь.
Кріс Л. Барнс

225

Зберігайте як JSON numpy.ndarray або будь-яку композицію вкладеного списку.

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)

Виведе:

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

Щоб відновити з JSON:

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

Виведе:

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

26
Це повинно бути вище вгору дошки, це загальний і правильно абстрагований спосіб зробити це. Дякую!
thclark

2
Чи є простий спосіб повернути ndarray зі списку?
DarksteelPenguin

5
@DarksteelPenguin ви шукаєте numpy.asarray()?
aeolus

3
Ця відповідь чудова і її можна легко розширити, щоб також серіалізувати значення numat float32 та np.float64 як json:if isinstance(obj, np.float32) or isinstance(obj, np.float64): return float(obj)
Bensge

Це рішення дозволяє вам не вручну вводити в список кожен масивний масив до списку.
eduardosufan

44

Ви можете використовувати Pandas :

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

6
Чудово! І я думаю, що для 2D np.array це буде щось подібне pd.DataFrame(your_array).to_json('data.json', orient='split').
nix

2
Врятував день. Спасибі
anurag

40

Я знайшов найкраще рішення, якщо ви вклали нумерові масиви у словник:

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)

Завдяки цьому хлопцю .


Дякуємо за корисну відповідь! Я записав атрибути до файлу json, але зараз у мене виникають проблеми з читанням параметрів для логістичної регресії. Чи є 'декодер' для цього збереженого файлу json?
ТТЗ

Звичайно, для читання jsonзворотного боку ви можете скористатися цим:, with open(path, 'r') as f: data = json.load(f)який повертає словник із вашими даними.
tsveti_iko

Це для того, щоб прочитати jsonфайл, а потім, щоб десаріалізувати його вихід, ви можете скористатися цим:data = json.loads(data)
tsveti_iko

Мені довелося додати це для обробки байтів даних типу .. припускаючи, що всі байти - це строка utf-8. elif isin substance (obj, (bytes,)): return obj.decode ("utf-8")
Soichi Hayashi

+1. Навіщо нам потрібен рядок "return json.JSONEncoder.default (self, obj)" в кінці "def default (self, obj)"?
Ганс

23

Використовуйте json.dumps defaultkwarg:

за замовчуванням має бути функція, яка викликає об'єкти, які інакше не можуть бути серіалізовані.

У defaultфункції перевірте, чи об'єкт від модуля numpy, якщо так, або використовувати ndarray.tolistдля a, ndarrayабо використовувати .itemдля будь-якого іншого типу 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)

Яка роль лінії type(obj).__module__ == np.__name__: там? Чи не буде достатньо перевірити примірник?
Рамон Мартінес

@RamonMartinez, щоб знати, що об’єкт - це нумерований об’єкт, таким чином я можу використовувати .itemмайже будь-який об’єкт numpy. defaultФункція викликається для всіх невідомих типів json.dumpsспроб серіалізації. не просто нудний
мошеви

5

Це не підтримується за замовчуванням, але ви можете змусити його працювати досить легко! Є кілька речей, які ви хочете кодувати, якщо ви хочете повернути ті самі дані:

  • Самі дані, які ви можете отримати obj.tolist() як згадуються @travelingbones. Іноді це може бути досить добре.
  • Тип даних. Я вважаю, що це важливо в деяких випадках.
  • Розмір (не обов'язково 2D), який може бути отриманий із зазначеного вище, якщо ви припускаєте, що вхід дійсно завжди є «прямокутною» сіткою.
  • Порядок пам'яті (основний рядок або стовпець). Це часто не має значення, але іноді це є (наприклад, продуктивність), так чому б не зберегти все?

Крім того, ваш numpy масив може бути частиною вашої структури даних, наприклад, у вас є список з деякими матрицями всередині. Для цього ви можете використовувати спеціальний кодер, який в основному робить вищезазначене.

Цього має бути достатньо для реалізації рішення. Або ви можете використовувати json-трюки, які роблять саме це (і підтримують різні інші типи) (відмова: я зробив це).

pip install json-tricks

Тоді

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

У мене була схожа проблема з вкладеним словником з деякими 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

Ви також можете використовувати defaultаргумент, наприклад:

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

json.dump(data, default=myconverter)

1

Крім того, деякі дуже цікаві відомості далі про списки та масиви в Python ~> Список Python проти масиву - коли використовувати?

Можна зазначити, що як тільки я перетворюю свої масиви у список, перш ніж зберігати його у файлі JSON, у моєму розгортанні зараз будь-коли, як тільки я прочитаю, що файл JSON для подальшого використання, я можу продовжувати використовувати його у формі списку (як на противагу перетворенню його назад у масив).

І насправді виглядає приємніше (на мій погляд) на екрані як список (відокремлений комами) проти масиву (не відокремлений комами) таким чином.

Використовуючи вищевказаний метод @ putbones's .tolist (), я використовував як такий (вловлював декілька помилок, які я теж знайшов):

ЗБЕРІГАТИ СЛОВНИК

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

ЧИТАТИ СЛОВНИК

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

Сподіваюсь, це допомагає!


1

Ось реалізація, яка працює для мене та видалила всіх нан (якщо припустити, що це простий об’єкт (список чи диктант)):

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

Це інша відповідь, але це може допомогти людям, які намагаються зберегти дані, а потім прочитати їх знову.
Існує жировик, який швидше соління і простіше.
Я спробував зберегти і прочитати це на смітнику, але під час читання виникло багато проблем і витрачався витрачений годину, і досі не знайшов рішення, хоча я працював над власними даними, щоб створити бот-чат.

vec_xі vec_yє нумерованими масивами:

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

Потім ви просто прочитаєте його і виконайте операції:

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

1

Можливо зробити цикл простим для перевірки типів:

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

використовуйте NumpyEncoder, він обробляє дамп json успішно. Без викидання - масив NumPy не є серіалізаційним 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: масив ([[0.46872085, 0.67374235, 1.0218339, 0.13210179, 0.5440686, 0.9140083, 0.58720225, 0.2199381]], dtype = float32) не є JSON серіалізабельним

Вищезгадана помилка була видана, коли я намагався передати список даних до model.predict (), коли я очікував відповіді у форматі 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)

Але, на щастя, знайдено підказку, щоб вирішити помилку, яка була викинута. Серіалізація об'єктів застосовна лише для наступних перетворень. Картографування має бути таким чином об’єкт - dict array - list string - string integer - integer

Якщо ви прокрутите вгору, щоб побачити рядок номер 10 prediction = load_model.predict (d), де цей рядок коду генерував вихід типу типу масиву масиву, коли ви намагаєтеся перетворити масив у формат json, його неможливо

Нарешті, я знайшов рішення, просто перетворивши отриманий результат у список типів наступними рядками коду

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

Bhoom! нарешті отримали очікуваний вихід, введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.