我可以使用什么算法来识别此散点图中的线?

时间:2013-11-30 03:08:02

标签: scipy linear-regression

我正在创建一个程序来比较使用类似算法的音频文件http://www.ee.columbia.edu/~dpwe/papers/Wang03-shazam.pdf。我正在绘制两首被比较的歌曲之间的匹配时间,并找到该情节的最小二乘线。 href = http://imgur.com/fGu7jhX&yOeMSK0是匹配文件的示例图。该图太杂乱,即使图中有明显的线,最小二乘回归线也不会产生高相关系数。我可以用什么其他算法来识别这一行?

2 个答案:

答案 0 :(得分:5)

这是一个有趣的问题,但它一直很安静。也许这个答案 会引发更多的活动。

用于识别具有任意斜率的线和集合内的截距 对于点,霍夫变换将是一个很好的起点。对于你的音频 但是,看起来坡度应该始终为1,所以你不应该这样 需要霍夫变换的完整普遍性。

相反,您可以将问题视为聚类差异x - y之一,其中xy是包含点的x和y坐标的向量。

一种方法是计算x - y的直方图。接近于具有斜率1的同一行中的点将在直方图中的相同仓中具有差异。具有最大计数的仓对应于近似对齐的最大点集合。在这种方法中要处理的一个问题是选择直方图箱的边界。一个糟糕的选择可能会导致应该组合在一起的点被拆分成相邻的区域。

一种简单的蛮力方法是想象一个具有给定宽度的对角窗口,在(x,y)平面上从左向右滑动。线条的最佳候选对应于包含最多点的窗口的位置。这类似于x - y的直方图,但不是有一组不相交的箱子,而是有重叠的箱子,每个点一个。所有箱子的宽度都相同,每个点都决定了箱子的左边缘。

下面代码中的函数count_diag_groups执行该计算。对于每个点,它计算当窗口的左边缘在该点上时对角线窗口中有多少个点。一条线的最佳候选者是得分最多的窗口。这是脚本生成的图。顶部是数据的散点图。瓶底是相同的散点图,突出显示最佳候选点。

Plot generated by the script

此方法的一个很好的功能是只有一个参数,窗口宽度。一个不太好的功能是它具有时间复杂度O(n ** 2),其中n是点数。肯定有时间复杂度更高的算法可以做类似的事情;您链接的文章讨论了这一点。然而,要判断替代品的质量,将需要更具体的规范,以确定线路识别必须“良好”或强大。

import numpy as np
import matplotlib.pyplot as plt


def count_diag_groups(x, y, width):
    """
    Returns a list of arrays.  The length of the list is the same
    as the length of x.  The k-th array holds the indices into x
    (and y) of a set of points that are in a "diagonal" window with
    the given width whose left edge includes the point (x[k], y[k]).
    """
    d = x - y
    result = []
    for i in range(d.size):
        delta = d - d[i]
        neighbors = np.where((delta >= 0) & (delta <= width))[0]
        result.append(neighbors)
    return result


def generate_demo_data():
    # Generate some data.
    np.random.seed(123)
    xmin = 0
    xmax = 100
    ymin = 0
    ymax = 25
    nrnd = 175
    xrnd = xmin + (xmax - xmin)*np.random.rand(nrnd)
    yrnd = ymin + (ymax - ymin)*np.random.rand(nrnd)
    n = 25
    xx = xmin + 0.1*(xmax - xmin) + ymax*np.random.rand(n)
    yy = (xx - xx.min()) + 0.2*np.random.randn(n)
    x = np.concatenate((xrnd, xx))
    y = np.concatenate((yrnd, yy))
    return x, y


def plot_result(x, y, width, selection):
    xmin = x.min()
    xmax = x.max()
    ymin = y.min()
    ymax = y.max()

    xsel = x[selection]
    ysel = y[selection]
    # Plot...
    plt.figure(1)
    plt.clf()
    ax = plt.subplot(2,1,1)
    plt.plot(x, y, 'o', mfc='b', mec='b', alpha=0.5)
    plt.xlim(xmin - 1, xmax + 1)
    plt.ylim(ymin - 1, ymax + 1)

    plt.subplot(2,1,2, sharex=ax, sharey=ax)
    plt.plot(x, y, 'o', mfc='b', mec='b', alpha=0.5)
    plt.plot(xsel, ysel, 'o', mfc='w', mec='w')
    plt.plot(xsel, ysel, 'o', mfc='r', mec='r', alpha=0.65)
    xi = np.array([xmin, xmax])
    d = x - y
    yi1 = xi - d[imax]
    yi2 = yi1 - width
    plt.plot(xi, yi1, 'r-', alpha=0.25)
    plt.plot(xi, yi2, 'r-', alpha=0.25)
    plt.xlim(xmin - 1, xmax + 1)
    plt.ylim(ymin - 1, ymax + 1)

    plt.show()

if __name__ == "__main__":
    x, y = generate_demo_data()

    # Find a selection of points that are close to being aligned
    # with a slope of 1.
    width = 0.75
    r = count_diag_groups(x, y, width)

    # Find the largest group.
    sz = np.array(list(len(f) for f in r))
    imax = sz.argmax()
    # k holds the indices of the selected points.
    selection = r[imax]

    plot_result(x, y, width, selection)

答案 1 :(得分:4)

这看起来像Random Sampling Consensus (RANSAC)的任务的一个很好的例子。 维基百科的文章甚至以您的问题为例!

粗略的轮廓是这样的。

  1. 在数据中选择2个随机点,为其添加一行
  2. 对于每个其他点,找到该线的距离。如果距离低于阈值,则它是内部集合的一部分。
  3. 如果为此特定行设置的最终内部值大于之前的最佳行,则将新行保留为最佳候选者。
  4. 如果达到了确定的迭代次数,则返回找到的最佳线,否则返回1并选择新的随机点。
  5. 查看维基百科文章以获取更多信息。