Загрузка большого словаря с использованием Python pickle - PullRequest
9 голосов
/ 18 октября 2010

У меня есть полный инвертированный индекс в виде словаря вложенных питонов.Его структура:

{word : { doc_name : [location_list] } }

Например, пусть словарь будет называться индексом, тогда для слова «спам» запись будет выглядеть так:

{ spam : { doc1.txt : [102,300,399], doc5.txt : [200,587] } }

Я использовал эту структуру как pythondict довольно оптимизированы, и это облегчает программирование.

для любого слова «спам», документы, содержащие его, могут быть заданы следующим образом:

index['spam'].keys()

и список публикации для документа doc1 следующим образом:

index['spam']['doc1']

В настоящее время я использую cPickle для хранения и загрузки этого словаря.Но размер загруженного файла составляет около 380 МБ, и его загрузка занимает много времени - 112 секунд (прибл. Я рассчитал время, используя time.time () ), а использование памяти - 1,2 ГБ (монитор системы Gnome).Как только он загружается, это нормально.У меня 4 ГБ ОЗУ.

len(index.keys()) дает 229758

Код

import cPickle as pickle

f = open('full_index','rb')
print 'Loading index... please wait...'
index = pickle.load(f)  # This takes ages
print 'Index loaded. You may now proceed to search'

Как мне сделать так, чтобы он загружался быстрее? Мне нужно толькозагрузить его один раз, когда приложение запускается.После этого важно время доступа для ответа на запросы.

Должен ли я переключиться на базу данных, такую ​​как SQLite, и создать индекс по ее ключам?Если да, как мне сохранить значения, чтобы иметь эквивалентную схему, что облегчает поиск.Есть ли еще что-то, на что я должен обратить внимание?

Приложение

Используя ответ Тима pickle.dump(index, file, -1), маринованный файл значительно меньше - около 237 МБ (потребовалось 300 секунд для выгрузки) ... изагрузка занимает сейчас половину времени (61 секунда ... в отличие от 112 с ранее .... time.time () )

Но я должен перейти на базу данных для масштабируемости?

На данный момент я отмечаю ответ Тима как принятый.

PS: я не хочу использовать Lucene или Xapian ... Этот вопрос относится к Хранение инвертированного индекса .Мне пришлось задать новый вопрос, потому что я не смог удалить предыдущий.

Ответы [ 5 ]

12 голосов
/ 18 октября 2010

Попробуйте аргумент протокола при использовании cPickle.dump / cPickle.dumps.From cPickle.Pickler.__doc__:

Pickler (file, protocol = 0) - Создать средство выбора.

Это принимает файл-подобный объект для записи потока данных рассола.Необязательный аргумент proto указывает сборщику использовать данный протокол;поддерживаемые протоколы: 0, 1, 2. Протокол по умолчанию - 0, для обратной совместимости.(Протокол 0 является единственным протоколом, который может быть записан в файл, открытый в текстовом режиме и успешно считанный обратно. При использовании протокола, превышающего 0, убедитесь, что файл открыт в двоичном режиме, как при извлечении, так и при извлечении.)

Протокол 1 более эффективен, чем протокол 0;протокол 2 более эффективен, чем протокол 1.

При указании отрицательной версии протокола выбирается максимальная поддерживаемая версия протокола.Чем выше используемый протокол, тем свежее версия Python, необходимая для чтения созданного pickle.

Параметр file должен иметь метод write (), который принимает один строковый аргумент.Таким образом, это может быть открытый файловый объект, объект StringIO или любой другой пользовательский объект, который соответствует этому интерфейсу.

Преобразование JSON или YAML, вероятно, займет больше времени, чем выборка, большую часть времени - выборка сохраняет собственныеТипы Python.

5 голосов
/ 11 августа 2014

Вы действительно нуждаетесь в этом, чтобы загрузить все сразу?Если вам не нужно все это в памяти, а только выбранные части, которые вы хотите в любой момент времени, вы можете сопоставить свой словарь с набором файлов на диске, а не с одним файлом ... или сопоставить диктовку стаблица базы данных.Итак, если вы ищете что-то, что сохраняет большие словари данных на диск или в базу данных, и может использовать выборку и кодирование (кодеки и хеш-карты), то вы можете посмотреть на klepto.

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

>>> from klepto.archives import dir_archive
>>> d = {'a':1, 'b':2, 'c':map, 'd':None}
>>> # map a dict to a filesystem directory
>>> demo = dir_archive('demo', d, serialized=True) 
>>> demo['a']
1
>>> demo['c']
<built-in function map>
>>> demo          
dir_archive('demo', {'a': 1, 'c': <built-in function map>, 'b': 2, 'd': None}, cached=True)
>>> # is set to cache to memory, so use 'dump' to dump to the filesystem 
>>> demo.dump()
>>> del demo
>>> 
>>> demo = dir_archive('demo', {}, serialized=True)
>>> demo
dir_archive('demo', {}, cached=True)
>>> # demo is empty, load from disk
>>> demo.load()
>>> demo
dir_archive('demo', {'a': 1, 'c': <built-in function map>, 'b': 2, 'd': None}, cached=True)
>>> demo['c']
<built-in function map>
>>> 

