PostgreSQL遺傳學應用-矩陣相似距離計算(歐式距離,…XX距離)
標籤
PostgreSQL ,
背景
生物科學中相當重要的工作之一解開遺傳密碼?
歐式空間計算,是其中的一個需求,很有意思吧,PostgreSQL可以用來解開遺傳密碼。
https://en.wikipedia.org/wiki/Euclidean_distance
https://www.math.uci.edu/~gpatrick/source/205b06/chapviii.pdf
實際上PostgreSQL是一個擴充套件性非常強大的資料庫,比如在文字相似計算方面,就有諸多擴充套件外掛。
《17種相似演算法與GIN索引 – pg_similarity》
https://github.com/eulerto/pg_similarity
《PostgreSQL結合餘弦、線性相關演算法 在文字、圖片、陣列相似 等領域的應用 – 3 rum, smlar應用場景分析》
《PostgreSQL結合餘弦、線性相關演算法 在文字、圖片、陣列相似 等領域的應用 – 2 smlar外掛詳解》
在基因科學方面,也有擴充套件外掛應用:
《為了部落 – 如何通過PostgreSQL基因配對,產生優良下一代》
在化學分析方面,也有相似的外掛:
某個生物科技公司,有這樣的一種需求:
每張表有幾十萬行,幾萬列,全部浮點型別,任意列勾選,計算歐氏距離等需求。
設計
因為資料庫設計限制,不能支援一張表幾萬列,不過PostgreSQL可以將多列存成陣列。
1、DNA結構如下:
create table dna (
id serial primary key, -- 主鍵
arr float8[] -- 浮點陣列
);
比如每行代表一個物種的測序資料。
2、生成隨機浮點陣列的函式,可以方便的生成測試資料。
create or replace function gen_randarr(int) returns float8[] as $$
select array_agg(random()*1000) from generate_series(1, $1);
$$ language sql strict;
postgres=# select gen_randarr(10);
gen_randarr
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
{830.968368332833,283.642665948719,64.4483459182084,24.3995497003198,654.509209562093,762.801019474864,109.366949647665,849.462529178709,111.898560542613,650.523159187287}
(1 row)
Time: 0.758 ms
3、生成50萬條測試資料,每組2萬浮點數。
vi test.sql
insert into dna (arr) values (gen_randarr(20000));
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 50 -j 50 -t 10000
資料大概佔用86GB空間。
postgres=# dt+ dna
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+----------+-------+-------------
public | dna | table | postgres | 86 GB |
(1 row)
計算歐式距離的函式
可以使用plpgsql建立計算兩個浮點陣列的歐式距離的函式,長度可以不一樣,因為可能不同物種的遺傳資料不一樣,有的多,有的少。
如果使用C函式,效能會更好。
CREATE OR REPLACE FUNCTION euc_distance(l float8[], r float8[]) RETURNS float8 AS $$
DECLARE
s float8 := 0; -- 中間結果
x float8; -- LOOP中的陣列元素值
i int := 1; -- 陣列下標
r_len int := array_length(r,1); -- 右邊陣列的長度
l_len int := array_length(l,1); -- 左邊陣列的長度
BEGIN
if l_len >= r_len then
foreach x in array l LOOP
s := s + ( (x - case when i<=r_len then r[i] else 0 end) ^ 2 );
i := i+1;
END LOOP;
else
foreach x in array r LOOP
s := s + ( (x - case when i<=l_len then l[i] else 0 end) ^ 2 );
i := i+1;
END LOOP;
end if;
RETURN |/ s;
END;
$$ LANGUAGE plpgsql;
例子
postgres=# select euc_distance(array[1,2,3], array[1,2,3]);
euc_distance
--------------
0
(1 row)
Time: 0.386 ms
postgres=# select euc_distance(array[1,2,3], array[1,2,3,4,5]);
euc_distance
------------------
6.40312423743285
(1 row)
Time: 0.470 ms
通過這個函式,傳入要計算的陣列即可計算歐式距離。
計算部分指定位置的歐式距離
這個主要用於部分計算,例如人類和猴子,在某一段的相似性,那麼需要從這兩條記錄中,分別取出要計算的部分,重新組成兩個陣列,然後計算它們兩的歐氏距離。
例子:
select t1.id, t2.id, euc_distance(t1.arr, t2.arr) from
(select * from dna where id=1) t1,
(select * from dna where id=2) t2;
id | id | euc_distance
----+----+------------------
1 | 2 | 57768.4024741692
(1 row)
Time: 12.027 ms
或
select t1.id, t2.id,
euc_distance(
array[t1.arr[1], t1.arr[7], t1.arr[8], t1.arr[9], t1.arr[10]], -- 指定位置
array[t2.arr[1], t2.arr[7], t2.arr[8], t2.arr[9], t2.arr[10]] -- 指定位置
) from
(select * from dna where id=1) t1,
(select * from dna where id=2) t2;
id | id | euc_distance
----+----+------------------
1 | 2 | 679.897967241517
(1 row)
Time: 1.887 ms
計算被勾選物種的排列組合歐式距離
比如選中了100個物種,計算它們的任意組合的歐氏距離。
需要一些輔助函式:
1、組合去重函式,只去掉重複行。
CREATE or replace FUNCTION has_dupli_val(VARIADIC arr int[]) RETURNS boolean AS $$
select count(distinct val)<>count(*) dist_val from unnest($1) t(val) where val is not null;
$$ language sql strict;
2、組合去重函式,去掉按列值排序後的重複行。
CREATE or replace FUNCTION arr_sort(arr int[]) RETURNS int[] AS $$
select array_agg(id order by id) from unnest(arr) t(id);
$$ language sql strict;
3、比如選中了1,2,3,4這四種物種,如何得到他們的排列組合呢?
select distinct on (arr_sort(array[t1.id, t2.id])) t1.id, t2.id from
(select unnest(array[1,2,3,4]) id) t1,
(select unnest(array[1,2,3,4]) id) t2
where not has_dupli_val(t1.id, t2.id);
id | id
----+----
1 | 2
3 | 1
1 | 4
2 | 3
4 | 2
4 | 3
(6 rows)
Time: 1.066 ms
4、建立一個函式,用於計算輸入組合物種的排列組合歐式距離。
create or replace function compute_eu_dist(
arr_kind int[], -- 輸入物種IDs
out kind1 int, -- 物種1
out kind2 int, -- 物種2
out euc_dist float8 -- 物種1,2的歐氏距離
) returns setof record as $$
declare
l float8[]; -- 左陣列
r float8[]; -- 右陣列
begin
for kind1,kind2 in
select distinct on (arr_sort(array[t1.id, t2.id])) t1.id, t2.id from
(select unnest(arr_kind) id) t1,
(select unnest(arr_kind) id) t2
where not has_dupli_val(t1.id, t2.id) -- 排列組合
loop
select arr into l from dna where id=kind1; -- 獲取物種1的遺傳資訊
select arr into r from dna where id=kind2; -- 獲取物種2的遺傳資訊
euc_dist := euc_distance(l,r); -- 計算物種1,2的歐式距離
return next; -- 返回
end loop;
return;
end;
$$ language plpgsql strict;
計算例子:
輸入5個物種的ID,返回這5個物種的排列組合歐式距離。
postgres=# select * from compute_eu_dist(array[1,2,3,4,5]);
kind1 | kind2 | euc_dist
-------+-------+------------------
2 | 1 | 57768.4024741692
1 | 3 | 57866.2845528097
1 | 4 | 57632.9837382263
5 | 1 | 57779.36595061
3 | 2 | 58004.3926579964
4 | 2 | 57593.0783041254
5 | 2 | 57802.9690538283
3 | 4 | 57837.6707750057
3 | 5 | 57921.5524014271
4 | 5 | 57818.9181109456
(10 rows)
Time: 100.582 ms
小結
PostgreSQL是一個擴充套件性很好的資料庫,內建了豐富的資料型別。
本例,使用函式程式設計、陣列型別兩個特性,解決了生物科學中的遺傳計算的場景的疑難問題(上萬列,任意組合計算排列組合的歐式距離)。
同時PostgreSQL還能支援平行計算,在重計算的場景,可以提高計算響應速度。
參考
《17種相似演算法與GIN索引 – pg_similarity》
https://github.com/eulerto/pg_similarity
《為了部落 – 如何通過PostgreSQL基因配對,產生優良下一代》
《PostgreSQL結合餘弦、線性相關演算法 在文字、圖片、陣列相似 等領域的應用 – 3 rum, smlar應用場景分析》
《PostgreSQL結合餘弦、線性相關演算法 在文字、圖片、陣列相似 等領域的應用 – 2 smlar外掛詳解》
相關文章
- 馬氏距離與歐氏距離
- milvus 使用 l2 歐式距離計算向量的距離,計算出來的距離的最大值是多少?
- 最小距離分類器,互動式選取影像樣本分類資料,進行最小距離分類(實現歐式距離,馬氏距離,計程距離)
- 28、(向量)歐幾里得距離計算
- 【Python】距離Python
- JavaScript 元素距離視窗頂部的距離JavaScript
- 距離度量學習
- PyTorch 實戰:計算 Wasserstein 距離PyTorch
- 編輯距離及編輯距離演算法演算法
- 曼哈頓距離與切比雪夫距離
- JavaScript獲取元素距離文件頂部的距離JavaScript
- Laravel 距離排序Laravel排序
- unit原子距離
- 餘弦距離
- 馬氏距離
- 經緯度距離換算
- Levenshtein:計算字串的編輯距離字串
- 【Leetcode 每日一題】1030. 距離順序排列矩陣單元格(水題,曼哈頓距離排序)LeetCode每日一題矩陣排序
- 求矩陣中向量兩兩間的歐氏距離(python實現)矩陣Python
- 【DP】編輯距離
- 曼哈頓距離與切比雪夫距離的互化
- opencv距離變換函式distanceTransformOpenCV函式ORM
- 距離容差來源
- 461. 漢明距離
- 漢明距離(Hamming distance)
- JAVA計算兩經緯度間的距離Java
- 【leetcode】72. Edit Distance 編輯距離計算LeetCode
- C語言:使用函式計算兩點間的距離C語言函式
- 計算地圖中兩點之間的距離地圖
- 編輯距離演算法演算法
- LeetCode 461. 漢明距離LeetCode
- LeetCode72編輯距離LeetCode
- LeetCode 834 樹中距離之和LeetCode
- 線性dp:編輯距離
- 距離-有這麼多類
- 902.最短編輯距離
- leetcode-624.陣列列表中的最大距離LeetCode陣列
- 你距離PostgreSQL 14版本只有“一鍵之差”SQL
- 微信小程式——計算2點之間的距離微信小程式