поиск дубликата в pytable hdf5 с 500e6 строками

Проблема

У меня есть большой (> 500e6 строк) набор данных, который я поместил в базу данных pytables.

Допустим, первый столбец - это идентификатор, второй - счетчик для каждого идентификатора. каждая комбинация ID-счетчика должна быть уникальной. У меня есть одна неуникальная строка среди строк 500e6, которые я пытаюсь найти.

Для начала я сделал что-то вроде этого:

index1 = db.cols.id.create_index()
index2 = db.cols.counts.create_index()
for row in db:
    query = '(id == %d) & (counts == %d)' % (row['id'],  row['counts'])
    result = th.readWhere(query)
    if len(result) > 1:
        print row

Я признаю, что это метод грубой силы. Есть предложения по улучшению?

обновить

текущее время работы методом перебора составляет 8421 минуту.

решение Всем спасибо за вклад. Мне удалось сократить время выполнения до 2364,7 секунды, используя следующий метод:

ex = tb.Expr('(x * 65536) + y', uservars = {"x":th.cols.id, "y":th.cols.counts})
ex = tb.Expr(expr)
ex.setOutput(th.cols.hash)
ex.eval()
indexrows = th.cols.hash.create_csindex(filters=filters)

ref = None
dups = []
for row in th.itersorted(sortby=th.cols.hash):
  if row['hash'] == ref:
    dups.append(row['hash'] )
  ref = row['hash']

print("ids: ", np.right_shift(np.array(dups, dtype=np.int64), 16))
print("counts: ", np.array(dups, dtype=np.int64) & 65536-1)

Я могу сгенерировать идеальный хеш, потому что мои максимальные значения меньше 2 ^ 16. Я эффективно упаковываю два столбца в 32-битное int.

После того, как csindex сгенерирован, довольно просто перебрать отсортированные значения и выполнить проверку соседей на наличие дубликатов.

Этот метод, вероятно, можно немного изменить, но я тестирую несколько альтернатив, которые могут обеспечить более естественное решение.


person Community    schedule 23.12.2013    source источник
comment
Вы знаете что-нибудь о том, где может быть неповторяющаяся строка? Например, имеет идентификатор выше X?   -  person Simeon Visser    schedule 23.12.2013
comment
К сожалению, это не ключ к разгадке. и что еще хуже, идентификаторы явно не сортируются.   -  person scrooge    schedule 23.12.2013
comment
Какой диапазон входов? Могут ли они быть компактно представлены всего несколькими байтами или хешированы до нескольких байтов?   -  person tripleee    schedule 23.12.2013
comment
Взгляните на этот вопрос: stackoverflow.com/questions/1315129/, похоже, аналогичная проблема.   -  person Gianluca    schedule 23.12.2013
comment
оба столбца помещаются в беззнаковые короткие целые числа. Я думаю, вы могли бы упаковать их оба в 4-байтовое int или хэш. так что тогда возникает вопрос поиска дубликатов в одном столбце. я думаю, стоит некоторых тестов скорости ...   -  person scrooge    schedule 23.12.2013
comment
Распараллеливайте это, используя threading.Thread (или что-нибудь в этом роде), и просто живите с тем фактом, что наличие 1 плохой строки среди 500e6 (я выделил это жирным шрифтом, потому что это число просто смешно), другие - это игла в стоге сена?   -  person Aleksander Lidtke    schedule 23.12.2013
comment
Джанлука: мой код основан на ответе Акиладилы   -  person scrooge    schedule 23.12.2013
comment
Вы можете попробовать th.readWhere('(id == cid) & (counts == ccounts)', {'cid': row['id'], 'ccounts': row['counts']}). Если я правильно прочитал документы, это могло бы работать и быть быстрее.   -  person zch    schedule 23.12.2013
comment
Александр: это сейсмические данные. по общему признанию, это мой самый большой набор данных, но в этом году у меня было 3 набора данных с более чем 100e6 строками. пугает то, что фактические данные начинают превышать 10 тыс. выборок на кадр. сейчас я просто имею дело с заголовками данных. Я смотрю на некоторые параллельные алгоритмы сортировки. Я полагаю, если я могу гарантировать сортировку, мне нужно только сравнить соседей на наличие дубликатов.   -  person scrooge    schedule 23.12.2013
comment
зч: спасибо, попробую.   -  person scrooge    schedule 23.12.2013
comment
С каким объемом памяти нужно работать?   -  person DSM    schedule 23.12.2013
comment
Если вы можете хэшировать / комбинировать свои ключи с 4-байтовым int, попробуйте использовать битовый вектор для указанных найденных ключей.   -  person mpez0    schedule 23.12.2013
comment
DSM: 16 ГБ на этой рабочей станции   -  person scrooge    schedule 27.12.2013
comment
mpez0: есть ссылка, описывающая, что вы имеете в виду под битовыми векторами?   -  person scrooge    schedule 27.12.2013
comment
Битовый вектор - это растровое изображение, если бит n установлен, значит, вы видели ключ n.   -  person tripleee    schedule 27.12.2013
comment
Один интересный результат, который должен быть получен в результате некоторого тестирования, в небольших тестах (1e4 строки) с использованием where() примерно в 3 раза быстрее (11,6 с), чем с использованием readWhere() (35 с). Повторяю тесты с 1e6 строками.   -  person scrooge    schedule 28.12.2013


