Массив NumPy не поддерживает сериализацию в формате JSON


249

Создав массив 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.
Мгилсон

24
Примечание list(your_array)не всегда будет работать, так как возвращает целые числа, а не нативные. Используйте your_array.to_list()вместо этого.
ашишсингал

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

Для «unjsonify» массива используйте:

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().
Харветт

3
@frankliuao Я обнаружил, что причина в том, что tolist()при больших объемах данных уходит огромное количество времени.
Харветт

4
@NikhilPrabhu JSON - это объектная нотация Javascript, и поэтому он может представлять только базовые конструкции из языка javascript: объекты (аналогично python dicts), массивы (аналогично спискам python), числа, логические значения, строки и нули (аналогично python Nones). ). Numpy массивы не являются ни одной из этих вещей, и поэтому не могут быть сериализованы в JSON. Некоторые могут быть преобразованы в JSO-подобную форму (список списков), что и делает этот ответ.
Крис Л. Барнс

226

Сохраните как 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
Это должно быть намного выше, это общий и правильно абстрагированный способ сделать это. Спасибо!
Спасибо

2
Есть ли простой способ вернуть ndarray из списка?
DarksteelPenguin

5
@DarksteelPenguin вы ищете numpy.asarray()?
Эол

3
Этот ответ великолепен и может быть легко расширен для сериализации значений numpy float32 и np.float64 как json:if isinstance(obj, np.float32) or isinstance(obj, np.float64): return float(obj)
Bensge

Это решение позволяет избежать приведения каждого массива к списку вручную.
Эдуардосуфан

44

Вы можете использовать панд :

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').
Никс

2
Спас день Спасибо
анураг

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?
TTZ

Конечно, чтобы прочитать jsonответ, вы можете использовать это:, with open(path, 'r') as f: data = json.load(f)который возвращает словарь с вашими данными.
tsveti_iko

Это для чтения jsonфайла, а затем для десериализации его вывода, вы можете использовать это:data = json.loads(data)
tsveti_iko

Я должен был добавить это для обработки байтов типа данных ... при условии, что все байты являются строкой utf-8. elif isinstance (obj, (bytes,)): вернуть obj.decode ("utf-8")
Соичи Хаяси

+1. Зачем нам нужна строка «return json.JSONEncoder.default (self, obj)» в конце «def default (self, obj)»?
Ганс

23

Используйте json.dumps defaultkwarg:

default должна быть функция, которая вызывается для объектов, которые иначе не могут быть сериализованы.

В 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, чтобы знать, что объект является объектом numpy, я могу использовать его .itemпрактически для любого объекта numpy. defaultФункция вызывается для всех неизвестных типов json.dumpsпопыток сериализации. не просто
numy

6

Это не поддерживается по умолчанию, но вы можете сделать это довольно легко! Есть несколько вещей, которые вы хотите закодировать, если вы хотите вернуть те же самые данные:

  • Сами данные, которые вы можете получить с obj.tolist() как упомянуто @travelingbones. Иногда это может быть достаточно хорошо.
  • Тип данных. Я чувствую, что это важно в некоторых случаях.
  • Измерение (не обязательно 2D), которое может быть получено из вышеприведенного, если вы предполагаете, что ввод действительно всегда является «прямоугольной» сеткой.
  • Порядок памяти (строка или столбец). Это не часто имеет значение, но иногда это имеет значение (например, производительность), так почему бы не сохранить все?

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

Этого должно быть достаточно для реализации решения. Или вы можете использовать json-tricks, который делает именно это (и поддерживает различные другие типы) (отказ от ответственности: я сделал это).

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 List vs. Array - когда использовать?

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

И на самом деле выглядит лучше (на мой взгляд) на экране как список (разделенный запятой), так и массив (не разделенный запятой) таким образом.

Используя метод @tallist () @ travelbones выше, я использовал его как таковой (обнаружив несколько ошибок, которые я тоже нашел):

СОХРАНИТЬ СЛОВАРЬ

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

Вот реализация, которая работает для меня и удаляет все nans (при условии, что это простой объект (list или 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

Это другой ответ, но это может помочь людям, которые пытаются сохранить данные, а затем прочитать их снова.
Есть хикл, который быстрее, чем маринад, и легче.
Я пытался сохранить и прочитать его в дампе маринада, но во время чтения было много проблем, потраченных впустую час, и я так и не нашел решения, хотя работал над своими данными, чтобы создать чат-бота.

vec_xи vec_yявляются массивами NumPy:

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

Ошибка типа: массив ([[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)

Но, к счастью, нашла подсказку, чтобы разрешить ошибку, которая выдает

Если вы прокрутите вверх, чтобы увидеть строку с номером 10pretion =loaded_model.predict (d), где эта строка кода генерировала выходные данные типа массива данных типа, при попытке преобразовать массив в формат json это невозможно

Наконец, я нашел решение, просто преобразовав полученный вывод в список типов с помощью следующих строк кода

предсказание = загруженная_модель.predict (d)
listtype = предсказание.tolist () возвращение jsonify (список)

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.