选择最近的邻居

Posted

技术标签:

【中文标题】选择最近的邻居【英文标题】:select nearest neighbours 【发布时间】:2015-07-31 05:24:01 【问题描述】:

考虑以下数据:

category | index | value
-------------------------
cat 1    | 1     | 2
cat 1    | 2     | 3
cat 1    | 3     |  
cat 1    | 4     | 1
cat 2    | 1     | 5
cat 2    | 2     |  
cat 2    | 3     |  
cat 2    | 4     | 6
cat 3    | 1     |  
cat 3    | 2     |  
cat 3    | 3     | 2 
cat 3    | 4     | 1

我正在尝试填补空洞,以便 hole = avg(value) 在一个类别中具有非空值的 2 个最近邻居:

category | index | value
-------------------------
cat 1    | 1     | 2
cat 1    | 2     | 3
cat 1    | 3     | 2*
cat 1    | 4     | 1
cat 2    | 1     | 5
cat 2    | 2     | 5.5*
cat 2    | 3     | 5.5* 
cat 2    | 4     | 6
cat 3    | 1     | 1.5*
cat 3    | 2     | 1.5* 
cat 3    | 3     | 2 
cat 3    | 4     | 1

我一直在玩窗口函数,我很确定它可以实现,但解决方案却让我望而却步。

有什么想法吗?

【问题讨论】:

为什么26之间的值平均为1.5而不是4 另外,你必须只使用 Postgres 来解决这个问题吗?我很难在像 R 或 Matlab 这样的工具中做到这一点,这两种工具都是为这类事情设计的。 '6' 不在 'cat 3' 中。在 cat 3 中,最近的邻居是 2 和 1。 只感谢postgresql。 【参考方案1】:

你是对的,窗口函数就是你要找的。下面是它的实现方法(with 部分用于定义表,因此您可能不需要它):

with dt as
(
    select * from
    (
        values
            ('cat 1', 1, 2),
            ('cat 1', 2, 3),
            ('cat 1', 3, null),
            ('cat 1', 4, 1),
            ('cat 2', 1, 5),
            ('cat 2', 2, null),
            ('cat 2', 3, null),
            ('cat 2', 4, 6),
            ('cat 3', 1, null),
            ('cat 3', 2, null),
            ('cat 3', 3, 1),
            ('cat 3', 4, 2)

    ) tbl ("category", "index", "value")
)
select
        "category",
        "index",
        case
            when "value" is null then (avg("value") over (partition by "category") )
            else "value"
        end
    from dt
    order by "category", "index";

有关窗口功能的更多信息,请参阅this 页面的WINDOW Clause 部分。

【讨论】:

我不得不做一些稍微不同的事情,涉及内部查询和row_number(),但您的回答非常好。【参考方案2】:

我正在为您制定解决方案,但 SQLfiddle 目前出现(内部)错误,因此我无法完成。

这样的声明应该会为你做更新:

update table1 as t1
set value = 
  (select avg(value)
   from 
   (select value
    from table1 as t3
    where t1.category = t3.category
    and   t3.index in (t1.index - 1, t1.index + 1)
    ) AS T2
   )
where value is null
;

我正在研究的小提琴在这里:http://sqlfiddle.com/#!15/acbc2/1

【讨论】:

index+1 和 index-1 并不总是最近的邻居,因为缺失的行可能是该类别中的第一行,或者空洞可能是 2x 行 那么解决方案是使用MIN()MAX() 函数。如果 sqlfiddle 对我有用,我以后也许可以修改它。目前我无法进行实验。【参考方案3】:

虽然我确信可以编写一些极其复杂和嵌套的语句来满足您的需求,但我想说的是,有时最好用常规编程语言(如 python/ruby/java)编写脚本遍历数据库表并进行您想要的任何更改。

这将更易于维护,并且您希望每次需要对其进行任何更改时都必须重新架构整个事物(例如使用 3 个最近的邻居,或更改“最近邻居”的定义)

【讨论】:

但效率低得可怜!

以上是关于选择最近的邻居的主要内容,如果未能解决你的问题,请参考以下文章

如何在 O(n) 时间内找到与 n 个不同数字的中位数最近的 k 个邻居?

最近邻居图中第 k 个邻居的奇怪距离

使用 morton 代码查找最近的邻居

点的第 k 个最近邻居的空间查询

C++ - 使用 opencv flann 查找最近的邻居

JavaScript 函数最近的地理邻居