SQL эффективный запрос ближайшего соседа

У меня возникли проблемы с созданием эффективного SQL-запроса для обработки следующей ситуации:

Предположим, у нас есть таблица с двумя столбцами

groupId : int 
value : float

Таблица огромная (несколько миллионов строк). Существует различное количество «значений» для «groupId» - скажем, от 100 до 50 000. Все значения с плавающей запятой больше или равны нулю, но в остальном не ограничены.

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

Меня убивает именно это определение сходства. Я думаю, что для вычисления сходства, как определено выше, наивным алгоритмом является O (n ^ 2). Теперь я ищу идеи, чтобы либо переопределить «подобие», либо эффективную реализацию вышеизложенного. Я мог бы представить решение, включающее k-ближайших соседей, что-то вроде геометрических ближайших соседей PostGis или, возможно, алгоритм наибольшей общей подпоследовательности (хотя мне понадобится «нечеткая» реализация последнего, потому что «значения» вряд ли когда-либо будут сравниваться точно равными) .

В настоящее время мы используем mySQL, если это имеет значение.

ваше здоровье,

Sören

person BuschnicK    schedule 06.04.2009    source источник
comment
Существует различное количество значений для каждого groupId - скажем, от 100 до 50 000, и все возможные пары из 30 значений в двух группах меня смущают. Можете ли вы уточнить или, может быть, дать представление о том, как будет работать наивный подход?   -  person tpdi    schedule 06.04.2009
comment
Со сколькими группами вы обычно имеете дело?   -  person Tom H    schedule 06.04.2009
comment
Danbruc (первый ответ ниже) описывает проблему намного лучше, чем я. Может быть, его анализ прояснит проблему? В настоящее время у нас есть ~500 групп и ~1.800.000 значений. Однако мы надеемся масштабировать это до нескольких 100 000 групп. Текущая настройка — это всего лишь небольшой тестовый пример.   -  person BuschnicK    schedule 07.04.2009
comment
Когда вы говорите о минимальном евклидовом расстоянии между всеми возможными парами из 30 значений, вы имеете в виду, что {10, 100} ближе к {10, 9999} (поскольку они разделены на 0) или ближе к {20, 90} (поскольку минимальное общее расстояние 10 + 10 = 20)   -  person FryGuy    schedule 07.04.2009
comment
dist((10,100),(10,9999)) = sqrt((10-10)^2 + (9999-100)^2) = большое dist((10,100),(20,90)) = sqrt((10 -20)^2 + (100-90)^2) = меньше   -  person BuschnicK    schedule 07.04.2009
comment
Оригинальный комментарий к ответу FryGuy: этот оператор SQL может использовать некоторые координаты несколько раз. Это разрешено или желательно? Для ‹1, 2, 3, 4› и ‹5, 100, 100, 100› при расчете расстояния будет использоваться первая координата 5 четыре раза.   -  person Daniel Brückner    schedule 08.04.2009
comment
Взгляните на мой ответ и посмотрите, поможет ли это вам.   -  person Evan Carroll    schedule 08.08.2018


Ответы (4)


Не могли бы вы проверить, правильно ли я понял вопрос?

Ваша таблица представляет векторы, идентифицированные groupId. Каждый вектор имеет размерность от 100 до 50 000, но для измерения не определен порядок. То есть вектор из таблицы фактически является представителем класса эквивалентности.

Теперь вы определяете подобие двух классов эквивалентности как минимальное евклидово расстояние проекций любых двух представителей классов эквивалентности на подпространство первых 30 измерений.

Примеры проекции на два измерения:

A = <1, 2, 3, 4>
B = <5, 6, 7, 8, 9, 10>

A представляет следующий класс эквивалентности векторов.

<1, 2, 3, 4>    <2, 1, 2, 3>    <3, 1, 2, 4>    <4, 1, 2, 3>
<1, 2, 4, 4>    <2, 1, 3, 2>    <3, 1, 4, 2>    <4, 1, 3, 2>
<1, 3, 2, 4>    <2, 3, 1, 4>    <3, 2, 1, 4>    <4, 2, 1, 3>
<1, 3, 4, 2>    <2, 3, 4, 1>    <3, 2, 4, 1>    <4, 2, 3, 1>
<1, 4, 2, 2>    <2, 4, 1, 3>    <3, 4, 1, 2>    <4, 3, 1, 2>
<1, 4, 3, 2>    <2, 4, 3, 1>    <3, 4, 2, 1>    <4, 3, 2, 1>

Проекция всех представителей этого класса эквивалентности на первые два измерения дает результат.

<1, 2>    <1, 3>    <1, 4>
<2, 1>    <2, 3>    <2, 4>
<3, 1>    <3, 2>    <3, 4>
<4, 1>    <4, 2>    <4, 3>

B представляет собой класс эквивалентности с 720 элементами. Проекция на первые два измерения дает 30 элементов.

< 5, 6>    < 5, 7>    < 5, 8>    < 5, 9>    < 5, 10>
< 6, 5>    < 6, 7>    < 6, 8>    < 6, 9>    < 6, 10>
< 7, 5>    < 7, 6>    < 7, 8>    < 7, 9>    < 7, 10>
< 8, 5>    < 8, 6>    < 8, 7>    < 8, 9>    < 8, 10>
< 9, 5>    < 9, 6>    < 9, 7>    < 9, 8>    < 9, 10>
<10, 5>    <10, 6>    <10, 7>    <10, 8>    <10,  9>

