web-dev-qa-db-ja.com

データポイントのクラスターの中心を見つけるにはどうすればよいですか?

過去1年間、毎日ヘリコプターの位置をプロットして、次のマップを作成したとします。

Map

これを見ている人間なら誰でも、このヘリコプターはシカゴを拠点としていると私に言うことができるでしょう。

コードで同じ結果を見つけるにはどうすればよいですか?

私はこのようなものを探しています:

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

最終的に次のようなものを生成します:

Map-Home

更新:サンプルデータセット

サンプルデータセットを含むマップは次のとおりです。 http://batchgeo.com/map/c3676fe29985f00e1605cd4f86920179

150のジオコードのペーストビンは次のとおりです。 http://Pastebin.com/grVsbgL9

上記には150のジオコードが含まれています。最初の50は、シカゴに近いいくつかのクラスターにあります。残りは、ニューヨーク、ロサンゼルス、サンフランシスコのいくつかの小さなクラスターを含め、全国に散らばっています。

私はこのような(真剣に)約100万のデータセットを持っており、それらを繰り返し処理して、最も可能性の高い「家」を特定する必要があります。あなたの助けは大歓迎です。

更新2:飛行機がヘリコプターに切り替えられました

飛行機のコンセプトは、物理的な空港に向けてあまりにも多くの注目を集めていました。座標は、空港だけでなく、世界中のどこにでも配置できます。それが物理学、燃料、または他のものに縛られていないスーパーヘリコプターであると仮定しましょう。好きな場所に着陸できます。 ;)

32
Ryan

次の解決策は、緯度と経度をデカルト座標に変換することにより、ポイントが地球全体に散在している場合でも機能します。これは一種のKDE(カーネル密度推定)を実行しますが、最初のパスでは、カーネルの合計がデータポイントでのみ評価されます。問題に合うようにカーネルを選択する必要があります。以下のコードでは、冗談めかして/思い切ってトロシアンと呼ぶことができます。つまり、d≤hの場合は2-d²/h²、d> hの場合はh²/d²です(ここで、dはユークリッド距離、hは「帯域幅」です_$global_kernel_radius_)、ただしガウス分布(e-d²/2h²)、Epanechnikovカーネル(d <hの場合は1-d²/h²、それ以外の場合は0)、または別のカーネル。オプションの2番目のパスは、ローカルグリッド上の独立したカーネルを合計するか、どちらの場合も_$local_grid_radius_で定義された環境で重心を計算することにより、ローカルで検索を絞り込みます。

本質的に、各ポイントは、周囲にあるすべてのポイント(それ自体を含む)を合計し、(ベルカーブによって)近い場合はさらに重みを付けます。また、オプションの重み配列_$w_arr_によってそれらを重み付けします。勝者は最大額のポイントです。勝者が見つかったら、(別のベルカーブを使用して)勝者の周りで同じプロセスをローカルで繰り返すことで、探している「家」を見つけることができます。または、すべてのポイントの「重心」であると推定できます。勝者から指定された半径内。半径はゼロにすることができます。

適切なカーネルを選択し、ローカルで検索を絞り込む方法を選択し、パラメーターを調整することにより、アルゴリズムを問題に適合させる必要があります。サンプルデータセットの場合、最初のパスのTrossianカーネルと2番目のパスのEpanechnikovカーネルでは、3つの半径すべてが30 miに設定され、グリッドステップが1 miであることが適切な開始点になりますが、2つのサブシカゴのクラスターは、1つの大きなクラスターと見なす必要があります。それ以外の場合は、より小さい半径を選択する必要があります。

