DBSCAN для кластеризации данных географического местоположения
у меня есть фрейм данных с парами широты и долготы.
вот мой фрейм данных выглядит так.
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. Он объединяет точки, которые находятся слишком далеко, в одном группа.(приблизительно 2 км) это потому, что он вычисляет евклидово расстояние при кластеризации? пожалуйста помочь.
3 ответов
DBSCAN это означает быть использованным на сырцовых данных, с пространственным индексом для ускорения. Единственный инструмент, который я знаю с ускорением для гео расстояния елки (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 класс.группа.DBSCAN (eps=0.5, min_samples=5,metric= 'euclidean', algorithm= 'auto', leaf_size=30, p=нет, random_state=нет)
метрика : строку, или callable
метрика, используемая при расчете расстояния между экземплярами в массиве объектов. Если метрика является Строковой или вызываемой, она должна быть одним из параметров, разрешенных метриками.попарно.calculate_distance для своего параметра метрики. если метрика "предварительно вычислена", 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")
вы можете кластеризировать пространственные данные широты и долготы с помощью DBSCAN scikit-learn без предварительного вычисления матрицы расстояний.
db = DBSCAN(eps=2/6371., min_samples=5, algorithm='ball_tree', metric='haversine').fit(np.radians(coordinates))
это происходит из этого урока на кластеризация пространственных данных с помощью scikit-learn DBSCAN. В частности, обратите внимание, что eps
значение все еще 2 км, но оно делится на 6371, чтобы преобразовать его в радианы. Кроме того, обратите внимание, что .fit()
координаты в радианах единиц для гаверсинус метрики.
Я не знаю, какая реализация haversine
вы используете, но похоже, что он возвращает результаты в km so eps
должно быть 0,2, не 2 для 200 м.
на min_samples
параметр, который зависит от того, что ваш ожидаемый результат. Вот несколько примеров. Мои результаты используют реализацию haversine
на основе ответ что дает матрицу расстояний, похожую, но не идентичную вашей.
это с db = DBSCAN(eps=0.2, min_samples=5)
[ 0 -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 2 0 -1 1 2 2 0 0 0 -1 -1 -1 1 1 1 -1 -1 1 -1 -1 1]
это создает три кластера, 0, 1
и 2
, и многие образцы не попадают в кластер с по крайней мере 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]
здесь большинство образцов находятся в пределах 200 м от по крайней мере одного другого образца и поэтому попадают в один из восьми кластеров 0
to 7
.
редактировать, чтобы добавить
похоже, @ 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()