Самый быстрый способ хранить большие файлы в Python - PullRequest
12 голосов
/ 04 октября 2011

Я недавно задал вопрос о том, как сохранить большие объекты Python в файл.Ранее я сталкивался с проблемами при преобразовании массивных словарей Python в строку и записи их в файл через write().Сейчас я использую рассол.Хотя это работает, файлы невероятно велики (> 5 ГБ).У меня мало опыта в области таких больших файлов.Я хотел знать, будет ли быстрее, или даже возможно, сжать этот файл рассола перед сохранением его в памяти.

Ответы [ 5 ]

10 голосов
/ 04 октября 2011

Вы можете сжать данные с помощью bzip2 :

from __future__ import with_statement # Only for Python 2.5
import bz2,json,contextlib

hugeData = {'key': {'x': 1, 'y':2}}
with contextlib.closing(bz2.BZ2File('data.json.bz2', 'wb')) as f:
  json.dump(hugeData, f)

Загрузить его так:

from __future__ import with_statement # Only for Python 2.5
import bz2,json,contextlib

with contextlib.closing(bz2.BZ2File('data.json.bz2', 'rb')) as f:
  hugeData = json.load(f)

Вы также можете сжать данные с помощью zlib или gzip с почти таким же интерфейсом.Тем не менее, степень сжатия как zlib, так и gzip будет ниже, чем та, которая достигается с помощью bzip2 (или lzma).

4 голосов
/ 04 октября 2011

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

Помимо cPickle, вы найдете модуль marshal, который работает намного быстрее. Но для этого нужен настоящий дескриптор файла (не из файлового объекта). Вы можете import marshal as Pickle и увидеть разницу. Я не думаю, что вы можете сделать собственный сериализатор, который намного быстрее, чем этот ...

Вот фактический (не очень старый) серьезный тест сериализаторов Python

1 голос
/ 06 мая 2014

Я бы просто расширил ответ phihag.

При попытке сериализации объекта, приближающегося к размеру оперативной памяти, pickle / cPickle следует избегать , поскольку для требуются дополнительныепамять в 1-2 раза больше объекта для сериализации.Это правда даже при потоковой передаче в BZ2File.В моем случае у меня даже заканчивалось пространство подкачки.

Но проблема с JSON (и аналогично с файлами HDF, как упомянуто в связанной статье) заключается в том, что он не может сериализовать кортежи, которые в моих данных используются какключи к диктовке.Там нет отличного решения для этого;лучшее, что я смог найти, - это преобразовать кортежи в строки, что требует некоторой памяти, но гораздо меньше, чем pickle.В настоящее время вы также можете использовать библиотеку ujson , которая намного быстрее, чем библиотека json.

Для кортежей, состоящих из строк (требуется, чтобы строки не содержали запятых):

import ujson as json
from bz2 import BZ2File

bigdata = { ('a','b','c') : 25, ('d','e') : 13 }
bigdata = dict([(','.join(k), v) for k, v in bigdata.viewitems()]) 

f = BZ2File('filename.json.bz2',mode='wb')
json.dump(bigdata,f)
f.close()

Чтобы перекомпоновать кортежи:

bigdata = dict([(tuple(k.split(',')),v) for k,v in bigdata.viewitems()])

В качестве альтернативы, если, например, ваши ключи представляют собой 2 кортежа целых чисел:

bigdata2 = { (1,2): 1.2, (2,3): 3.4}
bigdata2 = dict([('%d,%d' % k, v) for k, v in bigdata2.viewitems()])
# ... save, load ...
bigdata2 = dict([(tuple(map(int,k.split(','))),v) for k,v in bigdata2.viewitems()])

Еще одно преимущество этого подхода перед рассолом состоит в том, чтоjson, по-видимому, сжимает значительно лучше, чем соленья при использовании сжатия bzip2.

1 голос
/ 04 октября 2011

быстрее, или даже возможно, сжать этот файл рассола до [записи]

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

См. http://docs.python.org/library/gzip.html. По сути, вы создаете особый вид потока с

gzip.GzipFile("output file name", "wb")

, а затем используйте его точно так же, как обычный file, созданный с помощью open(...) (или file(...) в этом отношении).

0 голосов
/ 04 октября 2011

Посмотрите на Google ProtoBuffers .Хотя они не предназначены для готовых больших файлов, таких как аудио-видео файлы, они хорошо работают с сериализацией объектов, как в вашем случае, потому что они были разработаны для этого.Практика показывает, что однажды вам может понадобиться обновить структуру ваших файлов, и ProtoBuffers справится с этим.Кроме того, они высоко оптимизированы для сжатия и скорости.И вы не привязаны к Python, Java и C ++ хорошо поддерживаются.

...