_function find_home($lat_arr, $lng_arr, $global_kernel_radius,
                                       $local_kernel_radius,
                                       $local_grid_radius, // 0 for no 2nd pass
                                       $local_grid_step,   // 0 for centroid
                                       $units='mi',
                                       $w_arr=null)
{
   // for lat,lng <-> x,y,z see http://en.wikipedia.org/wiki/Geodetic_datum
   // for K and h see http://en.wikipedia.org/wiki/Kernel_density_estimation

   switch (strtolower($units)) {
      /*  */case 'nm' :
      /*or*/case 'nmi': $m_divisor = 1852;
      break;case  'mi': $m_divisor = 1609.344;
      break;case  'km': $m_divisor = 1000;
      break;case   'm': $m_divisor = 1;
      break;default: return false;
   }
   $a  = 6378137 / $m_divisor; // Earth semi-major axis      (WGS84)
   $e2 = 6.69437999014E-3;     // First eccentricity squared (WGS84)

   $lat_lng_count = count($lat_arr);
   if ( !$w_arr) {
      $w_arr = array_fill(0, $lat_lng_count, 1.0);
   }
   $x_arr = array();
   $y_arr = array();
   $z_arr = array();
   $rad = M_PI / 180;
   $one_e2 = 1 - $e2;
   for ($i = 0; $i < $lat_lng_count; $i++) {
      $lat = $lat_arr[$i];
      $lng = $lng_arr[$i];
      $sin_lat = sin($lat * $rad);
      $sin_lng = sin($lng * $rad);
      $cos_lat = cos($lat * $rad);
      $cos_lng = cos($lng * $rad);
      // height = 0 (!)
      $N = $a / sqrt(1 - $e2 * $sin_lat * $sin_lat);
      $x_arr[$i] = $N * $cos_lat * $cos_lng;
      $y_arr[$i] = $N * $cos_lat * $sin_lng;
      $z_arr[$i] = $N * $one_e2  * $sin_lat;
   }
   $h = $global_kernel_radius;
   $h2 = $h * $h;
   $max_K_sum     = -1;
   $max_K_sum_idx = -1;
   for ($i = 0; $i < $lat_lng_count; $i++) {
      $xi = $x_arr[$i];
      $yi = $y_arr[$i];
      $zi = $z_arr[$i];
      $K_sum  = 0;
      for ($j = 0; $j < $lat_lng_count; $j++) {
         $dx = $xi - $x_arr[$j];
         $dy = $yi - $y_arr[$j];
         $dz = $zi - $z_arr[$j];
         $d2 = $dx * $dx + $dy * $dy + $dz * $dz;
         $K_sum += $w_arr[$j] * ($d2 <= $h2 ? (2 - $d2 / $h2) : $h2 / $d2); // Trossian ;-)
         // $K_sum += $w_arr[$j] * exp(-0.5 * $d2 / $h2); // Gaussian
      }
      if ($max_K_sum < $K_sum) {
          $max_K_sum = $K_sum;
          $max_K_sum_i = $i;
      }
   }
   $winner_x   = $x_arr  [$max_K_sum_i];
   $winner_y   = $y_arr  [$max_K_sum_i];
   $winner_z   = $z_arr  [$max_K_sum_i];
   $winner_lat = $lat_arr[$max_K_sum_i];
   $winner_lng = $lng_arr[$max_K_sum_i];

   $sin_winner_lat = sin($winner_lat * $rad);
   $cos_winner_lat = cos($winner_lat * $rad);
   $sin_winner_lng = sin($winner_lng * $rad);
   $cos_winner_lng = cos($winner_lng * $rad);
   $east_x  = -$local_grid_step * $sin_winner_lng;
   $east_y  =  $local_grid_step * $cos_winner_lng;
   $east_z  =  0;
   $north_x = -$local_grid_step * $sin_winner_lat * $cos_winner_lng;
   $north_y = -$local_grid_step * $sin_winner_lat * $sin_winner_lng;
   $north_z =  $local_grid_step * $cos_winner_lat;

   if ($local_grid_radius > 0 && $local_grid_step > 0) {
      $r = intval($local_grid_radius / $local_grid_step);
      $r2 = $r * $r;
      $h = $local_kernel_radius;
      $h2 = $h * $h;
      $max_L_sum     = -1;
      $max_L_sum_idx = -1;
      for ($i = -$r; $i <= $r; $i++) {
         $winner_east_x = $winner_x + $i * $east_x;
         $winner_east_y = $winner_y + $i * $east_y;
         $winner_east_z = $winner_z + $i * $east_z;
         $j_max = intval(sqrt($r2 - $i * $i));
         for ($j = -$j_max; $j <= $j_max; $j++) {
            $x = $winner_east_x + $j * $north_x;
            $y = $winner_east_y + $j * $north_y;
            $z = $winner_east_z + $j * $north_z;
            $L_sum  = 0;
            for ($k = 0; $k < $lat_lng_count; $k++) {
               $dx = $x - $x_arr[$k];
               $dy = $y - $y_arr[$k];
               $dz = $z - $z_arr[$k];
               $d2 = $dx * $dx + $dy * $dy + $dz * $dz;
               if ($d2 < $h2) {
                  $L_sum += $w_arr[$k] * ($h2 - $d2); // Epanechnikov
               }
            }
            if ($max_L_sum < $L_sum) {
                $max_L_sum = $L_sum;
                $max_L_sum_i = $i;
                $max_L_sum_j = $j;
            }
         }
      }
      $x = $winner_x + $max_L_sum_i * $east_x + $max_L_sum_j * $north_x;
      $y = $winner_y + $max_L_sum_i * $east_y + $max_L_sum_j * $north_y;
      $z = $winner_z + $max_L_sum_i * $east_z + $max_L_sum_j * $north_z;

   } else if ($local_grid_radius > 0) {
      $r = $local_grid_radius;
      $r2 = $r * $r;
      $wx_sum = 0;
      $wy_sum = 0;
      $wz_sum = 0;
      $w_sum  = 0;
      for ($k = 0; $k < $lat_lng_count; $k++) {
         $xk = $x_arr[$k];
         $yk = $y_arr[$k];
         $zk = $z_arr[$k];
         $dx = $winner_x - $xk;
         $dy = $winner_y - $yk;
         $dz = $winner_z - $zk;
         $d2 = $dx * $dx + $dy * $dy + $dz * $dz;
         if ($d2 <= $r2) {
            $wk = $w_arr[$k];
            $wx_sum += $wk * $xk;
            $wy_sum += $wk * $yk;
            $wz_sum += $wk * $zk;
            $w_sum  += $wk;
         }
      }
      $x = $wx_sum / $w_sum;
      $y = $wy_sum / $w_sum;
      $z = $wz_sum / $w_sum;
      $max_L_sum_i = false;
      $max_L_sum_j = false;

   } else {
      return array($winner_lat, $winner_lng, $max_K_sum_i, false, false);
   }

   $deg = 180 / M_PI;
   $a2 = $a * $a;
   $e4 = $e2 * $e2;
   $p = sqrt($x * $x + $y * $y);
   $zeta = (1 - $e2) * $z * $z / $a2;
   $rho  = ($p * $p / $a2 + $zeta - $e4) / 6;
   $rho3 = $rho * $rho * $rho;
   $s = $e4 * $zeta * $p * $p / (4 * $a2);
   $t = pow($s + $rho3 + sqrt($s * ($s + 2 * $rho3)), 1 / 3);
   $u = $rho + $t + $rho * $rho / $t;
   $v = sqrt($u * $u + $e4 * $zeta);
   $w = $e2 * ($u + $v - $zeta) / (2 * $v);
   $k = 1 + $e2 * (sqrt($u + $v + $w * $w) + $w) / ($u + $v);
   $lat = atan($k * $z / $p) * $deg;
   $lng = atan2($y, $x) * $deg;

   return array($lat, $lng, $max_K_sum_i, $max_L_sum_i, $max_L_sum_j);
}
_

