Быстрая проверка большой базы данных на сходство расстояния редактирования

У меня есть база данных из 350,000 строк со средней длиной около 500. Строки не состоят из слов, они представляют собой по существу случайный набор символов.

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

Наивно мы вычисляем расстояние редактирования со временем выполнения O(a*b), где a,b – длина двух строк. Мы делаем это для всех пар n^2, что дает общее время выполнения O(n^2*a*b), что явно слишком велико для n=350,000, a,b=500.

База данных представлена ​​в виде списка Python, считанного из CSV-файла. Я хотел бы обработать его по-питоновски, если это возможно.

Как это можно ускорить? Я не уверен, сколько времени займет выполнение наивного алгоритма (порядка недель), но в идеале он должен выполняться менее суток.


person Evan Weissburg    schedule 16.02.2018    source источник
comment
Построение FST позволит вам выполнять поиск намного быстрее.   -  person user2722968    schedule 16.02.2018
comment
Можете ли вы рассказать нам больше о базе данных? Это СУБД? NOSQL? С чем мы здесь имеем дело? Вы пытаетесь написать запрос для этого или загружаете все строки из БД и выполняете свои вычисления в самом коде Python? Насколько его нужно ускорить?   -  person mypetlion    schedule 20.02.2018
comment
@mypetlion Обновлено.   -  person Evan Weissburg    schedule 20.02.2018
comment
Вам подходит ratio или partial_ratio с сайта pypi.python.org/pypi/fuzzywuzzy? Или вам нужно только изменить расстояние?   -  person Tarun Lalwani    schedule 20.02.2018
comment
Это должно сработать @TarunLalwani. Проблема в том, что это, вероятно, все еще займет слишком много времени O(n^2*m), если предположить, что отношение работает в линейном m времени.   -  person Evan Weissburg    schedule 20.02.2018
comment
Прежде всего, если у вас есть строка длиной 500, вы не хотите делать char путем сравнения char. Вы хотите сделать это слово в слово. Это уменьшит длину слов на 1/4 или 1/5 и, следовательно, уменьшит количество перестановок. Я использовал этот метод, чтобы сначала кэшировать токенизацию всех строк, а затем выполнить сравнение с использованием многопроцессорного пула. Я не заглядывал под капот сложности этого метода   -  person Tarun Lalwani    schedule 20.02.2018
comment
Вы обязательно должны написать ответ - это похоже на то, что я ищу. Почти любое сравнение строк достаточно для моих целей, если оно непротиворечиво.   -  person Evan Weissburg    schedule 20.02.2018
comment
Похоже, вы можете использовать какой-то en.wikipedia.org/wiki/Locality- чувствительный_хеширование . Есть разработанные для дистанции Хэмминга.   -  person Haochen Wu    schedule 20.02.2018
comment
Одна вещь, которую вы можете сделать с хэшированием, зависящим от местоположения, - это в основном хешировать всю вашу строку (O (n)) и проверять, все ли хэши уникальны (O (n)). Я бы сказал, что битовая выборка, вероятно, является хорошим выбором, поскольку вы можете настроить количество битов, которые вы сэмплируете. Единственная проблема заключается в том, что он основан на расстоянии Хэмминга, а это не совсем то, что вам нужно.   -  person Haochen Wu    schedule 20.02.2018
comment
Могу ли я проверить, похожи ли хэши?   -  person Evan Weissburg    schedule 20.02.2018
comment
Вы не знаете. Идея состоит в том, что если две строки достаточно похожи, они должны иметь одинаковый хэш. Например, настраивая биты, которые вы сэмплируете, вы можете указать, какой уровень подобия даст вам тот же хэш.   -  person Haochen Wu    schedule 20.02.2018
comment
Определенно напишите ответ с фрагментом кода Pythonic, чтобы получить награду — это звучит здорово.   -  person Evan Weissburg    schedule 20.02.2018
comment
Я попробовал, и скорость составляет 3500 сравнений в секунду с 8 ядрами, которые не будут работать. Основная идея улучшения заключалась бы в том, чтобы не проводить сравнение один к одному методом грубой силы и уменьшать количество сравнений.   -  person Tarun Lalwani    schedule 20.02.2018
comment
Также просмотрите github.com/scivey/relevanced/blob/master/ документы/index.md   -  person Tarun Lalwani    schedule 21.02.2018
comment
@EvanWeissburg - не могли бы вы включить небольшой репрезентативный образец ваших строк? Или, если нет, то сколько разных символов они содержат и как выглядит распределение частот символов? Наконец, не могли бы вы привести пример двух строк, которые слишком похожи (но не тривиально)?   -  person Nathan Vērzemnieks    schedule 22.02.2018


Ответы (1)


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

Предположим, что все ваши строки хранятся в strings.

import random
from collections import Counter

MAX_LENGTH = 500
SAMPLING_LENGTH = 10

def bit_sampling(string, indices):
    return ''.join([string[i] if i<len(string) else ' ' for i in indices])

indices = random.sample(range(MAX_LENGTH),SAMPLING_LENGTH)
hashes = [bit_sampling(string, indices) for string in strings]

