NumPy数组不是JSON可序列化的

Kar*_*rus 196 python django json numpy

创建NumPy数组并将其保存为Django上下文变量后,加载网页时收到以下错误:

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

这是什么意思?

tra*_*nes 237

我经常"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
Run Code Online (Sandbox Code Playgroud)

为了"unjsonify"数组使用:

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

  • @frankliuao 我发现原因是当数据很大时,`tolist()` 会花费大量时间。 (6认同)
  • 为什么它只能存储为列表列表? (3认同)
  • 我不知道,但我希望 np.array 类型具有不适合 json 的元数据(例如,它们指定每个条目的数据类型,如 float) (2认同)
  • 我试过你的方法,但程序似乎卡在了`tolist()`。 (2认同)
  • @NikhilPrabhu JSON是Javascript对象表示法,因此只能表示javascript语言的基本构造:对象(类似于python dict),数组(类似于python列表),数字,布尔值,字符串和null(类似于python无) )。numpy数组不是所有这些东西,因此无法序列化为JSON。有些答案可以转换成类似JSO的形式(列表列表),这就是答案。 (2认同)

小智 169

存储为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)
Run Code Online (Sandbox Code Playgroud)

将输出:

(2, 3)
{"a": [[1, 2, 3], [4, 5, 6]], "aa": [2, [2, 3, 4], [[1, 2, 3], [4, 5, 6]]], "bb": [2]}
Run Code Online (Sandbox Code Playgroud)

要从JSON还原:

json_load = json.loads(json_dump)
a_restored = np.asarray(json_load["a"])
print(a_restored)
print(a_restored.shape)
Run Code Online (Sandbox Code Playgroud)

将输出:

[[1 2 3]
 [4 5 6]]
(2, 3)
Run Code Online (Sandbox Code Playgroud)

  • 这应该是更高的董事会,这是通用和适当抽象的方式.谢谢! (20认同)
  • 这应该是公认的答案,更简洁的做事方式. (5认同)
  • 这个答案很棒,也可以轻松扩展以将 numpy float32 和 np.float64 值序列化为 json:`if isinstance(obj, np.float32) or isinstance(obj, np.float64): return float(obj)` (5认同)
  • @DarksteelPenguin你在寻找[`numpy.asarray()`](https://docs.scipy.org/doc/numpy-1.10.4/reference/generated/numpy.asarray.html)? (3认同)
  • 有没有一种简单的方法可以从列表中取回 ndarray? (2认同)

Joh*_*nck 35

你可以使用熊猫:

import pandas as pd
pd.Series(your_array).to_json(orient='values')
Run Code Online (Sandbox Code Playgroud)

  • 大!我认为对于2D np.array,它将类似于`pd.DataFrame(your_array).to_json('data.json',orient ='split')`. (3认同)
  • 挽救了这一天。谢谢 (2认同)

tsv*_*iko 29

如果在字典中嵌套了numpy数组,我找到了最佳解决方案:

import json
import numpy as np

class NumpyEncoder(json.JSONEncoder):
    """ Special json encoder for numpy types """
    def default(self, obj):
        if isinstance(obj, (np.int_, np.intc, np.intp, np.int8,
            np.int16, np.int32, np.int64, np.uint8,
            np.uint16, np.uint32, np.uint64)):
            return int(obj)
        elif isinstance(obj, (np.float_, np.float16, np.float32, 
            np.float64)):
            return float(obj)
        elif isinstance(obj,(np.ndarray,)): #### This is the fix
            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)
Run Code Online (Sandbox Code Playgroud)

谢谢你这个家伙.


mos*_*evi 13

其他一些numpy编码器似乎过于冗长。

使用json.dumps defaultkwarg:

default应该是一个为无法序列化的对象调用的函数。

default函数中,检查对象是否来自numpy模块,如果是,则将其ndarray.tolist用于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)
Run Code Online (Sandbox Code Playgroud)


JLT*_*JLT 5

我对其中有一些 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
Run Code Online (Sandbox Code Playgroud)


Mar*_*ark 5

默认情况下不支持此功能,但是您可以使其轻松工作!如果您想返回完全相同的数据,则需要对几件事进行编码:

  • 数据本身,您可以通过obj.tolist()@travelingbones 获得。有时这可能已经足够了。
  • 数据类型。我觉得在某些情况下这很重要。
  • 如果假设输入确实始终是“矩形”网格,则可以从上面得出尺寸(不一定是2D)。
  • 内存顺序(行或列为主)。这通常并不重要,但有时却很重要(例如性能),那么为什么不保存所有内容呢?

此外,您的numpy数组可能是数据结构的一部分,例如,您有一个包含一些矩阵的列表。为此,您可以使用自定义编码器,基本上可以完成上述操作。

这应该足以实施解决方案。或者,您可以使用json-tricks来做到这一点(并支持其他各种类型)(免责声明:我做到了)。

pip install json-tricks
Run Code Online (Sandbox Code Playgroud)

然后

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))
Run Code Online (Sandbox Code Playgroud)