距離がユークリッドであり、大円ではないという事実は、目前のタスクにほとんど影響を与えないはずです。大円距離の計算ははるかに面倒であり、非常に遠いポイントの重みのみが大幅に低くなりますが、これらのポイントの重みはすでに非常に低くなっています。原則として、同じ効果は別のカーネルでも実現できます。 Epanechnikovカーネルのように、ある程度の距離を超えて完全にカットオフされているカーネルでは、この問題はまったく発生しません(実際には)。

WGS84データムのlat、lngとx、y、zの間の変換は、真の必要性のためというよりも、参照として正確に(数値的安定性の保証はありませんが)与えられています。高さを考慮に入れる場合、またはより高速な逆変換が必要な場合は、 ウィキペディアの記事 を参照してください。

Epanechnikovカーネルは、ガウスカーネルやトロシアンカーネルよりも「ローカル」であることに加えて、2番目のループであるO(ng)で最速であるという利点があります。ここで、gはローカルグリッドのポイント数であり、次のことができます。 nが大きい場合は、最初のループであるO(n²)でも使用されます。

15
Walter Tross

これは、危険な表面を見つけることで解決できます。 ロスモの公式 を参照してください。

これは捕食者の問題です。地理的に配置された死骸のセットを考えると、捕食者の隠れ家はどこにありますか?ロスモの公式はこの問題を解決します。

