Внедрение k-d дерева для поиска «ближайшего соседа» в MYSQL?

Я разрабатываю программное обеспечение для автоматической торговли на валютном рынке. В базе данных MYSQL у меня есть годовые рыночные данные с пятиминутными интервалами. У меня есть 4 разных метрики для этих данных, помимо цены и времени.

[Time|Price|M1|M2|M3|M4] 
x ~400,0000

Time - это первичный ключ, а с M1 по M4 - разные показатели (например, стандартное отклонение или наклон скользящей средней).

Вот реальный пример (отрывок :)

+------------+--------+-----------+--------+-----------+-----------+
|  Time      | Price  |     M1    |   M2   |    M3     |     M4    |
+------------+--------+-----------+--------+-----------+-----------+
| 1105410300 | 1.3101 |   12.9132 | 0.4647 |   29.6703 |        50 |
| 1105410600 | 1.3103 |    14.056 | 0.5305 | 29.230801 |        50 |
| 1105410900 | 1.3105 |   15.3613 | 0.5722 |   26.8132 |        25 |
| 1105411200 | 1.3106 | 16.627501 | 0.4433 | 24.395599 |  26.47059 |
| 1105411500 | 1.3112 |   18.7843 | 1.0019 | 24.505501 |    34.375 |
| 1105411800 | 1.3111 |   19.8375 | 0.5626 |        20 |   32.8125 |
| 1105412100 | 1.3105 |   20.0168 | 0.6718 |    9.7802 |   23.4375 |
| 1105412400 | 1.3105 |   20.4538 | 0.8943 |     7.033 |   23.4375 |
| 1105412700 | 1.3109 |   21.6078 | 0.4902 |   11.7582 |   29.6875 |
| 1105413000 | 1.3104 |   21.2045 |  1.565 |    8.6813 |    21.875 |
+------------+--------+-----------+--------+-----------+-----------+...400k more

Учитывая ввод M1, M2, M3 и M4, я хочу (быстро и точно) найти 5000 ближайших совпадений.

Пример ввода:

+------------+--------+-----------+--------+-----------+-----------+
|  Time      | Price  |     M1    |   M2   |    M3     |     M4    |
+------------+--------+-----------+--------+-----------+-----------+
| 1205413000 | 1.4212 |   20.1045 | 1.0012 |    9.1013 |    11.575 |
+------------+--------+-----------+--------+-----------+-----------+

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

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

Я читал о чем-то под названием K-D Trees, используемом для этой цели. Может ли кто-нибудь объяснить или предоставить мне некоторый материал, объясняющий, как реализовать это в MYSQL?

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

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

INSERT INTO Dim1 SELECT * FROM myTable AS myTable USE INDEX(M1) WHERE myTable.M1 < currentM1 ORDER BY M1 DESC LIMIT 2500;
INSERT INTO Dim1 SELECT * FROM myTable AS myTable USE INDEX(M1) WHERE myTable.M1 > currentM1 ORDER BY M1  ASC LIMIT 2500;

INSERT INTO Dim2 SELECT * FROM myTable AS myTable USE INDEX(M2) WHERE myTable.M2 < currentM2 ORDER BY M2 DESC LIMIT 2500;
INSERT INTO Dim2 SELECT * FROM myTable AS myTable USE INDEX(M2) WHERE myTable.M2 > currentM2 ORDER BY M2  ASC LIMIT 2500;

INSERT INTO Dim3 SELECT * FROM myTable AS myTable USE INDEX(M3) WHERE myTable.M3 < currentM3 ORDER BY M3 DESC LIMIT 2500;
INSERT INTO Dim3 SELECT * FROM myTable AS myTable USE INDEX(M3) WHERE myTable.M3 > currentM3 ORDER BY M3  ASC LIMIT 2500;

INSERT INTO Dim4 SELECT * FROM myTable AS myTable USE INDEX(M4) WHERE myTable.M4 < currentM4 ORDER BY M4 DESC LIMIT 2500;
INSERT INTO Dim4 SELECT * FROM myTable AS myTable USE INDEX(M4) WHERE myTable.M4 > currentM4 ORDER BY M4  ASC LIMIT 2500;

Важно понимать, что меня интересует расстояние по рангу, а не по ценности.

Изменить: я немного ближе к пониманию того, как это сделать (я думаю): мне нужно предварительно обработать каждую строку каждой метрики и присвоить ей percentile, которая будет представлять ее местоположение (в процентах ) в своем диапазоне.

Например, для любого заданного значения M1:

