Похоже, ваша единственная реальная проблема с JSON - это способ кодирования массивов NumPy (и таблиц Pandas). JSON не идеален для вашего варианта использования - не потому, что он медленно обрабатывает данные NumPy, а потому, что это текстовый формат, и у вас есть много данных, которые легче кодировать в нетекстовом формате.
Итак, я покажу вам способ решения всех ваших проблем с JSON ниже ... но я бы предложил использовать другой формат.
Два основных двоичных формата JSON: BJSON и BSON, стремятся предоставить большинство преимуществ JSON (простой, безопасный, динамический / бессхемный, проходимый и т. д.), а также сделать возможным прямое встраивание двоичных данных. (Тот факт, что они также являются двоичными, а не текстовыми форматами, в данном случае для вас не особо важен.) Я считаю, что то же самое верно и для Улыбнись, но никогда этим не пользовался.
Это означает, что точно так же, как JSON позволяет легко подключать все, что вы можете свести к строкам, числам с плавающей запятой, спискам и диктовкам, BJSON и BSON позволяют легко подключать все, что вы можете свести к строкам, числам с плавающей запятой, спискам, диктовкам. , и байтовые строки. Итак, когда я показываю, как кодировать / декодировать NumPy в строки, то же самое работает для байтовых строк, но без всех дополнительных шагов в конце.
Недостатки BJSON и BSON в том, что они не читаются человеком и не имеют такой широкой поддержки.
Я понятия не имею, как вы в настоящее время кодируете свои массивы, но, судя по таймингу, я подозреваю, что вы используете метод tolist
или что-то подобное. Это определенно будет медленным и большим. И он даже потеряет информацию, если вы где-то храните что-либо, кроме f8
значений (потому что JSON понимает только числа типа IEEE double). Решение заключается в кодировании в строку.
NumPy имеет текстовый формат, который будет быстрее и без потерь, но все же, вероятно, медленнее и больше, чем вы хотите.
Он также имеет двоичный формат, что здорово ... но не имеет достаточной информации для восстановления исходного массива.
Итак, давайте посмотрим, что использует pickle
, что вы можете увидеть, вызвав метод __reduce__
для любого объекта: в основном, это тип, форма, dtype, некоторые флаги, которые сообщают NumPy, как интерпретировать необработанные данные, а затем двоичный -форматировать необработанные данные. Фактически вы можете сами закодировать __reduce__
данные - на самом деле, это может стоить того. Но давайте сделаем что-нибудь попроще для пояснения, понимая, что это будет работать только на ndarray
и не будет работать на машинах с другим порядком байтов (или в более редких случаях, таких как целые числа знака или числа с плавающей запятой, не относящиеся к IEEE).
def numpy_default(obj):
if isinstance(obj, np.ndarray):
return {'_npdata': obj.tostring(),
'_npdtype': obj.dtype.name,
'_npshape': obj.shape}
else:
return json.dumps(obj)
def dumps(obj):
return json.dumps(obj, default=numpy_default)
def numpy_hook(obj):
try:
data = obj['_npdata']
except AttributeError:
return obj
return np.fromstring(data, obj['_npdtype']).reshape(obj['_npshape'])
def loads(obj):
return json.loads(obj, object_hook=numpy_hook)
Единственная проблема в том, что np.tostring
дает вам 'bytes'
объектов, с которыми json
Python 3 не знает, как справиться.
На этом вы можете остановиться, если используете что-то вроде BJSON или BSON. Но с JSON вам нужны строки.
Вы можете легко это исправить, хотя и хакерски, «декодируя» байты с помощью любой кодировки, которая отображает каждый однобайтовый символ, например Latin-1: измените obj.tostring()
на obj.tostring().decode('latin-1')
и data = obj['_npdata']
на data = obj['_npdata'].encode('latin-1')
. Это тратит немного места на кодировку UTF-8 поддельных строк Latin-1, но это не слишком плохо.
К сожалению, Python будет кодировать каждый символ, отличный от ASCII, с помощью escape-последовательности Unicode. Вы можете отключить это, установив ensure_ascii=False
для дампа и strict=False
для загрузки, но он по-прежнему будет кодировать управляющие символы, в основном в 6-байтовые последовательности. Это удваивает размер случайных данных, и это может сделать намного хуже - например, массив с нулевыми значениями будет в 6 раз больше!
Раньше существовал трюк, чтобы обойти эту проблему, но в 3.3 он не работает. Лучшее, что вы можете сделать, - это форкнуть или исправить пакет json
, чтобы он позволял передавать управляющие символы при задании ensure_ascii=False
, что можно сделать следующим образом:
json.encoder.ESCAPE = re.compile(r'"')
Это довольно хитроумно, но работает.
В любом случае, надеюсь, этого достаточно, чтобы вы начали.
person
abarnert
schedule
11.11.2013
pickle.dumps
занял 35,6 мс иjson.dumps
71,6 мс. Вас беспокоят накладные расходы на производительность? Или есть что-то другое в ваших данных? (По результатам быстрого теста создание 100 тыс. Копий того же спискаpickle
значительно ускоряет, ноjson
совсем нет; это то, с чем вы имеете дело?) - person abarnert   schedule 11.11.2013