10
Tyler Durden

最大の密度推定値を持つ点を見つけます。

かなり簡単なはずです。直径の大きな空港を大まかにカバーするカーネル半径を使用します。 2DガウスカーネルまたはEpanechnikovカーネルで問題ありません。

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

これは、ヒープマップの計算に似ています: http://en.wikipedia.org/wiki/Heat_map そして、そこで最も明るいスポットを見つけます。それがすぐに明るさを計算することを除いて。

楽しみのために、DBpedia(つまりウィキペディア)の地理座標の1%サンプルを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]])

シカゴ空港から10マイル未満の[1] 42.14697 -88.09508を生成します。

より良い座標を取得するには、次のことを試してください。

  • 推定座標の周りの20x20マイルのエリアで再実行
  • その領域のビン化されていないKDE
  • dpikによる帯域幅の選択の改善
  • より高いグリッド解像度
7
Anony-Mousse

天体物理学では、いわゆる「半質量半径」を使用します。分布とその中心が与えられると、半分の質量半径は、分布の点の半分を含む円の最小半径です。

この量は、ポイントの分布の特徴的な長さです。

ヘリコプターのホームがポイントが最大に集中する場所であることが必要な場合は、最小の半質量半径を持つポイントになります。

私のアルゴリズムは次のとおりです。各ポイントについて、現在のポイントの分布を中心にこの半質量半径を計算します。ヘリコプターの「ホーム」は、最小の半質量半径を持つポイントになります。

私はそれを実装し、計算された中心は42.149994 -88.133698(シカゴにあります)です。また、天体物理学で通常使用される0.5(半分)の代わりに、総質量の0.2を使用しました。

これは、ヘリコプターのホームを見つける私の(Pythonでの)アルゴリズムです。

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]
5