counter = Counter(hashes)
most_common, count = counter.most_common()[0]
while count > 1:
    dup_indices = [i for i, x in enumerate(hashes) if x == most_common]
    # You can use dup_indices to check the edit distance for original groups here.
    counter.pop(most_common)
    most_common, count = counter.most_common()[0]

Прежде всего, это небольшой вариант битовой выборки, который лучше всего подходит для общего расстояния Хэмминга. В идеале, если все ваши строки имеют одинаковую длину, это может дать теоретическую оценку вероятности для расстояния Хэмминга. Когда расстояние Хэмминга между двумя строками мало, очень маловероятно, что они будут иметь разный хэш. Это можно указать параметром SAMPLING_LENGTH. Чем больше SAMPLING_LENGTH, тем больше вероятность хэширования похожей строки в другой хэш, но также снижается вероятность хеширования не очень похожей строки в тот же хэш. Для расстояния Хэмминга вы можете легко рассчитать этот компромисс.

Запустив этот фрагмент несколько раз, вы сможете повысить свою уверенность в отсутствии похожих строк, поскольку каждый раз вы будете пробовать разные места.

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

Хотя все операции в этом фрагменте являются линейными (O(n)), они по-прежнему могут потреблять значительный объем памяти и времени выполнения, и может быть возможно уменьшить постоянный коэффициент.

Вы также можете рассмотреть возможность использования более сложного алгоритма хеширования с учетом местоположения, например описанного здесь: https://arxiv.org/pdf/1408.2927.pdf

person Haochen Wu    schedule 20.02.2018
comment
Вопрос, прежде чем я реализую и поиграюсь с этим (а затем приму ваш ответ): вы тестировали это во время выполнения с некоторыми строками (350 000 @ 500 каждая)? - person Evan Weissburg; 20.02.2018
comment
Просто запустите синтетический набор данных, и это займет менее 1 минуты без проблем. Возможно, вы все же захотите немного подождать, прежде чем согласиться, на случай, если у других может быть лучший ответ, но вы можете начать играть с ним, потому что он очень быстрый. - person Haochen Wu; 20.02.2018
comment
Истинный. Ваше объяснение было очень чистым и кратким, и я также ценю источник arxiv. Спасибо. - person Evan Weissburg; 20.02.2018
comment
У меня есть возможная проблема с предоставленным подходом. Так как максимальная длина составляет 500, длина выборки 500 не должна показывать никаких дубликатов. Однако я обнаружил, что счетчик имеет только 85 701 запись из набора данных 350 820 с использованием вышеуказанных параметров. Это ожидаемое поведение? - person Evan Weissburg; 21.02.2018
comment
Это, вероятно, специфично для ваших данных. Я получил 350000 записей в счетчике при случайном вводе. Вы проверяли, на каком входе был тот же хэш? dup_indices должен иметь возможность указывать на эти записи. - person Haochen Wu; 21.02.2018
comment
Верно ли мое предположение, что единственные дубликаты, найденные с помощью max_len=500 и sampling_len=500, являются точными символами для дубликатов символов? - person Evan Weissburg; 21.02.2018
comment
Да, так и должно быть. Ввод точно такой же? - person Haochen Wu; 21.02.2018
comment
Давайте продолжим обсуждение в чате. - person Evan Weissburg; 21.02.2018
comment
@EvanWeissburg Мне любопытно, работает ли приведенный выше код для вас. Как я правильно понимаю, вы ищете сходство на основе расстояния редактирования, верно? Как упомянул Ву, это может хорошо сработать на хамминговой дистанции. Две строки с малым расстоянием редактирования могут иметь большое расстояние Хэмминга. - person viz12; 21.02.2018
comment
@ vis12 Это не идеальный показатель для задачи, но он молниеносный. Ложноположительные дубликаты представляют собой меньшую проблему, чем неполная идентификация дубликатов, поэтому для меня это работает нормально, поскольку я могу настроить длину выборки. - person Evan Weissburg; 21.02.2018
comment
@EvanWeissburg Я рад, что у вас все получилось. Я только что видел ваши обсуждения в чате, поскольку вы работаете над биологическими данными, расстояние между ними будет работать, если только не будет много вставок и удалений. - person viz12; 21.02.2018
comment
Кажется, что этот подход не будет обнаруживать идентичные строки, за исключением даже одной вставки или удаления, не говоря уже о менее похожих. Не будет ли это проблемой? Вы не получите много ложных срабатываний, а скорее много ложных срабатываний, по крайней мере, если две строки разной длины могут быть слишком похожими. - person Nathan Vērzemnieks; 22.02.2018
comment
@EvanWeissburg - вы пытались ввести в свой набор данных строку, которая была бы слишком похожа на существующую, и посмотреть, обнаружит ли ее этот подход? - person Nathan Vērzemnieks; 22.02.2018
comment
@NathanVērzemnieks Я проверю это завтра. Большинство белковых строк, с которыми я работаю, которые являются дубликатами, имеют замены (возможно, проверенные в исследовании на предмет эффекта замещения), а не добавление/удаление. Я все еще ищу лучшее решение, если это возможно. - person Evan Weissburg; 22.02.2018