Postgresql k-ближайший сосед (KNN) на многомерном кубе

У меня есть куб, который имеет 8 измерений. Я хочу сделать сопоставление ближайших соседей. Я совершенно новичок в postgresql. Я читал, что 9.1 поддерживает сопоставление ближайших соседей в многомерных измерениях. Я был бы очень признателен, если бы кто-нибудь мог привести полный пример:

  1. Как создать таблицу с кубом 8D?

  2. Образец вкладыша

  3. Поиск - точное соответствие

  4. Поиск - поиск ближайшего соседа

Пример данных:

Для простоты можно предположить, что все значения находятся в диапазоне от 0 до 100.

Точка1: (1,1,1,1, 1,1,1,1)

Точка2: (2,2,2,2, 2,2,2,2)

Искать значение: (1,1,1,1, 1,1,1,2)

Это должно соответствовать Point1, а не Point2.

Ссылки:

Что нового_в_PostgreSQL_9.1

https://en.wikipedia.org/wiki/K-d_tree#Nearest_neighbour_search


person MD Luffy    schedule 21.05.2013    source источник
comment
Можете ли вы объяснить, какие данные у вас есть, может быть, предоставить небольшую выборку? Я думаю, что куб 8D - это просто таблица с 8 столбцами (размерами).   -  person Tomas Greif    schedule 22.05.2013
comment
Я отредактировал вопрос, включив в него образцы данных. Да, куб 8D можно представить с помощью 8 различных числовых столбцов.   -  person MD Luffy    schedule 22.05.2013
comment
Я добавил полный пример к своему исходному ответу.   -  person Tomas Greif    schedule 23.05.2013


Ответы (2)


PostgreSQL поддерживает оператор расстояния <->, и, насколько я понимаю, его можно использовать для анализа текста (с модулем pg_trgrm) и геометрия тип данных.

Я не знаю, как вы можете использовать его с более чем 1 измерением. Возможно, вам придется определить свою собственную функцию расстояния или как-то преобразовать ваши данные в один столбец с текстовым или геометрическим типом. Например, если у вас есть таблица с 8 столбцами (8-мерный куб):

c1 c2 c3 c4 c5 c6 c7 c8
 1  0  1  0  1  0  1  2

Вы можете преобразовать его в:

c1 c2 c3 c4 c5 c6 c7 c8
 a  b  a  b  a  b  a  c

А затем в таблицу с одним столбцом:

c1
abababac

Затем вы можете использовать (после создания gist индекса):

SELECT c1, c1 <-> 'ababab'
 FROM test_trgm 
 ORDER BY c1 <-> 'ababab';

Пример

Создать образец данных

-- Create some temporary data
-- ! Note that table are created in tmp schema (change sql to your scheme) and deleted if exists !
drop table if exists tmp.test_data;

-- Random integer matrix 100*8 
create table tmp.test_data as (
   select 
      trunc(random()*100)::int as input_variable_1,
      trunc(random()*100)::int as input_variable_2, 
      trunc(random()*100)::int as input_variable_3,
      trunc(random()*100)::int as input_variable_4, 
      trunc(random()*100)::int as input_variable_5, 
      trunc(random()*100)::int as input_variable_6, 
      trunc(random()*100)::int as input_variable_7, 
      trunc(random()*100)::int as input_variable_8
   from 
      generate_series(1,100,1)
);

Преобразование входных данных в текст

drop table if exists tmp.test_data_trans;

create table tmp.test_data_trans as (
select 
   input_variable_1 || ';' ||
   input_variable_2 || ';' ||
   input_variable_3 || ';' ||
   input_variable_4 || ';' ||
   input_variable_5 || ';' ||
   input_variable_6 || ';' ||
   input_variable_7 || ';' ||
   input_variable_8 as trans_variable
from 
   tmp.test_data
);

Это даст вам одну переменную trans_variable, в которой хранятся все 8 измерений:

trans_variable
40;88;68;29;19;54;40;90
80;49;56;57;42;36;50;68
29;13;63;33;0;18;52;77
44;68;18;81;28;24;20;89
80;62;20;49;4;87;54;18
35;37;32;25;8;13;42;54
8;58;3;42;37;1;41;49
70;1;28;18;47;78;8;17

Вместо оператора || вы также можете использовать следующий синтаксис (более короткий, но более загадочный):

select 
   array_to_string(string_to_array(t.*::text,''),'') as trans_variable
from 
   tmp.test_data t

Добавить индекс

create index test_data_gist_index on tmp.test_data_trans using gist(trans_variable);

Тестовое расстояние Примечание. Я выбрал одну строку из таблицы — 52;42;18;50;68;29;8;55 — и использовал слегка измененное значение (42;42;18;52;98;29;8;55) для проверки расстояния. Конечно, у вас будут совершенно другие значения в ваших тестовых данных, потому что это СЛУЧАЙНАЯ матрица.

select 
   *, 
   trans_variable <->  '42;42;18;52;98;29;8;55' as distance,
   similarity(trans_variable, '42;42;18;52;98;29;8;55') as similarity,
from 
   tmp.test_data_trans 
order by
   trans_variable <-> '52;42;18;50;68;29;8;55';

Вы можете использовать оператор расстояния ‹-> или функцию подобия. Расстояние = 1 - Сходство

person Tomas Greif    schedule 22.05.2013
comment
Спасибо twn08. Я столкнулся с этой ошибкой при попытке создать индекс: создайте индекс test_data_gist_index на tmp.test_data_trans с помощью gist(trans_variable); ОШИБКА: текстовый тип данных не имеет класса оператора по умолчанию для метода доступа gist Состояние SQL: 42704 Подсказка: Вы должны указать класс оператора для индекса или определить класс оператора по умолчанию для типа данных. - person MD Luffy; 24.05.2013
comment
Может btree_gist не хватает? Аналогичная проблема в этом вопросе< /а> - person Tomas Greif; 24.05.2013
comment
Я не видел, чтобы вы определяли метрику расстояния между записями в столбце, соединенном точкой с запятой. Использует ли оператор ‹-› строковое расстояние или геометрическое расстояние декодируемой точки? - person Andrew; 11.09.2014

"патч, который вводит поиск kNN для кубов с евклидовым , такси и чебышевские расстояния" недавно были включены в список pgsql-hackers. Это может сработать для вашей цели, если вы сможете настроить сборку PostgreSQL.

Обратите внимание, что тип cube, расширение PostgreSQL, может использоваться для представления точек или кубов в n-мерном пространстве. (Значение n может доходить до 100 по умолчанию, больше, если предел в cubedata.h повышен.) Таким образом, этот патч должен, среди прочего, включать многомерный поиск ближайшего соседа точки/вектора/куба с помощью индекса.

(Без этого патча тип cube не имеет оператора расстояния <->, а вспомогательная функция (#8) отсутствует в OPERATOR CLASS gist_cube_ops, которая необходима, чтобы дать gist возможность сделать индекс, связанный с расстоянием, для этих значений.)

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

person gojomo    schedule 02.10.2013