そのタスクには [〜#〜] dbscan [〜#〜] を使用できます。

DBSCANは、ノイズの概念を備えた密度ベースのクラスタリングです。 2つのパラメータが必要です。

まず、クラスターが持つ必要のあるポイントの数は、少なくとも"minpoints"です。次に、クラスターに含める必要がある周囲のポイントまでの距離のしきい値を設定する"epsilon"と呼ばれる近隣パラメーター。

アルゴリズム全体は次のように機能します。

  1. セット内でまだアクセスされていない任意のポイントから開始します。
  2. イプシロン近隣からポイントを取得し、すべて訪問済みとしてマークします
    1. この近傍で十分なポイントが見つかった場合(> minpointsパラメーター)、新しいクラスターを開始し、それらのポイントを割り当てます。ここで、このクラスター内のすべてのポイントについて、ステップ2に再度戻ります。
    2. 持っていない場合は、この点をノイズとして宣言してください
  3. すべてのポイントを訪問するまで、もう一度やり直してください

実装は本当に簡単で、このアルゴリズムをすでにサポートしているフレームワークはたくさんあります。クラスターの平均を見つけるには、近隣から割り当てられたすべてのポイントの平均を取得するだけです。

ただし、@ TylerDurdenが提案する方法とは異なり、これにはパラメーター化が必要です。したがって、問題に適合するいくつかの手動調整パラメーターを見つける必要があります。

あなたの場合、飛行機が空港で追跡する時間の10%に留まる可能性が高い場合は、最小点を合計点の10%に設定してみることができます。密度パラメーターイプシロンは、地理センサーの解像度と使用する距離メトリックによって異なります。地理データの場合は haversine distance をお勧めします。

4
Thomas Jungblut

このマシンにあるのは古いコンパイラだけなので、これのASCIIバージョンを作成しました。これは(ASCIIで)マップを「描画」します-ドットはポイントであり、Xは実際のソースがある場所です、Gは、推測されたソースがある場所です。2つが重なる場合は、Xのみが表示されます。

例(それぞれ難易度1.5および3): enter image description here

ポイントは、ランダムなポイントをソースとして選択し、ランダムにポイントを分散することによって生成されます。これにより、ポイントがソースに近くなる可能性が高くなります。

DIFFICULTYは、初期のポイント生成(ポイントがソースに近づく可能性がどれだけ高いか)を調整する浮動小数点定数です。1以下の場合、プログラムは正確なソースを推測できるはずです。または非常に近い。 2.5でも、それでもかなりまともなはずです。 4+になると、推測が悪くなり始めますが、それでも人間よりも推測がうまくいくと思います。

X、次にYの二分探索を使用して最適化できます。これにより、推測が悪化しますが、はるかに高速になります。または、より大きなブロックから始めて、最良のブロックをさらに分割します(または最良のブロックとその周囲の8つ)。より高解像度のシステムの場合、これらの1つが必要になります。これは非常に単純なアプローチですが、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);

} 
3
svinja

enter image description here

マップを多くのゾーンに分割してから、最も平面のあるゾーンで平面の中心を見つけてはどうでしょうか。アルゴリズムは次のようになります

セットする ゾーン[40] 
foreach 平面内の平面
ゾーン[GetZone(Plane.position)]。Add(Plane)
 
セットする MaxZone = Zones [0] 
foreach ゾーン内のゾーン
 もし MaxZone.Length()<Zone.Length()
 MaxZone = Zone 
 
セットする センター
foreach MaxZoneの平面
 Center.X + = Plane.X 
 Center.Y + = Plane.Y 
 Center.X/= MaxZone.Length 
 Center.Y/= MaxZone.Length 
3
invisal

単純な混合モデルは、この問題に対してかなりうまく機能するようです。

一般に、データセット内の他のすべてのポイントまでの距離を最小化するポイントを取得するには、平均を取ることができます。この場合、集中点のサブセットからの距離を最小化する点を見つけたいと思います。ポイントが関心のあるポイントの集中セットまたは背景ポイントの拡散セットのいずれかから来る可能性があると仮定すると、これにより混合モデルが得られます。

以下にいくつかの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()
1
user1149913

まず、問題の説明と説明において、あなたの方法が好きだと表現したいと思います。

