Как я могу сериализовать массив numpy при сохранении размеров матрицы?
numpy.array.tostring
, похоже, не сохраняет информацию о размерах матрицы (см. этот вопрос), требуя от пользователя вызывать вызов numpy.array.reshape
.
Есть ли способ сериализации массива numpy в формате JSON при сохранении этой информации?
Примечание. Массивы могут содержать int, floats или bools. Разумно ожидать транспонированный массив.
Примечание 2: это выполняется с целью передачи массива numpy через топологию Storm с использованием streamparse, в случае, если такая информация окажется актуальной.
Ответы
Ответ 1
pickle.dumps
или numpy.save
закодировать все информация, необходимая для восстановления произвольного массива NumPy, даже при наличии проблем с контентом, несмежных массивов или странных кортежей. Проблемы с предельной вероятностью, вероятно, наиболее важны; вы не хотите, чтобы array([1])
внезапно становился array([16777216])
, потому что вы загрузили свой массив на машине большого конца. pickle
, вероятно, является более удобным вариантом, хотя save
имеет свои преимущества, приведенные в npy
аргументах формата.
Параметр pickle
:
import pickle
a = # some NumPy array
serialized = pickle.dumps(a, protocol=0) # protocol 0 is printable ASCII
deserialized_a = pickle.loads(serialized)
numpy.save
использует двоичный формат, и его нужно записать в файл, но вы можете обойти это с помощью StringIO
:
a = # any NumPy array
memfile = StringIO.StringIO()
numpy.save(memfile, a)
memfile.seek(0)
serialized = json.dumps(memfile.read().decode('latin-1'))
# latin-1 maps byte n to unicode code point n
И для десериализации:
memfile = StringIO.StringIO()
memfile.write(json.loads(serialized).encode('latin-1'))
memfile.seek(0)
a = numpy.load(memfile)
Ответ 2
РЕДАКТИРОВАТЬ:. Как можно прочитать в комментариях к вопросу, это решение имеет дело с "нормальными" массивами numpy (float, ints, bools...), а не с многотипными структурированными массивами.
Решение для сериализации массива numpy любых размеров и типов данных
Насколько я знаю, вы не можете просто сериализовать массив numpy с любым типом данных и любым измерением... но вы можете сохранить его тип данных, размер и информацию в представлении списка, а затем сериализовать его с помощью JSON.
Необходимый импорт:
import json
import base64
Для кодирования вы можете использовать (nparray
- это некоторый массив numpy любого типа данных и любая размерность):
json.dumps([str(nparray.dtype), base64.b64encode(nparray), nparray.shape])
После этого вы получаете дамп JSON (строку) ваших данных, содержащий представление списка его типа и формы данных, а также данные массива данных/содержимого base64.
И для декодирования это делает работу (encStr
- закодированная строка JSON, загруженная откуда-то):
# get the encoded json dump
enc = json.loads(encStr)
# build the numpy data type
dataType = numpy.dtype(enc[0])
# decode the base64 encoded numpy array data and create a new numpy array with this data & type
dataArray = numpy.frombuffer(base64.decodestring(enc[1]), dataType)
# if the array had more than one data set it has to be reshaped
if len(enc) > 2:
dataArray.reshape(enc[2]) # return the reshaped numpy array containing several data sets
Дампы JSON эффективны и кросс-совместимы по многим причинам, но просто использование JSON приводит к неожиданным результатам, если вы хотите хранить и загружать массивы numpy любого типа и любого измерения.
Это решение хранит и загружает массивы numpy независимо от типа или размера, а также восстанавливает его правильно (тип данных, размерность...)
Я попробовал несколько решений самостоятельно несколько месяцев назад, и это было единственное эффективное, универсальное решение, с которым я столкнулся.
Ответ 3
Я нашел код в Msgpack-numpy полезным.
https://github.com/lebedov/msgpack-numpy/blob/master/msgpack_numpy.py
Я немного изменил сериализованный диктофон и добавил кодировку base64, чтобы уменьшить размер сериализованного размера.
Используя тот же интерфейс, что и json (обеспечивая нагрузку, дамп (ы)), вы можете предоставить замену для json-сериализации.
Эта же логика может быть расширена, чтобы добавить любую автоматическую нетривиальную сериализацию, такую как объекты datetime.
ИЗМЕНИТЬ
Я написал общий, модульный, парсер, который делает это и многое другое.
https://github.com/someones/jaweson
Мой код выглядит следующим образом:
np_json.py
from json import *
import json
import numpy as np
import base64
def to_json(obj):
if isinstance(obj, (np.ndarray, np.generic)):
if isinstance(obj, np.ndarray):
return {
'__ndarray__': base64.b64encode(obj.tostring()),
'dtype': obj.dtype.str,
'shape': obj.shape,
}
elif isinstance(obj, (np.bool_, np.number)):
return {
'__npgeneric__': base64.b64encode(obj.tostring()),
'dtype': obj.dtype.str,
}
if isinstance(obj, set):
return {'__set__': list(obj)}
if isinstance(obj, tuple):
return {'__tuple__': list(obj)}
if isinstance(obj, complex):
return {'__complex__': obj.__repr__()}
# Let the base class default method raise the TypeError
raise TypeError('Unable to serialise object of type {}'.format(type(obj)))
def from_json(obj):
# check for numpy
if isinstance(obj, dict):
if '__ndarray__' in obj:
return np.fromstring(
base64.b64decode(obj['__ndarray__']),
dtype=np.dtype(obj['dtype'])
).reshape(obj['shape'])
if '__npgeneric__' in obj:
return np.fromstring(
base64.b64decode(obj['__npgeneric__']),
dtype=np.dtype(obj['dtype'])
)[0]
if '__set__' in obj:
return set(obj['__set__'])
if '__tuple__' in obj:
return tuple(obj['__tuple__'])
if '__complex__' in obj:
return complex(obj['__complex__'])
return obj
# over-write the load(s)/dump(s) functions
def load(*args, **kwargs):
kwargs['object_hook'] = from_json
return json.load(*args, **kwargs)
def loads(*args, **kwargs):
kwargs['object_hook'] = from_json
return json.loads(*args, **kwargs)
def dump(*args, **kwargs):
kwargs['default'] = to_json
return json.dump(*args, **kwargs)
def dumps(*args, **kwargs):
kwargs['default'] = to_json
return json.dumps(*args, **kwargs)
Вы должны иметь возможность сделать следующее:
import numpy as np
import np_json as json
np_data = np.zeros((10,10), dtype=np.float32)
new_data = json.loads(json.dumps(np_data))
assert (np_data == new_data).all()
Ответ 4
Если это нужно для чтения человеком, и вы знаете, что это массив numpy:
import numpy as np;
import json;
a = np.random.normal(size=(50,120,150))
a_reconstructed = np.asarray(json.loads(json.dumps(a.tolist())))
print np.allclose(a,a_reconstructed)
print (a==a_reconstructed).all()
Может быть, не самый эффективный, поскольку размеры массива растут больше, но работают для меньших массивов.
Ответ 5
Msgpack имеет лучшую производительность сериализации: http://www.benfrederickson.com/dont-pickle-your-data/
Используйте msgpack-numpy. См. https://github.com/lebedov/msgpack-numpy
Установите его:
pip install msgpack-numpy
Тогда:
import msgpack
import msgpack_numpy as m
import numpy as np
x = np.random.rand(5)
x_enc = msgpack.packb(x, default=m.encode)
x_rec = msgpack.unpackb(x_enc, object_hook=m.decode)
Ответ 6
Попробуйте использовать numpy.array_repr
или numpy.array_str
.