【问题标题】:Is there a good way to do this type of mining?有没有做这种类型的采矿的好方法?
【发布时间】:2011-10-27 22:16:28
【问题描述】:

我正在尝试在 X 和 Y 方向上找到空间中最近的点(最后给出的示例数据集),并希望看看是否有比我的琐碎(且未经测试)的方法更聪明的方法来做到这一点。这些点在空间中的图如下所示,我试图找到标记在框内的点集,即我正在寻找的输出是一组组:

Group 1: (1,23), (2,23), (3,23)...
Group 2: (68,200), (68,201), (68,203), (68,204), (68,100), (68,101), (68,101)...

对于水平带,我想我可以继续使用大小为 5 或 10 的小滑动窗口(这应该由全局信息确定,哪个大小将给出最大分组点,但我是仍在探索一种好的方法)并搜索连续点,因为中断不再被视为水平带。

我猜同样的方法也适用于垂直波段,但并非在所有情况下都适用,因为水平和垂直波段存在细微差别:点应该看起来接近水平视为一个组,但它们可以出现在任何地方被认为是垂直带的一部分。观察图中的大垂直带。所以我猜我可以只寻找具有相同 x 坐标的点(在这种情况下,x=68)应该会给我很多分。

除了这个微不足道的解决方案之外,我想不出任何聪明的方法可以在这里完成,因为这个问题对我来说似乎很简单。我在这里错过了什么吗?这是否属于某些已知类别的问题,如果是,是否有一种良好且可扩展的方法来实现这一目标?

示例数据集:

1,23
1,23
2,23
3,23
4,23
5,23
6,23
7,23
8,23
9,23
10,23
11,23
12,23
13,23
14,23
15,23
16,23
10,33
11,33
12,33
13,33
14,33
15,33
16,33
17,33
18,33
19,33
2,28
2,28
3,28
34,75
34,76
34,76
34,77
34,78
34,79
34,80
34,81
34,82
34,83
34,75
34,76
34,76
34,77
34,78
34,79
34,80
34,81
400,28
400,28
400,28
68,200
68,201
68,203
68,204
68,100
68,101
68,103
68,104

【问题讨论】:

  • 聚类算法可能很有趣?
  • 您可以尝试查看 scipy.cluster 模块,尽管您的系统看起来有点不标准。集群必须是唯一的 1 行、1 列,或者是区域,例如(1,1), (1,2), (2,1), (2,2) 是否允许?
  • 看R-tree算法
  • @FredL:实际上,区域也是允许的,只是在我的情况下很难准确地找出一个区域有多大。在朝着您指出的方向前进之前,我试图先获得一维带。
  • @dziobas:您愿意详细说明一下吗?你的意思是,构建一个 R-tree,然后使用所有点查询它以获得空间上近邻?

标签: python algorithm data-mining


【解决方案1】:

这有点晚了,但是这个问题已经让我担心了一段时间。一世 确信它可以用混合整数/线性规划技术来解决 并在这个问题上寻求帮助:Identifying column and row clusters with linear programming

但是,在收到回复后,我了解到您的问题,在 至少据我了解,它是如此简单(当作为约束程序框架时) 你可以用一个简单的程序(你已经 知道)。换句话说,约束规划将是一种很酷的解决方法 这个,但是,至少用我找到的方法,会给你同样的答案 作为更简单的东西。

我将在下面解释我的推理,我将如何使用约束来实现它 求解包,然后给出最终的,琐碎的,算法。

混合整数规划解

最重要的细节是横向和纵向的区别 团体。据我所知,任何垂直对齐的东西都可以在 同一组。但是水平组不同 - 组件必须靠近 在一起。

解决有约束的问题最难的部分似乎是找到一个 以求解器可以理解的方式描述限制的方式。我不会 在这里详细介绍,但求解器非常有限。幸运的是我 觉得这里有办法做到这一点,就是考虑横向 邻居:如果连续有 N 个点,那么我们有 N-1 组 邻居(例如,有 4 个点 A B C 和 D 有三对 AB、BC 和 CD)。

对于每一对,我们可以给出一个分数,即它们之间的空格数 (S_i) 按某个因子 K 和一个标志 (F_i) 缩放,它是 0 或 1。如果 对在同一个水平组中然后我们将标志设置为1,否则它 是零。

看到所有对的标志集完全是至关重要的 定义一个解决方案。我们可以跑过任何行,放置带有标志的对 1 在同一水平组中,并且每个开始一个新的水平组 当标志为 0 时。然后,我们可以取所有大小为 1 的水平组和 将它们转换为垂直组:不在水平组中的任何点 必须在一个垂直组中(即使它是一个只有一个的垂直组)。