もし私があなたの立場にあったら、密度ベースのアルゴリズムのように [〜#〜] dbscan [〜#〜] そしてそれから領域をクラスター化してノイズポイントを削除した後いくつかの領域(選択肢)が残ります..次に、最も密度の高いクラスターを取得ポイントと平均点を計算するおよび最も近い実点を見つけるそれに。完了、場所を見つけました! :)。

よろしく、

1
CME64

Tyler Durdenが引用したRossmoの公式を、いくつかの簡単なメモで簡単にケースに適合させることができます。

式:

この式は、捕食者または連続殺人犯の基本操作の存在の確率に近い何かを与えます。あなたの場合、それはベースが特定のポイントにある確率を与えることができます。使い方は後で説明します。 Uはそれをこのように書くことができます:

Proba(ポイントAに基づく)= Sum {すべてのスポットで}(Phi /(dist ^ f)+(1-Phi)(B *(g-f))/(2B-dist)^ g)

ユークリッド距離を使用

飛行機やヘリコプターは道路や通りに縛られていないため、マンハッタンの距離ではなくユークリッド距離が必要です。したがって、連続殺人犯ではなく飛行機を追跡している場合は、ユークリッド距離を使用するのが正しい方法です。したがって、式の「距離」は、テスト中のスポットと考慮されるスポットの間のユークリッド距離です。

妥当な変数Bを取る

変数Bは、「合理的に賢い殺人者は隣人を殺さない」というルールを表すために使用されました。あなたの場合、次の街角に行くのに飛行機/ロフルコプターを使う人はいないので、も適用されます。最小の移動距離は、たとえば10 kmか、ケースに適用した場合に妥当なものであると想定できます。

指数因子f

係数fは、距離に重みを追加するために使用されます。たとえば、すべてのスポットが小さなエリアにある場合、すべてのデータポイントが同じセクターにあると空港/基地/本社の確率が急速に低下するため、大きな係数fが必要になる可能性があります。 gも同様に機能し、「ベースがスポットのすぐ隣にある可能性は低い」領域のサイズを選択できます

ファクターファイ:

この場合も、この要因は、問題に関する知識を使用して決定する必要があります。たとえば、2番目の要素がほとんど無関係だと思われる場合は、「ベースがスポットに近い」と「飛行機を使用して5 mを作成しない」の間で、最も正確な要素を選択できます。Phiを0.95に設定できます(0<Phi<1)両方が興味深い場合、ファイは約0.5になります

何か便利なものとして実装する方法:

まず、マップを小さな正方形に分割します。マップをメッシュ化し(invisalと同じように)(正方形が小さいほど、結果はより正確になります(一般的に))、式を使用してより可能性の高い場所を見つけます。実際、メッシュはすべての可能な場所を含む単なる配列です。 (正確にしたい場合は、可能なスポットの数を増やしますが、より多くの計算時間が必要になり、PhPはその驚くべき速度でよく知られていません)

アルゴリズム:

//define all the factors you need(B , f , g , phi)

for(i=0..mesh_size) // computing the probability of presence for each square of the mesh
{
  P(i)=0;
  geocode squarePosition;//GeoCode of the square's center 
  for(j=0..geocodearray_size)//sum on all the known spots
  {
     dist=Distance(geocodearray[j],squarePosition);//small function returning distance between two geocodes

         P(i)+=(Phi/pow(dist,f))+(1-Phi)*pow(B,g-f)/pow(2B-dist,g);
  }
 }

return geocode corresponding to max(P(i))

それがあなたを助けることを願っています

1
Azhrilla

仮想地球には、それを比較的迅速に行う方法についての非常に優れた説明があります。また、コード例も提供しています。ご覧ください http://soulsolutions.com.au/Articles/ClusteringVirtualEarthPart1.aspx

1
Mukul Joshi

なぜこのようなものではないのですか?

  • 各ポイントについて、他のすべてのポイントからの距離を計算し、合計します。
  • 合計が最小のポイントがあなたの中心です。

たぶん、合計は使用するのに最適なメトリックではありません。おそらく、最も「距離が短い」ポイントですか?

0
Drew Khoury

距離を合計します。合計距離が最小のポイントを取ります。

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

最小スパニングツリーを取得して、最も長いエッジを削除できます。小さい木は、ルックアップする重心を提供します。アルゴリズム名はシングルリンクkクラスタリングです。ここに投稿があります: https://stats.stackexchange.com/questions/1475/visualization-software-for-clustering

0
Gigamegs