緯度と経度のペアを持つデータフレームがあります。
これは私のデータフレームのようなものです。
order_lat order_long
0 19.111841 72.910729
1 19.111342 72.908387
2 19.111342 72.908387
3 19.137815 72.914085
4 19.119677 72.905081
5 19.119677 72.905081
6 19.119677 72.905081
7 19.120217 72.907121
8 19.120217 72.907121
9 19.119677 72.905081
10 19.119677 72.905081
11 19.119677 72.905081
12 19.111860 72.911346
13 19.111860 72.911346
14 19.119677 72.905081
15 19.119677 72.905081
16 19.119677 72.905081
17 19.137815 72.914085
18 19.115380 72.909144
19 19.115380 72.909144
20 19.116168 72.909573
21 19.119677 72.905081
22 19.137815 72.914085
23 19.137815 72.914085
24 19.112955 72.910102
25 19.112955 72.910102
26 19.112955 72.910102
27 19.119677 72.905081
28 19.119677 72.905081
29 19.115380 72.909144
30 19.119677 72.905081
31 19.119677 72.905081
32 19.119677 72.905081
33 19.119677 72.905081
34 19.119677 72.905081
35 19.111860 72.911346
36 19.111841 72.910729
37 19.131674 72.918510
38 19.119677 72.905081
39 19.111860 72.911346
40 19.111860 72.911346
41 19.111841 72.910729
42 19.111841 72.910729
43 19.111841 72.910729
44 19.115380 72.909144
45 19.116625 72.909185
46 19.115671 72.908985
47 19.119677 72.905081
48 19.119677 72.905081
49 19.119677 72.905081
50 19.116183 72.909646
51 19.113827 72.893833
52 19.119677 72.905081
53 19.114100 72.894985
54 19.107491 72.901760
55 19.119677 72.905081
私の距離行列に従って、互いに最も近い(200メートルの距離)このポイントをクラスター化します。
from scipy.spatial.distance import pdist, squareform
distance_matrix = squareform(pdist(X, (lambda u,v: haversine(u,v))))
array([[ 0. , 0.2522482 , 0.2522482 , ..., 1.67313071,
1.05925366, 1.05420922],
[ 0.2522482 , 0. , 0. , ..., 1.44111548,
0.81742536, 0.98978355],
[ 0.2522482 , 0. , 0. , ..., 1.44111548,
0.81742536, 0.98978355],
...,
[ 1.67313071, 1.44111548, 1.44111548, ..., 0. ,
1.02310118, 1.22871515],
[ 1.05925366, 0.81742536, 0.81742536, ..., 1.02310118,
0. , 1.39923529],
[ 1.05420922, 0.98978355, 0.98978355, ..., 1.22871515,
1.39923529, 0. ]])
次に、距離行列にDBSCANクラスタリングアルゴリズムを適用しています。
from sklearn.cluster import DBSCAN
db = DBSCAN(eps=2,min_samples=5)
y_db = db.fit_predict(distance_matrix)
Epsとmin_samplesの値を選択する方法がわかりません。それは、1つのクラスター内で遠すぎる点をクラスター化します。(距離約2 km)クラスター化中にユークリッド距離を計算するためですか?助けてください。
DBSCANはmeantであり、生データで使用され、加速用の空間インデックスが使用されます。ジオ距離の加速で私が知っている唯一のツールは [〜#〜] elki [〜#〜] (Java)-scikit-learnは残念ながらユークリッド距離のようないくつかの距離でのみこれをサポートします(sklearn.neighbors.NearestNeighbors
)。しかし、明らかに、ペアワイズ距離を事前計算する努力をすることができるので、これは(まだ)問題ではありません。
ただし、ドキュメントを十分に注意深く読んでいなかったため、DBSCANが距離行列を使用するという仮定は間違っています:
from sklearn.cluster import DBSCAN
db = DBSCAN(eps=2,min_samples=5)
db.fit_predict(distance_matrix)
距離行列行のユークリッド距離を使用しますが、これは明らかに意味をなしません。
DBSCAN
のドキュメントを参照してください(強調を追加):
クラスsklearn.cluster.DBSCAN(eps = 0.5、min_samples = 5、metric = 'euclidean'、algorithm = 'auto'、leaf_size = 30、p =なし、random_state =なし)
metric:文字列、または呼び出し可能
フィーチャ配列内のインスタンス間の距離を計算するときに使用するメトリック。 metricが文字列または呼び出し可能の場合、metricパラメーターのmetrics.pairwise.calculate_distanceで許可されるオプションの1つでなければなりません。 メトリックが「事前計算」される場合、Xは距離行列であると想定され、正方でなければなりません。Xはスパース行列である場合があり、その場合は非ゼロ」要素は、DBSCANの近傍と見なされる場合があります。
fit_predict
でも同様:
[〜#〜] x [〜#〜]:形状の配列またはスパース(CSR)行列(n_samples、n_features)、または形状の配列( n_samples、n_samples)
特徴配列、またはサンプル間の距離の配列if metric = 'precomputed'。
言い換えれば、あなたはする必要があります
db = DBSCAN(eps=2, min_samples=5, metric="precomputed")
距離行列を事前計算することなく、scikit-learnのDBSCANを使用して空間緯度経度データをクラスタリングできます。
_db = DBSCAN(eps=2/6371., min_samples=5, algorithm='ball_tree', metric='haversine').fit(np.radians(coordinates))
_
これは、scikit-learn DBSCANを使用した空間データのクラスター化に関するこのチュートリアルからのものです。特に、eps
値はまだ2kmですが、ラジアンに変換するために6371で除算されています。また、.fit()
は、haversineメトリックの座標をラジアン単位で取得することに注意してください。
使用しているhaversine
の実装がわかりませんが、kmで結果を返すように見えるので、eps
は200 mの場合2ではなく0.2になります。
min_samples
パラメーターの場合、それは予想される出力によって異なります。以下に例をいくつか示します。私の出力は、 この答え に基づくhaversine
の実装を使用しています。これにより、距離行列は似ていますが、同じではありません。
これはdb = DBSCAN(eps=0.2, min_samples=5)
で
[0 -1 -1 -1 1 1 1 1 -1 -1 1 1 1 2 2 1 1 1 1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 1 1 -1 1 1 1 1 1 1 2 0 -1 1 2 2 0 0 0 -1 -1 -1 1 1 1 -1 -1 1 -1 -1 1]
これにより、0, 1
と2
の3つのクラスターが作成され、多くのサンプルは少なくとも5つのメンバーを持つクラスターに分類されないため、クラスターに割り当てられません(-1
として表示)。
より小さなmin_samples
値で再試行します。
db = DBSCAN(eps=0.2, min_samples=2)
[0 1 1 2 3 3 3 4 4 3 3 3 5 5 3 3 3 2 6 6 7 3 2 2 8 8 8 3 3 6 3 3 3 3 3 5 0 -1 3 5 5 0 0 0 6 -1- 1 3 3 3 7 -1 3 -1 -1 3]
ここで、ほとんどのサンプルは少なくとも1つの他のサンプルから200m以内にあるため、0
から7
の8つのクラスターのいずれかに分類されます。
編集して追加
@ Anony-Mousseは正しいように見えますが、結果には何も問題はありませんでした。何かを提供するために、クラスターを表示するために使用していたコードを以下に示します。
from math import radians, cos, sin, asin, sqrt
from scipy.spatial.distance import pdist, squareform
from sklearn.cluster import DBSCAN
import matplotlib.pyplot as plt
import pandas as pd
def haversine(lonlat1, lonlat2):
"""
Calculate the great circle distance between two points
on the earth (specified in decimal degrees)
"""
# convert decimal degrees to radians
lat1, lon1 = lonlat1
lat2, lon2 = lonlat2
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
# haversine formula
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
c = 2 * asin(sqrt(a))
r = 6371 # Radius of earth in kilometers. Use 3956 for miles
return c * r
X = pd.read_csv('dbscan_test.csv')
distance_matrix = squareform(pdist(X, (lambda u,v: haversine(u,v))))
db = DBSCAN(eps=0.2, min_samples=2, metric='precomputed') # using "precomputed" as recommended by @Anony-Mousse
y_db = db.fit_predict(distance_matrix)
X['cluster'] = y_db
plt.scatter(X['lat'], X['lng'], c=X['cluster'])
plt.show()