我想通过在两个最近的邻居之间插值来计算一个值。我有一个子查询,该查询以两列和两个元素的形式返回邻居的值及其相对距离。
比方说:
(select ... as value, ... as distance from [get some neighbours by distance] limit 2) as sub
如何通过线性插值计算点的值?是否可以在单个查询中执行此操作?
示例: 我的点的邻居A在距离1处的值为10,邻居B在距离4处的值为20。该函数应10 * 4 + 20 * 1 / 5 = 12为我的点返回一个值。
10 * 4 + 20 * 1 / 5 = 12
我尝试了明显的方法
select sum(value * (sum(distance)-distance)) / sum(distance)
这将失败,因为您无法使用group子句中的group子句。也不能使用另一个子查询返回总和,因为那样我就不能同时转发各个值。
这是一个丑陋的黑客行为(基于滥用的CTE;)。关键是
value1 * distance2 + value2 * distance1
可以通过除以distance1 * distance2来重写为
value1/distance1 + value2/distance2
因此,产品(或部门)可以留在其行内。求和后,乘以(distance1 * distance2)可将结果重新缩放为所需的输出。读者可以将对两个以上邻居的归纳作为练习。
DROP TABLE tmp.points; CREATE TABLE tmp.points ( pname VARCHAR NOT NULL PRIMARY KEY , distance INTEGER NOT NULL , value INTEGER ); INSERT INTO tmp.points(pname, distance, value) VALUES ( 'A' , 1, 10 ) , ( 'B' , 4, 20 ) , ( 'C' , 10 , 1) , ( 'D' , 11 , 2) ; WITH RECURSIVE twin AS ( select 1::INTEGER AS zrank , p0.pname AS zname , p0.distance AS dist , p0.value AS val , p0.distance* p0.value AS prod , p0.value::float / p0.distance AS frac FROM tmp.points p0 WHERE NOT EXISTS ( SELECT * FROM tmp.points px WHERE px.distance < p0.distance) UNION select 1+twin.zrank AS zrank , p1.pname AS zname , p1.distance AS dist , p1.value AS val , p1.distance* p1.value AS prod , p1.value::float / p1.distance AS frac FROM tmp.points p1, twin WHERE p1.distance > twin.dist AND NOT EXISTS ( SELECT * FROM tmp.points px WHERE px.distance > twin.dist AND px.distance < p1.distance ) ) -- SELECT * from twin ; SELECT min(zname) AS name1, max(zname) AS name2 , MIN(dist) * max(dist) *SUM(frac) / SUM(dist) AS score FROM twin WHERE zrank <=2 ;
结果:
CREATE TABLE INSERT 0 4 name1 | name2 | score -------+-------+------- A | B | 12
更新:这 有点 干净…关联仍未处理(为此需要外部函数中的窗口函数或LIMIT 1子句)
WITH RECURSIVE twin AS ( select 1::INTEGER AS zrank , p0.pname AS name1 , p0.pname AS name2 , p0.distance AS dist FROM tmp.points p0 WHERE NOT EXISTS ( SELECT * FROM tmp.points px WHERE px.distance < p0.distance) UNION select 1+twin.zrank AS zrank , twin.name1 AS name1 , p1.pname AS name2 , p1.distance AS dist FROM tmp.points p1, twin WHERE p1.distance > twin.dist AND NOT EXISTS ( SELECT * FROM tmp.points px WHERE px.distance > twin.dist AND px.distance < p1.distance ) ) SELECT twin.name1, twin.name2 , (p1.distance * p2.value + p2.distance * p1.value) / (p1.distance+p2.distance) AS score FROM twin JOIN tmp.points p1 ON (p1.pname = twin.name1) JOIN tmp.points p2 ON (p2.pname = twin.name2) WHERE twin.zrank =2 ;