percentile = (#  rows with values less than input)/(# total rows) 

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

Однако я все еще не знаю, как вести настоящий поиск. Возможно ли это даже эффективно выполнить в MySQL?


person Mike Furlender    schedule 10.08.2011    source источник
comment
Вы говорите, что поисковый ввод содержит M1, M2, M3 и M4, но ваш образец включает Time и Price. Включены ли они в ближайшие матчи? Как вы собираетесь определять близкое? Например, масштаб M4 против M2 довольно велик, поэтому я не думаю, что вам обязательно нужно искать сферически ...   -  person jswolf19    schedule 10.08.2011
comment
@ jswolf19 Time и Price не включены в поиск. Я хочу определить количество events от входа, где каждая строка в моей основной таблице - это event. Может быть, сначала нужно масштабировать размеры?   -  person Mike Furlender    schedule 10.08.2011
comment
Скажем, вход для M2 - 2, а вход для M4 - 30. Будет ли Time = 1105413000 ближе или Time = 1105412400 будет ближе?   -  person jswolf19    schedule 10.08.2011
comment
Может быть хорошей идеей добавить столбцы для нормализованных данных, чтобы близость была сопоставима в разных измерениях. Будете ли вы добавлять новые данные для вставки в таблицу по мере выполнения поиска?   -  person jswolf19    schedule 10.08.2011
comment
@jswolf Действительно ли нужно добавлять новые столбцы? Разве я не могу вместо этого просто использовать медиану или что-то в этом роде? Я намерен добавлять новые данные, не литературные, КАК ведется поиск, а сразу после каждого раза.   -  person Mike Furlender    schedule 10.08.2011
comment
Вы беспокоитесь о скорости, но вычисление медианы - это нормально? ^ _ ^ Возможно, вам удастся просто разделить на максимум, я думаю, но это ваш звонок, и для меня они просто числа, поэтому я не знаю, что они означают (не то чтобы я статистик и знание цифр в любом случае мне очень поможет ^^). Я думаю, что лучше всего установить сферу / эллипс в пространстве поиска и настроить его в режиме двоичного поиска, пока вы не получите как можно ближе к 5000 записей. Вы используете версию MySQL, которая позволяет хранить хранимые процедуры?   -  person jswolf19    schedule 10.08.2011
comment
@ jswolf19 Ну, у меня в таблице более 400 тысяч строк. Я упомянул что-то вроде использования медианы, потому что я могу ее предварительно вычислить. Я бы хотел, чтобы записей было ровно 5000. Кроме того, распределение этих показателей разное, поэтому я не могу использовать какую-либо форму (сфера / эллипс и т. Д.), Чтобы найти их.   -  person Mike Furlender    schedule 10.08.2011
comment
На самом деле - я понял, что МОГУТ расширяться сферически, если я просто буду делать это по рангу, а не по фактической стоимости! Не могли бы вы показать мне, как сферически расширяться по 4 измерениям в MySQL?   -  person Mike Furlender    schedule 10.08.2011
comment
Я не думаю, что вы всегда сможете получить ровно 5000 записей, используя фактические значения (и ранжирование, я полагаю, будет еще сложнее), если вы не поймете, как справиться с разрешениями вопроса ...   -  person jswolf19    schedule 11.08.2011


Ответы (1)


Вы должны иметь возможность выполнить следующий запрос:

SELECT * FROM myTable
WHERE M1 BETWEEN searchM1 - radiusM1 AND searchM1 + radiusM1
  AND M2 BETWEEN searchM2 - radiusM2 AND searchM2 + radiusM2
  AND M3 BETWEEN searchM3 - radiusM3 AND searchM3 + radiusM3
  AND M4 BETWEEN searchM4 - radiusM4 AND searchM4 + radiusM4

В случае сферы, конечно, все значения radius будут одинаковыми. Затем вы настраиваете радиус, пока не приблизитесь к желаемому количеству записей. Я бы предложил двоичный поиск.

Я не уверен, хотите ли вы возиться с распределением или нет, но предполагая, что вы это сделаете, вам просто нужно будет присвоить каждому поисковому значению ранг между двумя значениями, между которыми оно будет находиться в вашей таблице (например, если ранг 5 равен 5.5. , ранг 6 равен 5,9, а значение поиска - 5,6, тогда ранг поиска может быть 5,5)

person jswolf19    schedule 11.08.2011
comment
он смотрит на самые близкие точки. В пределах этого интервала могут быть миллионы данных. Неэффективно вычислять все расстояния для всех данных. - person user4951; 21.06.2012
comment
@JimThio, если у вас есть представление о том, как эффективно делать то, что OP хочет, используя mysql, то вы более чем можете дать им ответ. - person jswolf19; 21.06.2012
comment
на самом деле ваш ответ достаточно хорош для mysql. Mysql не может выполнять эффективный поиск ближайшего соседа. Ищу лучшую вя. - person user4951; 21.06.2012
comment
@ jswolf19: если у вас больше метрик, это проклятие размерности: en.wikipedia.org/wiki/Curse_of_dimensionality - в MySQL нет эффективного решения. Невозможно быстро отклонить кандидатов, используя разницу в одной координате в качестве нижней границы расстояния, основанного на всех измерениях. Решение состоит в том, чтобы либо самостоятельно выполнить поиск в дереве KD, либо использовать базу данных, поддерживающую операции NN, например Postgres: http://stackoverflow.com/questions/11015922/indexing-kd-tree - person Tomáš Kafka; 23.05.2016