Python: Zope's BTree OOSet, IISet и т. д. Подходит для этого требования?

Я задал еще один вопрос: https://stackoverflow.com/questions/1180240/best-way-to-sort-1m-records-in-python, где я пытался определить лучший подход для сортировки 1 миллиона записей. В моем случае мне нужно иметь возможность добавлять дополнительные элементы в коллекцию и использовать их. Мне предложили попробовать использовать для этой задачи BTree от Zope. После некоторого чтения я немного озадачен тем, какие данные я бы поместил в набор.

По сути, для каждой записи у меня есть две части данных. 1. Уникальный идентификатор, который соответствует пользователю, и 2. интересующее значение для сортировки.

Я вижу, что я могу добавить элементы в OOSet как кортежи, где значение для сортировки находится в индексе 0. Таким образом, (200, 'id1'),(120, 'id2'),(400, 'id3') и результирующий набор будут отсортированы с id2, id1 and id3 по порядку.

Однако частью требования для этого является то, что каждый идентификатор появляется в наборе только один раз. Я буду периодически добавлять дополнительные данные в набор, и новые данные могут включать или не включать дублированные «идентификаторы». Если они дублируются, я хочу обновить значение, а не добавлять дополнительную запись. Итак, основываясь на приведенных выше кортежах, я мог бы добавить (405, 'id1'),(10, 'id4') в набор и хотел бы, чтобы на выходе было id4, id2, id3, id1 по порядку.

Любые предложения о том, как это сделать. Извините за мою новизну в этом вопросе.

* ИЗМЕНИТЬ – дополнительная информация *

Вот реальный код из проекта:

for field in lb_fields:
        t = time.time()
        self.data[field] = [ (v[field], k) for k, v in self.foreign_keys.iteritems() ]
        self.data[field].sort(reverse=True)
        print "Added %s: %03.5f seconds" %(field, (time.time() - t))

Foreign_keys — это исходные данные в словаре с каждым идентификатором в качестве ключа и словарем дополнительных данных в качестве значения. data — это словарь, содержащий списки отсортированных данных.

В качестве примечания: по мере выполнения каждой итерации поля for в lb_fields время сортировки увеличивается - ненамного... но заметно. После того, как 1 миллион записей был отсортирован для каждого из 16 полей, он использует около 4 гигабайт или ОЗУ. В конце концов это будет работать на машине с 48 гигабайтами.


person sberry    schedule 26.07.2009    source источник


Ответы (2)


Я не думаю, что BTrees или другие традиционные отсортированные структуры данных (красно-черные деревья и т. д.) помогут вам, потому что они сохраняют порядок по ключу, а не по соответствующему значению - другими словами, поле, которое они гарантируют как уникальное, одно и то же. тот, по которому они заказывают. У вас другие требования, потому что вам нужна уникальность по одному полю, но сортировка по другому.

Каковы ваши требования к производительности? С довольно простой чистой реализацией Python, основанной на словарях Python для уникальности и сортировках Python, на не очень быстром ноутбуке я получаю 5 секунд для исходной конструкции (по сути, сортировка по миллиону элементов, начиная с них как dict) , и около 9 секунд для «обновления» с 20 000 новых пар идентификатор/значение, из которых половина «перекрывает» (таким образом перезаписывает) существующий идентификатор, а половина — новые (я могу реализовать обновление быстрее, около 6,5 секунд, но у этой реализации есть аномалия: если одна из «новых» пар точно идентична одной из «старых», как по идентификатору, так и по значению, она дублируется — защита от такого «дублирования идентичных» — это то, что толкает меня от 6,5 секунды до 9, и я полагаю, что вам потребуется такая же предосторожность).

Насколько далеки эти 5 и 9 секунд от ваших требований (с учетом фактической скорости машины, на которой вы будете работать, по сравнению с Core Duo 2,4 ГГц, 2 ГБ ОЗУ и типичными проблемами производительности этого ноутбука). пользуюсь)? IOW, достаточно ли это близко к «ударному расстоянию», чтобы стоит повозиться и попытаться выжать из него последние несколько циклов, или вам нужна на порядки более высокая производительность?

