如何找到数据点集群的中心?

时间:2013-06-14 16:03:16

标签: algorithm geocoding cluster-analysis data-mining markerclusterer

假设我在过去的一年中每天绘制一架直升机的位置,并得出以下地图:

Map

任何看到这个人的人都能告诉我这架直升机是在芝加哥以外的。

如何在代码中找到相同的结果?

我正在寻找类似的东西:

$geoCodeArray = array([GET=http://pastebin.com/grVsbgL9]);
function findHome($geoCodeArray) {
    // magic
    return $geoCode;
}

最终产生这样的东西:

Map-Home

更新:示例数据集

这是一张包含样本数据集的地图:http://batchgeo.com/map/c3676fe29985f00e1605cd4f86920179

这是一个包含150个地理编码的pastebin:http://pastebin.com/grVsbgL9

以上包含150个地理编码。前50个在靠近芝加哥的几个集群中。其余的分布在全国各地,包括纽约,洛杉矶和旧金山的一些小集群。

我有大约一百万(严重)这样的数据集,我需要迭代并确定最可能的“家”。非常感谢您的帮助。

更新2:飞机切换到直升机

飞机概念引起了对物理机场的过多关注。坐标可以在世界的任何地方,而不仅仅是机场。让我们假设它是一架超级直升机,不受物理,燃料或其他任何东西的束缚。它可以降落在它想要的地方。 ;)

14 个答案:

答案 0 :(得分:15)

通过将纬度和经度转换为笛卡尔坐标,即使点遍布地球,以下解法仍然有效。它执行一种KDE(内核密度估计),但在第一次传递中,仅在数据点处评估内核的总和。应该选择内核以适应问题。在下面的代码中,我可以开玩笑地/擅自称为Trossian,即d≤h为2-d²/h²,d> h为h²/d²(其中d为欧几里德距离,h为“带宽”{ {1}}),但它也可以是高斯(e -d²/2h²),Epanechnikov内核(d $global_kernel_radius定义的周围情况下计算质心,在本地优化搜索。

本质上,每个点对它周围的所有点(包括它自身)求和,如果它们更接近(通过钟形曲线),则称它们更多,并且还通过可选的权重数组对它们进行加权$local_grid_radius。获胜者是具有最大金额的点。一旦找到胜利者,我们正在寻找的“家”可以通过在获胜者周围重复相同的过程(使用另一个钟形曲线),或者可以估计为所有点的“质心”在获胜者的给定半径范围内,半径可以为零。

算法必须通过选择适当的内核,选择如何在本地优化搜索以及调整参数来适应问题。对于示例数据集,第一遍的Trossian内核和第二遍的Epanechnikov内核,所有3个半径设置为30英里,网格步长为1英里可能是一个很好的起点,但只有两个子芝加哥群集应被视为一个大集群。否则必须选择较小的半径。

$w_arr

距离是欧几里德而不是大圆的事实应该对手头的任务产生微不足道的影响。计算大圆距离会更麻烦,并且只会导致非常远的点的重量显着降低 - 但这些点的重量已经非常低。原则上,不同的内核可以实现相同的效果。具有超出一定距离的完全截止的内核,如Epanechnikov内核,根本没有这个问题(在实践中)。

WGS84基准的lat,lng和x,y,z之间的转换(尽管不保证数值稳定性)更多地作为参考,而不是真正的需要。如果要考虑高度,或者需要更快的反向转换,请参阅Wikipedia article

Epanechnikov内核除了比高斯和Trossian内核“更本地”外,还具有第二个循环最快的优势,即O(ng),其中g是本地网格的点数如果n很大,也可以在第一个循环中使用,即O(n²)。

答案 1 :(得分:10)

这可以通过找到危险的表面来解决。请参阅Rossmo's Formula

这是捕食者问题。鉴于一组地理位置上的尸体,掠食者的巢穴在哪里?罗斯莫的公式解决了这个问题。

答案 2 :(得分:7)

使用最大密度估算值找到该点。

应该非常简单明了。使用大致覆盖直径大型机场的核心半径。 2D Gaussian或Epanechnikov内核应该没问题。

http://en.wikipedia.org/wiki/Multivariate_kernel_density_estimation

这类似于计算堆映射:http://en.wikipedia.org/wiki/Heat_map 然后找到那里最亮的地方。除了它立即计算亮度。

为了好玩,我将1%DBpedia的Geocoordinates(即维基百科)样本读入ELKI,将其投影到3D空间并启用密度估计叠加(隐藏在可视化器散点图菜单中)。你可以看到欧洲有一个热点,而在美国则有一个较小的热点。我相信欧洲的热点是波兰。最后我查了一下,显然有人在波兰的几乎任何一个城镇为Geocoordinates创建了一篇维基百科文章。遗憾的是,ELKI可视化工具既不允许您放大,旋转或减少内核带宽,也无法直观地找到最密集的点。但实施自己很简单;你可能也不需要进入3D空间,但只能使用纬度和经度。

Density of DBpedia Geo data.

核心密度估算应该在应用程序中可用。 R中的那个可能更强大。我刚刚在ELKI中发现了这个热图,所以我知道如何快速访问它。参见例如http://stat.ethz.ch/R-manual/R-devel/library/stats/html/density.html用于相关的R函数。

在您的数据上,在R中,尝试例如:

library(kernSmooth)
smoothScatter(data, nbin=512, bandwidth=c(.25,.25))

这应该表现出对芝加哥的强烈偏好。

library(kernSmooth)
dens=bkde2D(data, gridsize=c(512, 512), bandwidth=c(.25,.25))
contour(dens$x1, dens$x2, dens$fhat)
maxpos = which(dens$fhat == max(dens$fhat), arr.ind=TRUE)
c(dens$x1[maxpos[1]], dens$x2[maxpos[2]])

收益率[1] 42.14697 -88.09508,离芝加哥机场不到10英里。

要获得更好的坐标,请尝试:

  • 在估计坐标周围的20x20英里区域重新运行
  • 该区域内的非分箱KDE
  • 使用dpik
  • 更好地选择带宽
  • 更高的网格分辨率

答案 3 :(得分:5)

在天体物理学中,我们使用所谓的半质量半径"。给定分布及其中心,半质量半径是包含分布的一半点的圆的最小半径。

此数量是点数分布的特征长度。

如果你想直升飞机的位置是最大程度集中点,那么它就是具有最小半质量半径的点!

我的算法如下:对于每个点,您计算这个半质量半径,以当前点的分布为中心。 "家庭"直升机将是最小半质量半径的点。

我实现了它,计算中心是42.149994 -88.133698(在芝加哥) 我还使用了总质量的0.2而不是通常用于天体物理学的0.5(一半)。

这是我(在python中)alghorithm找到直升机的家:

import math

import numpy

def inside(points,center,radius):
     ids=(((points[:,0]-center[0])**2.+(points[:,1]-center[1])**2.)<=radius**2.)
     return points[ids]

points = numpy.loadtxt(open('points.txt'),comments='#')

npoints=len(points)
deltar=0.1

idcenter=None
halfrmin=None

for i in xrange(0,npoints):
    center=points[i]
    radius=0.

    stayHere=True
    while stayHere:
         radius=radius+deltar
         ninside=len(inside(points,center,radius))
         #print 'point',i,'r',radius,'in',ninside,'center',center
         if(ninside>=npoints*0.2):
              if(halfrmin==None or radius<halfrmin):
                   halfrmin=radius
                   idcenter=i
                   print 'point',i,halfrmin,idcenter,points[idcenter]
              stayHere=False

#print halfrmin,idcenter
print points[idcenter]

答案 4 :(得分:4)

您可以使用DBSCAN执行该任务。

DBSCAN是一种基于密度的聚类,具有噪声概念。您需要两个参数:

首先,群集应具有的点数最少为"minpoints"。 第二个是一个名为"epsilon"的邻域参数,它为应该包含在群集中的周围点设置距离阈值。

整个算法的工作原理如下:

  1. 从您尚未访问的集合中的任意点开始
  2. 从所访问的epsilon邻域标记中检索点
    1. 如果您在此邻域中找到了足够的点(&gt; minpoints参数),则启动新群集并分配这些点。现在再次针对此群集中的每个点递归到第2步。
    2. 如果您没有,请将此点声明为噪音
  3. 重新开始,直到你访问过所有积分
  4. 实现起来非常简单,并且已经有很多框架支持这种算法。要查找群集的平均值,您可以简单地从其邻域中获取所有指定点的平均值。

    然而,与@TylerDurden提出的方法不同,这需要参数化 - 因此您需要找到适合您问题的一些手动调整参数。

    在您的情况下,如果飞机可能保持在您在机场跟踪的时间的10%,则可以尝试将最小点设置为总点数的10%。密度参数epsilon取决于您的地理传感器的分辨率和您使用的距离指标 - 我建议haversine distance用于地理数据。

答案 5 :(得分:3)

enter image description here

如何将地图划分为多个区域,然后在最平面的区域中找到平面的中心。算法将是这样的

set Zones[40]
foreach Plane in Planes
   Zones[GetZone(Plane.position)].Add(Plane)

set MaxZone = Zones[0]
foreach Zone in Zones
   if MaxZone.Length() < Zone.Length()
           MaxZone = Zone

set Center
foreach Plane in MaxZone
     Center.X += Plane.X
     Center.Y += Plane.Y
Center.X /= MaxZone.Length
Center.Y /= MaxZone.Length

答案 6 :(得分:3)

我在这台机器上拥有的只是一个旧编译器,因此我制作了这个版本的ASCII版本。它“绘制”(用ASCII表示)一个地图 - 点是点,X是真实来源的地方,G是猜测源的地方。如果两者重叠,则仅显示X.

示例(分别为困难1.5和3): enter image description here

通过选择随机点作为源,然后随机分布点,使它们更可能更接近源,生成点。

DIFFICULTY是一个浮点常数,用于调节初始点生成 - 点距离源更近的可能性 - 如果它是1或更小,程序应该能够准确猜出来源,或非常接近。在2.5,它应该还是相当不错的。在4岁以上时,它会开始猜测更糟,但我认为它仍然比人类想象的要好。

可以通过在X上使用二进制搜索来优化它,然后Y - 这会使猜测变得更糟,但会更快,更快。或者从较大的块开始,然后进一步分割最佳块(或最好的块和围绕它的8)。对于更高分辨率的系统,其中一个是必要的。但这是一种非常天真的方法,但它似乎在80x24系统中运行良好。 :d

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#define Y 24
#define X 80

#define DIFFICULTY 1 // Try different values... 

static int point[Y][X];

double dist(int x1, int y1, int x2, int y2)
{
    return sqrt((y1 - y2)*(y1 - y2) + (x1 - x2)*(x1 - x2));
}

main()
{
    srand(time(0));
    int y = rand()%Y;
    int x = rand()%X;

    // Generate points
    for (int i = 0; i < Y; i++)
    {
        for (int j = 0; j < X; j++)
        {
            double u = DIFFICULTY * pow(dist(x, y, j, i), 1.0 / DIFFICULTY);
            if ((int)u == 0)
                u = 1;
            point[i][j] = !(rand()%(int)u);
        }
    }

    // Find best source
    int maxX = -1;
    int maxY = -1;
    double maxScore = -1;
    for (int cy = 0; cy < Y; cy++)
    {
        for (int cx = 0; cx < X; cx++)
        {
            double score = 0;
            for (int i = 0; i < Y; i++)
            {
                for (int j = 0; j < X; j++)
                {
                    if (point[i][j] == 1)
                    {
                        double d = dist(cx, cy, j, i);
                        if (d == 0)
                            d = 0.5;
                        score += 1000 / d;
                    }
                }
            }
            if (score > maxScore || maxScore == -1)
            {
                maxScore = score;
                maxX = cx;
                maxY = cy;
            }
        }
    }

    // Print out results
    for (int i = 0; i < Y; i++)
    {
        for (int j = 0; j < X; j++)
        {
            if (i == y && j == x)
                printf("X");
            else if (i == maxY && j == maxX)
                printf("G");            
            else if (point[i][j] == 0)
                printf(" ");
            else if (point[i][j] == 1)
                printf(".");
        }
    }
    printf("Distance from real source: %f", dist(maxX, maxY, x, y));

    scanf("%d", 0);

} 

答案 7 :(得分:1)

虚拟地球有一个非常好的解释,说明如何相对快速地做到这一点。他们还提供了代码示例。请查看http://soulsolutions.com.au/Articles/ClusteringVirtualEarthPart1.aspx

答案 8 :(得分:1)

简单的混合模型似乎对这个问题很有效。

通常,要获得最小化到数据集中所有其他点的距离的点,您可以采用均值。在这种情况下,您希望找到一个点,该点最小化与集中点子集的距离。如果你假设一个点既可以来自集中的兴趣点,也可以来自一组漫反射的背景点,那么就会给出一个混合模型。

我在下面包含了一些python代码。集中区域由高精度正态分布建模,背景点由数据集上的边界框上的低精度正态分布或均匀分布建模(有一行代码可以注释掉在这些选项之间切换)。此外,混合模型可能有些不稳定,因此在随机初始条件下运行EM算法几次并选择具有最高对数似然的运行会得到更好的结果。

如果您实际上在观看飞机,那么添加某种依赖于时间的动力可能会提高您推断本垒的能力。

我也会对罗西莫的公式保持警惕,因为它包含了一些关于犯罪分布的非常强烈的假设。

#the dataset
sdata='''41.892694,-87.670898
42.056048,-88.000488
41.941744,-88.000488
42.072361,-88.209229
42.091933,-87.982635
42.149994,-88.133698
42.171371,-88.286133
42.23241,-88.305359
42.196811,-88.099365
42.189689,-88.188629
42.17646,-88.173523
42.180531,-88.209229
42.18168,-88.187943
42.185496,-88.166656
42.170485,-88.150864
42.150634,-88.140564
42.156743,-88.123741
42.118555,-88.105545
42.121356,-88.112755
42.115499,-88.102112
42.119319,-88.112411
42.118046,-88.110695
42.117791,-88.109322
42.182189,-88.182449
42.194145,-88.183823
42.189057,-88.196182
42.186513,-88.200645
42.180917,-88.197899
42.178881,-88.192062
41.881656,-87.6297
41.875521,-87.6297
41.87872,-87.636566
41.872073,-87.62661
41.868239,-87.634506
41.86875,-87.624893
41.883065,-87.62352
41.881021,-87.619743
41.879998,-87.620087
41.8915,-87.633476
41.875163,-87.620773
41.879125,-87.62558
41.862763,-87.608757
41.858672,-87.607899
41.865192,-87.615795
41.87005,-87.62043
42.073061,-87.973022
42.317241,-88.187256
42.272546,-88.088379
42.244086,-87.890625
42.044512,-88.28064
39.754977,-86.154785
39.754977,-89.648437
41.043369,-85.12207
43.050074,-89.406738
43.082179,-87.912598
42.7281,-84.572754
39.974226,-83.056641
38.888093,-77.01416
39.923692,-75.168457
40.794318,-73.959961
40.877439,-73.146973
40.611086,-73.740234
40.627764,-73.234863
41.784881,-71.367187
42.371988,-70.993652
35.224587,-80.793457
36.753465,-76.069336
39.263361,-76.530762
25.737127,-80.222168
26.644083,-81.958008
30.50223,-87.275391
29.436309,-98.525391
30.217839,-97.844238
29.742023,-95.361328
31.500409,-97.163086
32.691688,-96.877441
32.691688,-97.404785
35.095754,-106.655273
33.425138,-112.104492
32.873244,-117.114258
33.973545,-118.256836
33.681497,-117.905273
33.622982,-117.734985
33.741828,-118.092041
33.64585,-117.861328
33.700707,-118.015137
33.801189,-118.251343
33.513132,-117.740479
32.777244,-117.235107
32.707939,-117.158203
32.703317,-117.268066
32.610821,-117.075806
34.419726,-119.701538
37.750358,-122.431641
37.50673,-122.387695
37.174817,-121.904297
37.157307,-122.321777
37.271492,-122.033386
37.435238,-122.217407
37.687794,-122.415161
37.542025,-122.299805
37.609506,-122.398682
37.544203,-122.0224
37.422151,-122.13501
37.395971,-122.080078
45.485651,-122.739258
47.719463,-122.255859
47.303913,-122.607422
45.176713,-122.167969
39.566,-104.985352
39.124201,-94.614258
35.454518,-97.426758
38.473482,-90.175781
45.021612,-93.251953
42.417881,-83.056641
41.371141,-81.782227
33.791132,-84.331055
30.252543,-90.439453
37.421248,-122.174835
37.47794,-122.181702
37.510628,-122.254486
37.56943,-122.346497
37.593373,-122.384949
37.620571,-122.489319
36.984249,-122.03064
36.553017,-121.893311
36.654442,-121.772461
36.482381,-121.876831
36.15042,-121.651611
36.274518,-121.838379
37.817717,-119.569702
39.31657,-120.140991
38.933041,-119.992676
39.13785,-119.778442
39.108019,-120.239868
38.586082,-121.503296
38.723354,-121.289062
37.878444,-119.437866
37.782994,-119.470825
37.973771,-119.685059
39.001377,-120.17395
40.709076,-73.948975
40.846346,-73.861084
40.780452,-73.959961
40.778829,-73.958931
40.78372,-73.966012
40.783688,-73.965325
40.783692,-73.965615
40.783675,-73.965741
40.783835,-73.965873
'''

import StringIO
import numpy as np
import re

import matplotlib.pyplot as plt

def lp(l):
    return map(lambda m: float(m.group()),re.finditer('[^, \n]+',l))

data=np.array(map(lp,StringIO.StringIO(sdata)))

xmn=np.min(data[:,0])
xmx=np.max(data[:,0])
ymn=np.min(data[:,1])
ymx=np.max(data[:,1])

# area of the point set bounding box
area=(xmx-xmn)*(ymx-ymn)

M_ITER=100 #maximum number of iterations
THRESH=1e-10 # stopping threshold

def em(x):
    print '\nSTART EM'
    mlst=[]

    mu0=np.mean( data , 0 ) # the sample mean of the data - use this as the mean of the low-precision gaussian

    # the mean of the high-precision Gaussian - this is what we are looking for
    mu=np.random.rand( 2 )*np.array([xmx-xmn,ymx-ymn])+np.array([xmn,ymn])

    lam_lo=.001  # precision of the low-precision Gaussian
    lam_hi=.1 # precision of the high-precision Gaussian
    prz=np.random.rand( 1 ) # probability of choosing the high-precision Gaussian mixture component

    for i in xrange(M_ITER):
        mlst.append(mu[:])

        l_hi=np.log(prz)+np.log(lam_hi)-.5*lam_hi*np.sum((x-mu)**2,1)
        #low-precision normal background distribution
        l_lo=np.log(1.0-prz)+np.log(lam_lo)-.5*lam_lo*np.sum((x-mu0)**2,1)
        #uncomment for the uniform background distribution
        #l_lo=np.log(1.0-prz)-np.log(area)

        #expectation step
        zs=1.0/(1.0+np.exp(l_lo-l_hi))

        #compute bound on the likelihood 
        lh=np.sum(zs*l_hi+(1.0-zs)*l_lo)
        print i,lh

        #maximization step
        mu=np.sum(zs[:,None]*x,0)/np.sum(zs) #mean
        lam_hi=np.sum(zs)/np.sum(zs*.5*np.sum((x-mu)**2,1)) #precision
        prz=1.0/(1.0+np.sum(1.0-zs)/np.sum(zs)) #mixure component probability

        try:
            if np.abs((lh-old_lh)/lh)<THRESH:
                break
        except: 
            pass

        old_lh=lh

        mlst.append(mu[:])

    return lh,lam_hi,mlst    

if __name__=='__main__':

    #repeat the EM algorithm a number of times and get the run with the best log likelihood
    mx_prm=em(data)
    for i in xrange(4):
        prm=em(data)

        if prm[0]>mx_prm[0]:
            mx_prm=prm

        print prm[0]
        print mx_prm[0]

    lh,lam_hi,mlst=mx_prm
    mu=mlst[-1]

    print 'best loglikelihood:', lh
    #print 'final precision value:', lam_hi
    print 'point of interest:', mu
    plt.plot(data[:,0],data[:,1],'.b')

    for m in mlst:
        plt.plot(m[0],m[1],'xr')

    plt.show()

答案 9 :(得分:1)

您可以通过简单的笔记轻松调整Tyler Durden引用的Rossmo公式:

公式:

这个公式给出了一个接近于捕食者或连环杀手的基本操作概率的东西。在你的情况下,它可以给出基数在某一点的概率。我稍后会解释如何使用它。你可以这样写:

Proba(基于A点)=总和{在所有点上}(Phi /(dist ^ f)+(1-Phi)(B *(gf))/(2B-dist)^ g)

使用欧几里德距离

你想要一个欧几里得距离,而不是曼哈顿距离,因为飞机或直升机不受公路/街道的限制。因此,如果您正在跟踪飞机和飞机,那么使用欧几里得距离是正确的方法。不是连环杀手。因此,公式中的“dist”是现场测试与所考虑的点之间的欧几里德距离

采取合理的变量B

变量B用于表示“合理聪明的杀手不会杀死他的邻居”的规则。在你的情况下,也将适用,因为没有人使用飞机/ roflcopter到达下一个街角。我们可以假设最小旅程例如是10公里或适用于您的情况时的任何合理。

指数因子f

因子f用于向距离添加权重。例如,如果所有点都位于一个小区域中,您可能需要一个很大的因子f,因为如果您的所有数据点都位于同一扇区中,机场/基数/总部的概率会快速下降。 g以类似的方式工作,允许选择“基地不太可能只是在现场旁边”的区域

因素Phi:

同样,必须根据您对问题的了解来确定此因素。它允许选择“基点接近点”和“我不会使用飞机制作5米”之间的最准确因子,例如,如果你认为第二个几乎无关紧要,你可以将Phi设置为0.95 {{ 1}}如果两者都有趣,phi将在0.5左右

如何将其实现为有用的东西:

首先,您要将地图划分为小方块:对地图进行网格划分(就像invisal一样)(方块越小,结果越准确(通常)),然后使用公式查找更可能的位置。事实上,网格只是一个包含所有可能位置的数组。 (如果你想要准确,你增加可能的点数,但它需要更多的计算时间,并且PhP因其惊人的速度而闻名)

算法:

(0<Phi<1)

希望它能帮到你

答案 10 :(得分:1)

首先,我想表达我对你说明和解释问题的方法的喜爱。

如果我在你的鞋子里,我会选择基于密度的算法,例如DBSCAN 然后聚集区域并去除噪点后将保留一些区域(选项)..然后我将采用密度最高点的聚类< strong>计算平均点和找到最接近的实际点。做完了,发现了这个地方! :)

此致

答案 11 :(得分:0)

为什么不这样:

  • 对于每个点,计算它与所有其他点的距离并将总和相加。
  • 总和最小的点是你的中心。

也许sum不是最好的衡量指标。可能是最“小距离”的点?

答案 12 :(得分:0)

总结距离。取最小距离的点。

function () {
    for i in points P:
        S[i] = 0
        for j in points P:
            S[i] += distance(P[i], P[j])
    return min(S);
}

答案 13 :(得分:0)

您可以采用最小生成树并删除最长边。较小的树为您提供了查找的中心。算法名称是单链路k聚类。这里有一篇帖子:https://stats.stackexchange.com/questions/1475/visualization-software-for-clustering