klepto также имеет другие флаги, такие как compression и memmode, которые можно использовать для настройки хранения ваших данных (например, уровня сжатия, режима карты памяти и т. д.).В равной степени легко (точно такой же интерфейс) использовать базу данных (MySQL и т. Д.) В качестве бэкэнда вместо вашей файловой системы.Вы также можете отключить кеширование памяти, поэтому каждое чтение / запись идет непосредственно в архив, просто установив cached=False.

klepto, обеспечивающий доступ к настройке вашей кодировки, путем создания пользовательского keymap.

>>> from klepto.keymaps import *
>>> 
>>> s = stringmap(encoding='hex_codec')
>>> x = [1,2,'3',min]
>>> s(x)
'285b312c20322c202733272c203c6275696c742d696e2066756e6374696f6e206d696e3e5d2c29'
>>> p = picklemap(serializer='dill')
>>> p(x)
'\x80\x02]q\x00(K\x01K\x02U\x013q\x01c__builtin__\nmin\nq\x02e\x85q\x03.'
>>> sp = s+p
>>> sp(x)
'\x80\x02UT28285b312c20322c202733272c203c6275696c742d696e2066756e6374696f6e206d696e3e5d2c292c29q\x00.' 

klepto также предоставляет множество алгоритмов кэширования (например, mru, lru, lfu и т. Д.), Которые помогут вам управлять кэшем в памяти и будут использоватьАлгоритм делает дамп и загружает в бэкэнд архива.

Вы можете использовать флаг cached=False, чтобы полностью отключить кеширование памяти, и напрямую читать и записывать на диск или базу данных.Если ваши записи достаточно велики, вы можете выбрать запись на диск, где вы поместите каждую запись в отдельный файл.Вот пример, который делает и то, и другое.

>>> from klepto.archives import dir_archive
>>> # does not hold entries in memory, each entry will be stored on disk
>>> demo = dir_archive('demo', {}, serialized=True, cached=False)
>>> demo['a'] = 10
>>> demo['b'] = 20
>>> demo['c'] = min
>>> demo['d'] = [1,2,3]

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

Получите klepto здесь: https://github.com/uqfoundation

3 голосов
/ 11 ноября 2014

Обычный шаблон в Python 2.x - иметь одну версию модуля, реализованную на чистом Python, с необязательной ускоренной версией, реализованной как расширение C;например, pickle и cPickle.Это возлагает бремя импорта ускоренной версии и использования чистой версии Python на каждого пользователя этих модулей. В Python 3.0 ускоренные версии считаются деталями реализации чистых версий Python. Пользователи всегда должны импортировать стандартную версию, которая пытается импортировать ускоренную версию и возвращается к чистой версии Python. Пара pickle / cPickle получила эту обработку.

  • Протокол версии 0 является исходным «читаемым человеком» протоколом и обратно совместим с более ранними версиями Python.
  • Протокол версии 1 - это старый двоичный формат, который также совместим с более ранними версиями Python.
  • Протокол версии 2 был представлен в Python 2.3.Это обеспечивает намного более эффективное травление классов нового стиля.Обратитесь к PEP 307 за информацией об улучшениях, внесенных протоколом 2.
  • Версия протокола 3 была добавлена ​​в Python 3.0.Он имеет явную поддержку байтовых объектов и не может быть распакован Python 2.x. Это протокол по умолчанию и рекомендуемый протокол, когда требуется совместимость с другими версиями Python 3.
  • Протокол версии 4 был добавлен в Python 3.4.Добавлена ​​поддержка очень больших объектов , выборка большего количества объектов и некоторые оптимизации форматов данных.Обратитесь к PEP 3154 за информацией об улучшениях, внесенных протоколом 4.

Если ваш словарь огромен и должен быть совместим только с Python 3.4 или выше, используйте:

pickle.dump(obj, file, protocol=4)
pickle.load(file, encoding="bytes")

или:

Pickler(file, 4).dump(obj)
Unpickler(file).load()

Тем не менее, в 2010 модуль json был в 25 раз быстрее при кодировании и в 15 раз быстрее при декодировании простых типов, чем pickle, Мой тест 2014 года говорит marshal> pickle> json, но marshal's в сочетании с конкретными версиями Python .

0 голосов
/ 18 октября 2010

Зависит от того, как долго «долго» вы должны думать о компромиссах, которые вы должны сделать: либо все данные будут готовы в памяти после (длительного) запуска, либо загрузите только частичные данные (тогда вам нужно разделить дата в нескольких файлах или использовать SQLite или что-то вроде этого). Я сомневаюсь, что загрузка всех данных заранее, например, из sqlite в словарь принесет любое улучшение.

0 голосов
/ 18 октября 2010

Вы пытались использовать альтернативный формат хранения, такой как YAML или JSON ?Python поддерживает JSON изначально из Python 2.6 с использованием модуля json, я думаю, и есть сторонние модули для YAML .

Вы также можете попробовать модуль shelve.

...