PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Postgresql k-plus proche voisin (KNN) sur un cube multidimensionnel

PostgreSQL prend en charge l'opérateur de distance <-> et si je comprends bien, cela peut être utilisé pour analyser du texte (avec le module pg_trgrm) et géométrie type de données.

Je ne sais pas comment vous pouvez l'utiliser avec plus d'une dimension. Peut-être devrez-vous définir votre propre fonction de distance ou convertir d'une manière ou d'une autre vos données en une seule colonne avec un type de texte ou de géométrie. Par exemple si vous avez un tableau à 8 colonnes (cube à 8 dimensions) :

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

Vous pouvez le convertir en :

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

Et puis au tableau avec une colonne :

c1
abababac

Ensuite, vous pouvez utiliser (après avoir créé gist index ):

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

Exemple

Créer des exemples de données

-- 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)
);

Transformer les données d'entrée en texte

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
);

Cela vous donnera une variable trans_variable où toutes les 8 dimensions sont stockées :

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

Au lieu de || vous pouvez également utiliser la syntaxe suivante (plus courte, mais plus cryptée) :

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

Ajouter un index

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

Distance de testRemarque :J'ai sélectionné une ligne du tableau - 52;42;18;50;68;29;8;55 - et utilisé une valeur légèrement modifiée (42;42;18;52;98;29;8;55 ) pour tester la distance. Bien sûr, vous aurez des valeurs complètement différentes dans vos données de test, car il s'agit d'une matrice ALÉATOIRE.

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';

Vous pouvez utiliser l'opérateur de distance <-> ou la fonction de similarité. Distance =1 - Similitude