web-dev-qa-db-ja.com

地理的ポイントのリストを距離別にクラスター化するにはどうすればよいですか?

点のリストP = [p1、... pN]があります。ここで、pi =(latitudeI、longitudeI)です。

Python 3)を使用して、クラスターのすべてのメンバーがクラスター内の他のすべてのメンバーから20 km以内にあるように、クラスターの最小セット(Pの互いに素なサブセット)を見つけたいと思います。

2点間の距離は、 Vincenty法 を使用して計算されます。

これをもう少し具体的にするために、次のような一連のポイントがあるとします。

from numpy import *
points = array([[33.    , 41.    ],
       [33.9693, 41.3923],
       [33.6074, 41.277 ],
       [34.4823, 41.919 ],
       [34.3702, 41.1424],
       [34.3931, 41.078 ],
       [34.2377, 41.0576],
       [34.2395, 41.0211],
       [34.4443, 41.3499],
       [34.3812, 40.9793]])

次に、この関数を定義しようとしています。

from geopy.distance import vincenty
def clusters(points, distance):
    """Returns smallest list of clusters [C1,C2...Cn] such that
       for x,y in Ci, vincenty(x,y).km <= distance """
    return [points]  # Incorrect but gives the form of the output

注:多くの質問は、地理的位置と属性に集中しています。私の質問は場所のみです。これは緯度/経度用で、notユークリッド距離です。ある種の答えを与えるが、この質問への答えを与えない他の質問があります(多くは未回答):

5
Lars Ericson

これが始まりかもしれません。アルゴリズムがkを試みるということは、kを2からポイントの数まで反復することによってポイントをクラスター化することを意味します。最小の番号を選択する必要があります。

ポイントをクラスタリングし、各クラスターが制約に従っていることを確認することで機能します。いずれかのクラスターが準拠していない場合、ソリューションにはFalseというラベルが付けられ、次の数のクラスターに進みます。

Sklearnで使用されるK-meansアルゴリズムは極小値に分類されるため、これが探しているソリューションであるかどうかを証明するのはbestまだ確立されていませんが、1つである可能性があります

import numpy as np
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
import math

points = np.array([[33.    , 41.    ],
       [33.9693, 41.3923],
       [33.6074, 41.277 ],
       [34.4823, 41.919 ],
       [34.3702, 41.1424],
       [34.3931, 41.078 ],
       [34.2377, 41.0576],
       [34.2395, 41.0211],
       [34.4443, 41.3499],
       [34.3812, 40.9793]])


def distance(Origin, destination): #found here https://Gist.github.com/rochacbruno/2883505
    lat1, lon1 = Origin[0],Origin[1]
    lat2, lon2 = destination[0],destination[1]
    radius = 6371 # km
    dlat = math.radians(lat2-lat1)
    dlon = math.radians(lon2-lon1)
    a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
        * math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    d = radius * c

    return d

def create_clusters(number_of_clusters,points):
    kmeans = KMeans(n_clusters=number_of_clusters, random_state=0).fit(points)
    l_array = np.array([[label] for label in kmeans.labels_])
    clusters = np.append(points,l_array,axis=1)
    return clusters

def validate_solution(max_dist,clusters):
    _, __, n_clust = clusters.max(axis=0)
    n_clust = int(n_clust)
    for i in range(n_clust):
        two_d_cluster=clusters[clusters[:,2] == i][:,np.array([True, True, False])]
        if not validate_cluster(max_dist,two_d_cluster):
            return False
        else:
            continue
    return True

def validate_cluster(max_dist,cluster):
    distances = cdist(cluster,cluster, lambda ori,des: int(round(distance(ori,des))))
    print(distances)
    print(30*'-')
    for item in distances.flatten():
        if item > max_dist:
            return False
    return True

if __name__ == '__main__':
    for i in range(2,len(points)):
        print(i)
        print(validate_solution(20,create_clusters(i,points)))

ベンチマークが確立されると、距離の制約に違反することなくそのポイントを他のクラスターに分散できるかどうかを確立するために、各クラスターにさらに1つ焦点を合わせる必要があります。

Cdistのラムダ関数を選択した距離メトリックに置き換えることができます。私が言及したリポジトリで大円距離を見つけました。

1
vencaslac

これは正しいと思われ、データに応じてO(N ^ 2)の最悪の場合、より適切に動作するソリューションです。

def my_cluster(S,distance):
    coords=set(S)
    C=[]
    while len(coords):
        locus=coords.pop()
        cluster = [x for x in coords if vincenty(locus,x).km <= distance]
        C.append(cluster+[locus])
        for x in cluster:
            coords.remove(x)
    return C

[〜#〜]注[〜#〜]:私の要件の1つは最小のクラスターのセット。私の最初のパスは良いですが、それが最小のセットであることを証明していません。

結果(より大きなポイントのセット)は、次のように視覚化できます。

Clustering of military activities in Iraq

2
Lars Ericson

s2ライブラリを使用して20kmのゾーンを作成し、それぞれにどのポイントがあるかを確認してみませんか?

0
APD