Таким образом, расстояние между A и B равно квадратному корню из 8, потому что это минимальное расстояние двух векторов от проекций. Например, ‹3, 4> и ‹5, 6> дают это расстояние.

Итак, я прав в своем понимании проблемы?

Действительно наивный алгоритм для n векторов с m компонентами каждый должен был бы вычислять (n - 1) расстояний. Для каждого расстояния алгоритм будет вычислять расстояния m! /(м - 30)! проекция для каждого вектора. Таким образом, для 100 измерений (ваша нижняя граница) существует 2,65 * 10 ^ 32 возможных проекций для вектора. Для этого требуется вычислить около 7*10^64 расстояний между проекциями и найти минимум, чтобы найти расстояние между двумя векторами. А затем повторить это n раз.

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

Я подумал о том, чтобы упорядочить векторные компоненты и попытаться их сопоставить. Использование манхэттенского расстояния — если это возможно — может помочь упростить решение.

person Daniel Brückner    schedule 06.04.2009
comment
Да, вы прекрасно поняли проблему и объяснили ее гораздо лучше, чем я. Я тоже думал об упорядочении векторов, поэтому я упомянул LCS (самая длинная общая подпоследовательность). Я посмотрю, может ли нам помочь Манхэттенское расстояние. - person BuschnicK; 07.04.2009

Вот несколько хороших приближений:

Вы можете рассчитать центр масс каждой группы, а затем сравнить на основе расстояния до центра масс каждой группы.

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

Некоторая дополнительная информация была бы полезна, например:

Постоянно ли обновляется информация, и если да, то с каким интервалом. Насколько актуальной и насколько точной она должна быть?

person fuzzy-waffle    schedule 07.04.2009
comment
Центр масс в 1 измерении? Разве это не было бы просто медианой или средним значением? Или вы имеете в виду центр масс всех возможных 30 перестановок вектора значений? Хеширование в основном будет означать квантизацию всех значений, верно? т.е. мы бы привязали все значения к сетке? - person BuschnicK; 07.04.2009
comment
Существующая информация никогда не обновляется - добавляются только новые группы. Скажем, 100 в день. Точность была бы хороша, но не критична. Вся эта настройка является этапом предварительной обработки. Идея состоит в том, чтобы получить наиболее вероятные совпадения из базы данных и приступить к их тестированию с помощью гораздо более дорогого автономного инструмента. - person BuschnicK; 07.04.2009
comment
Я не читал первый ответ, который проясняет ситуацию. Я не уверен, что мой ответ хорош, учитывая это. - person fuzzy-waffle; 07.04.2009

Наивная версия будет примерно такой: (не запускать анализатор запросов)

select groupid, min(distance) as mindist
from
   (select other.groupid as groupid,
           min(abs(other.value - us.value)) as distance
    from g us
    join g other on other.groupid != us.groupid
    where us.groupid = ?)
order by mindist
group by groupid

Затем, чтобы воспользоваться индикаторами:

select groupid, min(abs(value - usvalue)) as mindist
from
   (select other.groupid as groupid,
           max(other.value) as value,
           us.value as usvalue
    from g us
    join g other on other.groupid != us.groupid and other.value <= us.value
    where us.groupid = ?

    union

    select other.groupid as groupid,
           min(other.value) as value,
           us.value as usvalue
    from g us
    join g other on other.groupid != us.groupid and other.value >= us.value
    where us.groupid = ?)
order by mindist
group by groupid

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

В этом могут быть ошибки, но, надеюсь, этот ход мыслей поможет.

person FryGuy    schedule 07.04.2009
comment
Спасибо, ФрайГай. Это в значительной степени то, что мы пробовали, но это совсем не масштабируется. Я поэкспериментирую с вариациями вышеизложенного и опубликую результаты. - person BuschnicK; 07.04.2009
comment
у вас есть индикаторы как на groupid, так и на значение? - person FryGuy; 07.04.2009
comment
да. Объяснение mySQL (план выполнения запроса) выглядит настолько хорошо, насколько я могу судить. - person BuschnicK; 07.04.2009
comment
Этот оператор SQL может использовать некоторые координаты несколько раз. Это разрешено или желательно? Для ‹1, 2, 3, 4› и ‹5, 100, 100, 100› при расчете расстояния будет использоваться первая координата 5 четыре раза. - person Daniel Brückner; 07.04.2009
comment
Ну, я неправильно понял вопрос. Этот запрос не ответит на ваш вопрос, а упорядочит по минимальному расстоянию, выбранному из вариантов всех расстояний от группы 1 до группы 2 (а не по сумме минимальных расстояний). Должен ли я удалить этот ответ? - person FryGuy; 08.04.2009

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

Если вы хотите использовать KNN для плавающих элементов, используйте btree_gist. модуль для PostgreSQL и создайте индекс GIST.

Кроме того, для типов данных, для которых существует метрика естественного расстояния, btree_gist определяет оператор расстояния <-> и обеспечивает поддержку индекса GiST для поиска ближайших соседей с использованием этого оператора. Операторы расстояния предоставляются для int2, int4 , int8, float4, float8, метка времени с часовым поясом, метка времени без часового пояса, время без часового пояса, дата, интервал, oid и деньги.

float8 is double precision.

person Evan Carroll    schedule 08.08.2018