【问题标题】:Python: nearest neighbour (or closest match) filtering on data records (list of tuples)Python:对数据记录(元组列表)进行最近邻(或最接近匹配)过滤
【发布时间】:2012-04-03 20:36:45
【问题描述】:

我正在尝试编写一个函数,该函数将使用“最近邻”或“最近匹配”类型算法过滤元组列表(模拟内存数据库)。

我想知道执行此操作的最佳(即大多数 Pythonic)方法。下面的示例代码希望能说明我正在尝试做的事情。

datarows = [(10,2.0,3.4,100),
            (11,2.0,5.4,120),
            (17,12.9,42,123)]

filter_record = (9,1.9,2.9,99) # record that we are seeking to retrieve from 'database' (or nearest match)
weights = (1,1,1,1) # weights to approportion to each field in the filter

def get_nearest_neighbour(data, criteria, weights):
    for each row in data:
        # calculate 'distance metric' (e.g. simple differencing) and multiply by relevant weight
    # determine the row which was either an exact match or was 'least dissimilar'
    # return the match (or nearest match)
    pass

if __name__ == '__main__':
    result = get_nearest_neighbour(datarow, filter_record, weights)
    print result

对于上面的sn-p,输出应该是:

(10,2.0,3.4,100)

因为它是传递给函数 get_nearest_neighbour() 的样本数据的“最近”。

那么我的问题是,实现 get_nearest_neighbour() 的最佳方式是什么?出于简洁等目的,假设我们只处理数值,并且我们使用的“距离度量”只是当前行中输入数据的算术减法。

【问题讨论】:

    标签: python


    【解决方案1】:

    开箱即用的简单解决方案:

    import math
    
    def distance(row_a, row_b, weights):
        diffs = [math.fabs(a-b) for a,b in zip(row_a, row_b)]
        return sum([v*w for v,w in zip(diffs, weights)])
    
    def get_nearest_neighbour(data, criteria, weights):
        def sort_func(row):
            return distance(row, criteria, weights)
        return min(data, key=sort_func)
    

    如果您需要处理庞大的数据集,您应该考虑切换到 Numpy 并使用 Numpy 的 KDTree 来查找最近的邻居。使用 Numpy 的优势在于,它不仅使用了更高级的算法,而且实现了高度优化的 LAPACK (Linear Algebra PACKage) 的顶部。

    【讨论】:

    • 您可以使用min 代替sorted -- 它也需要一个key 参数。
    • 这是一个 O(N)-per-query 算法,相当于朴素算法。中等大小的#points*#queries需要很长时间。
    • @ninjagecko:但是鉴于问题中没有任何内容表明会有数百万行,您提出的解决方案是过早的优化。 (stackoverflow.com/questions/2978460/…)
    • @vartec:我很清楚过早的优化。人们把它当作某种圣杯。我自己也为将其用作辩护而感到内疚。我并没有否定您的答案,只是指出该算法的效率远低于标准,但如果 查询乘积乘以行 小于,比如说一百万(例如 10000 个查询和 100 行),否则你会遇到问题。如果不是,那么更易于编写的解决方案确实更可取。
    【解决方案2】:

    关于 naive-NN:

    许多其他答案提出“朴素最近邻”,这是一个O(N*d)-per-query 算法(d 是维度,在这种情况下似乎是恒定的,所以它是 O(N)-per-query) .

    虽然O(N)-per-query 算法非常糟糕,但如果你的算法少于以下任何一个(例如),你或许可以摆脱它:

    • 10个查询和100000个点
    • 100个查询和10000个点
    • 1000个查询和1000个积分
    • 10000 个查询和 100 个积分
    • 100000 次查询和 10 分

    比 naive-NN 做得更好:

    否则,您将需要使用下列技术之一(尤其是最近邻数据结构):

    尤其是如果您计划多次运行您的程序。很可能有可用的库。如果您有大量#queries * #points 的乘积,否则不使用 NN 数据结构将花费太多时间。正如用户 'dsign' 在 cmets 中指出的那样,您可以通过使用 numpy 库大概挤出一个很大的额外恒定速度因子。

    但是,如果您可以避免使用易于实现的 naive-NN,那么您应该使用它。

    【讨论】:

    • 另外,如果速度是一个问题,其他有用的 hack,如使用 numpy、sqlite3 内存数据库、cython 或一些无害的 c 编码可以有很长的路要走...
    【解决方案3】:

    在生成器上使用 heapq.nlargest 计算每条记录的距离*权重。

    类似:

    heapq.nlargest(N, ((row, dist_function(row,criteria,weight)) for row in data), operator.itemgetter(1))
    

    【讨论】:

    • 这是一个 O(N)-per-query(或更糟)的算法,相当于朴素算法。中等大小的#points*#queries 需要很长时间。 (如果你用排序函数实例化一个队列并且没有预处理,它必须查看每个元素,这至少是 O(N)。)你不能重用队列,因为距离函数取决于查询点。
    • @ninjagecko 但它仍然避免对整个事物进行排序,并为您提供多个最近的邻居(min 无法给您)。如果我想要一个,我当然会使用 min 而不是 nlargest ,因为它本来就比较慢。但是你怎么知道你是否有不止一个最近的邻居?顺便说一句,这个数据结构可能会有所帮助:blog.notdot.net/2007/4/Damn-Cool-Algorithms-Part-1-BK-Trees
    • 它是否真正避免(有效地)对事物进行排序取决于heapq的实现,主要取决于插入时间。 python 文档似乎暗示heapq 实际上是使用二叉树/堆实现的,因此插入时间为O(log(N))。因此创建堆是O(N log(N)),这与排序一样糟糕。通过更好的堆实现,可以实现O(N)(堆可能是最简单的编码方式)。再说一次,做一次线性搜索 k('nlargest' 中的 'n')同样便宜。
    • @ninjagecko 实际上更像是 O(N * log(M)) 其中 N 是数据的大小, M 是您想要的最大项目的数量。我尝试将它与 sorted(foo)[:M] 进行比较,它当然不会基于 M 改变,你可以看到(除了 nlargest 快得多)它取决于堆的大小。我还没有阅读 C 实现,但我确定它不会首先堆积孔列表,它会创建一个大小为 M 的堆,然后迭代地将项目推送到它,同时弹出其他项目。
    • 嗯,这很有趣……您的分析似乎很合理。感谢您让我了解heapq.nlargest 的表现。 =) 我猜你可以使用标准的heapq 来实现naive-NN;似乎是这样做的合理方法。
    猜你喜欢
    • 2017-07-11
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 1970-01-01
    • 2016-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多