所以我们现在需要的只是一种表达最优解的方法 标志。我建议我们尽量减少:

sum(1 - F_i) + sum(K * S_i * F_i)

这有两个术语。第一个是每个“一减旗”的总和 一对。当点在同一水平组中时标志为 1 和 0 否则。所以最小化这个值就等于说我们想要 很少个水平组。如果这是唯一的限制,那么我们 可以通过将所有 F_i 设为 1 将其设置为零 - 通过将所有对设为一行 同一组的成员。

但是第二项阻止我们选择这样一个极端的解决方案。它 惩罚有差距的群体。如果一对在同一组中,但 由S_i 空格分隔,那么我们的“惩罚”为K * S_i

所以我们需要权衡取舍。我们想要水平组,但我们不想要间隙。 最终解决方案将取决于 K - 如果它很大,那么我们将不包括在内 水平组中的任何空间。但是随着它的减少,我们将开始做 所以,直到它非常小(趋于零),我们将所有内容排成一行 在一个组中。

要使用它,您可以选择一些K,计算S_i,然后将上面的表达式输入到约束系统中。然后系统会选择F_i 来最小化表达式。最后,您可以将F_i 转换为组模式,方法是如上所述扫描每一行,然后将单例垂直分组。

解析解

好的,很酷。至此我们就有了表达问题的方法,我们可以给出 到约束引擎。

但解决起来很简单!我们不需要任何臭气熏天的约束引擎 解决这个问题 - 我们可以只看表达式:

sum(1 - F_i) + sum(K * S_i * F_i)

这两个总和在同一对上,所以我们可以将所有内容移入总和:

sum(1 - F_i + K * S_i * F_i)
sum(1 + F_i * (K * S_i - 1))

然后提取常数(N这里是对的总数):

N + sum(F_i * (K * S_i - 1))

现在请注意,总和中的每一项都是独立的(和相加的)。所以对于每个 术语,我们想要最小值。我们有两种选择:

  • 如果F_i 为0,则整个项为0。

  • 否则,F_i 为 1,术语为 K * S_i - 1

所以最好的选择取决于K * S_i是否大于1。如果K * S_i大于1则该项的最小值为0,而F_i 应该是0。否则上面的第二个选择是否定的,F_i应该 成为一个。

简单算法

这是什么意思?这意味着对于每一对,我们可以简单地查看 空格数,S_i。如果大于1 / K 那么这两点 应该在不同的组中。否则他们应该在同一个组中。

所以所有这些花哨的数学和优化、约束和废话 归结为:相邻对中的两个点相距多远?如果他们 比一些截止点更近,将它们放在同一个水平组中。 否则,请将它们分成不同的组。

所以,最后,是你的算法:

choose some cut-off value, X
place each point in its own, singleton, horizontal group
for each row with more than one point:
    for each neighbouring pair in the row:
        if the space between the pair is less than X:
            join into a single horizontal group
for each column:
    join any remaining singleton groups into a single vertical group

结论

  • 您可以使用约束规划技术来解决此问题,但此类技术仅限于以“正确”(通常是线性)方式描述系统的解决方案。

  • 我能找到的最简单的这种方法相当于一个简单的、直接的 将一行中的点划分为水平组的算法,具体取决于 它们之间的空格数。

  • 这一切都取决于一堆关于你想要什么的假设,当然, 过于简单化,或者完全是错误的。

【讨论】:

    【解决方案2】:

    您可以尝试使用cluster module。它包含 K-means 聚类算法的实现。您可以调整getclusters 函数的参数来更改您想要的集群数量。

    s = '''
    1,23
    1,23
    2,23
    ...
    68,101
    68,103
    68,104
    '''
    
    from cluster import *
    
    ll = [tuple(map(int,each.split(','))) for each in s.split()]
    
    #horizontal 
    cl = HierarchicalClustering(ll, lambda x,y: abs(x[0]-y[0]))
    
    for c in cl.getlevel(1):
        print c
    
    #vertical
    cl = HierarchicalClustering(ll, lambda x,y: abs(x[1]-y[1]))
    
    for c in cl.getlevel(1):
        print c
    

    【讨论】:

    • 我不认为 k-means 聚类只会返回 1xn 和 nx1 集群,据我所知这是 OP 的要求。
    • @Abhinav 我不认为这是他的要求。但是让我再读一遍这个问题。这次小心! :)
    • OP 提到了水平带和垂直带,所以看起来他正在寻找一种方法来仅在带中的点中进行聚类。
    • @Abhinav 我已根据您的建议更新了答案。谢谢! :)
    • 太棒了!到目前为止,它确实给了我想要的东西,并接受了它作为解决方案。如果我发现其他内容,我会提供更新:) 非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-06
    • 2021-11-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多