Я пробовал несколько других подходов (с базой данных SQL, с C++ и его std::sort &c,...), но все они медленнее, поэтому, если вам нужна гораздо более высокая производительность, я не уверен, что вы могли бы сделать .

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

import gc
import operator
import random
import time


nk = 1000

def popcon(d):
  for x in xrange(nk*1000):
    d['id%s' % x] = random.randrange(100*1000)

def sorted_container():
  ctr = dict()
  popcon(ctr)
  start = time.time()
  ctr_sorted = ctr.items()
  ctr_sorted.sort(key=operator.itemgetter(1))
  stend = time.time()
  return stend-start, ctr_sorted

def do_update(ctr, newones):
  start = time.time()
  dicol = dict(ctr)
  ctr.extend((k,v) for (k,v) in newones if v!=dicol.get(k,None))
  dicnu = dict(newones)
  ctr.sort(key=operator.itemgetter(1))
  newctr = [(k,v) for (k,v) in ctr if v==dicnu.get(k,v)]
  stend = time.time()
  return stend-start, newctr

def main():
  random.seed(12345)
  for x in range(3):
    duration, ctr = sorted_container()
    print 'dict-to-sorted, %d: %.2f sec, len=%d' % (x, duration, len(ctr))
    newones = [('id%s' % y, random.randrange(nk*100))
                for y in xrange(nk*990,nk*1010)]
    duration, ctr = do_update(ctr, newones)
    print 'updt-to-sorted, %d: %.2f sec, len=%d' % (x, duration, len(ctr))
    del ctr
    gc.collect()

main()

и это типичный запуск:

$ time python som.py
dict-to-sorted, 0: 5.01 sec, len=1000000
updt-to-sorted, 0: 9.78 sec, len=1010000
dict-to-sorted, 1: 5.02 sec, len=1000000
updt-to-sorted, 1: 9.12 sec, len=1010000
dict-to-sorted, 2: 5.03 sec, len=1000000
updt-to-sorted, 2: 9.12 sec, len=1010000

real    0m54.073s
user    0m52.464s
sys 0m1.258s

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

Это с системным Python 2.5.2 на Macbook с Mac OS X 10.5.7, Intel Core Duo 2,4 ГГц и 2 ГБ ОЗУ (времена не сильно меняются, когда я использую разные версии Python).

person Alex Martelli    schedule 26.07.2009
comment
Для набора новых данных пересортировка может занять до пары минут, поэтому 9 секунд более чем нормально. Я чувствую, что должна быть какая-то основная проблема, которую я не решаю, потому что время сортировки у меня намного, намного больше. В настоящее время он тестируется/разрабатывается на IBM xServe Dual 2,2 ГГц Core II Duo, 6 ГБ ОЗУ. Я храню все эти данные в памяти. Я запускаю ThreadingTCPServer, который создает исходные данные и сортирует их. Затем есть метод, с помощью которого я могу добавить дополнительные данные. У меня есть некоторое время вывода результатов бенчмаркинга, чтобы сделать сортировку, и они занимают более 5 минут! - person sberry; 26.07.2009
comment
Позвольте мне отредактировать мой ответ, чтобы показать, что я использовал для измерения этого времени! - person Alex Martelli; 26.07.2009

Вполне возможно решить вашу проблему. Для этого вы должны просто заметить, что типы контейнеров в Python всегда сравнивают объекты, вызывая их методы. Поэтому вы должны сделать что-то вроде:

class Record:
    'Combination of unique part and sort part.'
    def __init__(self, unique, sort):
        self.unique = unique
        self.sort = sort

    def __hash__(self):
        # Hash should be implemented if __eq__ is implemented.
        return hash(self.unique)

    def __eq__(self, other):
        return self.unique == other.unique

    def __lt__(self, other):
        return self.sort < other.sort

 records = btree((Record(u, s) for u, s in zip(unique_data, sort_data)))

 print(records.pop())

Заметки:

  • в зависимости от того, как реализован ваш любимый тип контейнера, вам также может понадобиться добавить методы для !=, ‹=, >, >=
  • это не нарушит связь между == и ‹= до тех пор, пока x.unique == y.unique ==> x.sort == y.sort
person ilya n.    schedule 09.08.2009