Ответы (2)


На ум приходят два очевидных метода: хеширование и сортировка.

A) определить хэш-функцию для объединения идентификатора и счетчика в одно компактное значение.

Б) посчитайте, как часто встречается каждый хэш-код

C) выберите из ваших данных все, что имеет хеш-коллизии (это должен быть гораздо меньший набор данных)

Г) отсортируйте этот набор данных, чтобы найти дубликаты.

Хэш-функцию в A) необходимо выбрать так, чтобы она помещалась в основную память и в то же время обеспечивала достаточную избирательность. Возможно, для этого используйте два набора бит размером 2 ^ 30 или около того. Вы можете позволить себе иметь 5-10% коллизий, это все равно должно уменьшить размер набора данных, чтобы впоследствии обеспечить быструю сортировку в памяти.

По сути, это фильтр Блума.

person Has QUIT--Anony-Mousse    schedule 27.12.2013
comment
какую хеш-функцию вы имели в виду? Я просто немного опасаюсь касаться строковых функций. мне, вероятно, пришлось бы ограничиться простыми функциями, которые можно вычислить с помощью tables.Expr. как умножение двух столбцов. - person scrooge; 28.12.2013
comment
Это зависит от распределения ваших идентификаторов. Вероятно, вы можете просто использовать (a + b * p1) % p2 для двух хорошо выбранных простых чисел p1 и p2. - person Has QUIT--Anony-Mousse; 28.12.2013

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

Я утверждаю, что написанный вами простой код, скорее всего, еще не в полной мере использует возможности, которые pytables уже предоставляет вам.

В документации для create_index () , в нем говорится, что настройки по умолчанию - optlevel=6 и kind='medium'. В нем упоминается, что вы можете увеличить скорость каждого из ваших запросов 500e6, уменьшив энтропию индекса, и вы можете уменьшить энтропию своего индекса до минимально возможного значения (нуля), либо выбрав значения optlevel=9 и kind='full', отличные от значений по умолчанию. или, что то же самое, путем создания индекса с помощью вызова create_csindex () вместо этого. Согласно документации, вы должны заплатить немного больше вперед, потратив больше времени на создание лучше оптимизированного индекса для начала, но затем он окупится позже, сэкономив ваше время на серии запросов, которые вам нужно повторить 500e6 раз.

Если оптимизация индексов столбцов pytables не позволяет в достаточной степени ускорить ваш код, и вы хотите просто выполнить массовую сортировку по всем строкам, а затем просто искать дубликаты, ища совпадения в соседних отсортированных строках, можно выполнить сортировка слиянием за время O (N log (N)) с использованием относительно скромных объемов памяти путем сортировки данные в кусках, а затем сохраняют куски во временных файлах на диске. Примеры здесь и здесь продемонстрируют в принципе, как это сделать в Python. Но вам действительно стоит сначала попробовать оптимизировать индекс pytables, так как это, вероятно, предоставит гораздо более простое и естественное решение в вашем конкретном случае.

person stachyra    schedule 27.12.2013
comment
create_csindex () дает значительное улучшение мелкомасштабных тестов. однако на фактическом наборе данных время выполнения все еще кажется недопустимым. вероятно, потому что он масштабируется близко к O (N ^ 2). сейчас делаю некоторые временные тесты. - person scrooge; 28.12.2013