在坐标和非空间特征上聚类地理空间数据

9
假设我有一个数据框,存储在名为coordinates的变量中,前几行如下所示:
   business_lat  business_lng  business_rating
0   19.111841     72.910729           5.
1   19.111342     72.908387           5.
2   19.111342     72.908387           4.
3   19.137815     72.914085           5.
4   19.119677     72.905081           2.
5   19.119677     72.905081           2.
        .             .               .
        .             .               .
        .             .               .

如您所见,这些数据是地理空间数据(具有纬度和经度),每一行还有一个附加值business_rating,对应该行中latlng位置上的业务评分。我想要对数据进行聚类,将附近且评分相似的业务分配到同一群集中。本质上来说,我需要一个地理空间聚类,另外要求聚类必须考虑评分列。
我已经在下方运行了一个简单的DBSCAN,但是当我绘制出聚类的结果时,它似乎没有正确地实现我的需求。
from sklearn.cluster import DBSCAN
import numpy as np
db = DBSCAN(eps=2/6371., min_samples=5, algorithm='ball_tree', metric='haversine').fit(np.radians(coordinates))

我应该尝试调整DBSCAN的参数,对数据进行一些附加处理,还是完全采用其他方法?

2个回答

5

在聚类不同类型的信息(位置和评分)时,棘手的部分是确定它们之间的关系。当只有一个领域且比较的是相同单位时,这很简单。我的方法是先看一下如何将领域内的行相关联,然后确定一些领域之间的交互作用。可以使用像MinMaxScaler提到的缩放选项来完成这个过程,但我认为这有点过重,我们可以利用对领域的了解来更好地进行聚类。

处理位置信息

最好直接处理位置距离,因为这具有现实世界的意义,我们可以预计算距离。米的含义与我们所指的完全一样。

您可以使用上一个答案中提到的缩放选项,但这会使位置数据失真。例如,如果您有一组长而薄的位置数据,MinMaxScaling会给细长轴上的变化比长轴上的变化更多的重要性。如果要使用缩放,请在计算出的距离矩阵上进行,而不是在经纬度本身上进行。

import numpy as np
from sklearn.metrics.pairwise import haversine_distances


points_in_radians = df[['business_lat','business_lng']].apply(np.radians).values
distances_in_km = haversine_distances(points_in_radians) * 6371

添加评分

我们可以通过将评分与距离相关联来思考这个问题。我们可以问一些问题,如:在同一位置观察到的评分必须有多大差异才能区分?米数差异与评分差异比率是多少?有了比率的概念,我们可以计算出所有观测值的评分差异的另一个距离矩阵,并使用它来缩放或添加原始位置距离矩阵,或者我们可以增加每个评分间隙的距离。然后,我们可以对此位置和评分差异矩阵进行聚类。

from sklearn.metrics.pairwise import euclidean_distances

added_km_per_rating_gap = 1
rating_distances = euclidean_distances(df[['business_rating']].values) * added_km_per_rating_gap 

我们可以将它们相加并在生成的矩阵上进行聚类。
from sklearn.cluster import DBSCAN

distance_matrix = rating_distances + distances_in_km

clustering = DBSCAN(metric='precomputed', eps=1, min_samples=2)
clustering.fit(distance_matrix)

我们所做的是按地点进行聚类,并对评分差异添加惩罚。使这种惩罚直接而可控,可以优化以找到最佳聚类。

测试

我发现的问题是(至少对于我的测试数据),DBSCAN有一种倾向,即从观测到观测“漫步”,形成要么将评级混合在一起,因为惩罚不够高,要么分离成单个评级组的聚类。也许DBSCAN不适用于此类型的聚类。如果我有更多时间,我会寻找一些开放数据来测试,并尝试其他聚类方法。

以下是我用于测试的代码。我使用了评分距离的平方来强调较大的差距。

import random
from sklearn.datasets import make_blobs


X, y = make_blobs(n_samples=300, centers=6, cluster_std=0.60, random_state=0)
ratings = np.array([random.randint(1,4) for _ in range(len(X)//2)] \
          +[random.randint(2,5) for _ in range(len(X)//2)]).reshape(-1, 1)

distances_in_km = euclidean_distances(X)
rating_distances = euclidean_distances(ratings)


def build_clusters(multiplier, eps):
    rating_addition = (rating_distances ** 2) * multiplier
    distance_matrix = rating_addition + distances_in_km
    clustering = DBSCAN(metric='precomputed', eps=eps, min_samples=10)
    clustering.fit(distance_matrix)
    return clustering.labels_

0

使用DBSCAN方法,我们可以计算点之间的距离(欧几里得距离或其他距离),并寻找远离其他点的点。您可能需要考虑使用MinMaxScaler对值进行归一化,以便一个特征不会压倒其他特征。

你的代码在哪里?你最终的结果是什么?如果没有实际的代码示例,我只能猜测你在做什么。

我为您编写了一些示例代码。您可以在下面看到结果。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
import seaborn as sns; sns.set()
import csv

df = pd.read_csv('C:\\your_path_here\\business.csv')

X=df.loc[:,['review_count','latitude','longitude']]

K_clusters = range(1,10)
kmeans = [KMeans(n_clusters=i) for i in K_clusters]
Y_axis = df[['latitude']]
X_axis = df[['longitude']]
score = [kmeans[i].fit(Y_axis).score(Y_axis) for i in range(len(kmeans))]# Visualize

plt.plot(K_clusters, score)
plt.xlabel('Number of Clusters')
plt.ylabel('Score')
plt.title('Elbow Curve')
plt.show()

kmeans = KMeans(n_clusters = 3, init ='k-means++')
kmeans.fit(X[X.columns[0:2]]) # Compute k-means clustering.

X['cluster_label'] = kmeans.fit_predict(X[X.columns[0:2]])
centers = kmeans.cluster_centers_ # Coordinates of cluster centers.

labels = kmeans.predict(X[X.columns[0:2]]) # Labels of each point
X.head(10)

X.plot.scatter(x = 'latitude', y = 'longitude', c=labels, s=50, cmap='viridis')
plt.scatter(centers[:, 0], centers[:, 1], c='black', s=200, alpha=0.5)

from scipy.stats import zscore
df["zscore"] = zscore(df["review_count"])
df["outlier"] = df["zscore"].apply(lambda x: x <= -2.5 or x >= 2.5)
df[df["outlier"]]

df_cord = df[["latitude", "longitude"]]
df_cord.plot.scatter(x = "latitude", y = "latitude")

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
df_cord = scaler.fit_transform(df_cord)
df_cord = pd.DataFrame(df_cord, columns = ["latitude", "longitude"])
df_cord.plot.scatter(x = "latitude", y = "longitude")

from sklearn.cluster import DBSCAN
outlier_detection = DBSCAN(
  eps = 0.5,
  metric="euclidean",
  min_samples = 3,
  n_jobs = -1)
clusters = outlier_detection.fit_predict(df_cord)

clusters

from matplotlib import cm
cmap = cm.get_cmap('Accent')
df_cord.plot.scatter(
  x = "latitude",
  y = "longitude",
  c = clusters,
  cmap = cmap,
  colorbar = False
)

enter image description here

说实话,最终结果看起来有点奇怪。记住,不是所有东西都能被聚类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接