点のリスト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ユークリッド距離です。ある種の答えを与えるが、この質問への答えを与えない他の質問があります(多くは未回答):
これが始まりかもしれません。アルゴリズムが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のラムダ関数を選択した距離メトリックに置き換えることができます。私が言及したリポジトリで大円距離を見つけました。
これは正しいと思われ、データに応じて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つは最小のクラスターのセット。私の最初のパスは良いですが、それが最小のセットであることを証明していません。
結果(より大きなポイントのセット)は、次のように視覚化できます。
s2ライブラリを使用して20kmのゾーンを作成し、それぞれにどのポイントがあるかを確認してみませんか?