基于坐标和非空间特征的聚类地理空间数据
Clustering geospatial data on coordinates AND non spatial feature
假设我将以下数据框存储为一个名为坐标的变量,其中前几行如下所示:
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 的企业评级。我想对数据进行聚类,将附近且具有相似评级的企业分配到同一个聚类中。本质上我需要一个地理空间集群,附加要求是集群必须考虑评级列。
我在网上看过,但真的找不到太多解决方法:只有严格的地理空间聚类(只有要聚类的特征是 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 的参数、对数据进行一些额外处理或一起使用不同的方法是否会更好?
使用 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
)
说实话,最后的结果看起来有点奇怪。请记住,并非所有内容都是可聚类的。
关于将两种不同类型的信息(位置和评级)聚类的棘手部分是确定它们应该如何相互关联。当它只是一个域并且您正在比较相同的单位时,询问很简单。我的方法是查看如何关联域中的行,然后确定域之间的一些交互。这可以使用像 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 倾向于 'walk' 从观察到观察形成集群,这些集群要么将评级混合在一起,因为惩罚不够高,或者分为单个评级组。可能是 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_
假设我将以下数据框存储为一个名为坐标的变量,其中前几行如下所示:
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 的企业评级。我想对数据进行聚类,将附近且具有相似评级的企业分配到同一个聚类中。本质上我需要一个地理空间集群,附加要求是集群必须考虑评级列。
我在网上看过,但真的找不到太多解决方法:只有严格的地理空间聚类(只有要聚类的特征是 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 的参数、对数据进行一些额外处理或一起使用不同的方法是否会更好?
使用 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
)
说实话,最后的结果看起来有点奇怪。请记住,并非所有内容都是可聚类的。
关于将两种不同类型的信息(位置和评级)聚类的棘手部分是确定它们应该如何相互关联。当它只是一个域并且您正在比较相同的单位时,询问很简单。我的方法是查看如何关联域中的行,然后确定域之间的一些交互。这可以使用像 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 倾向于 'walk' 从观察到观察形成集群,这些集群要么将评级混合在一起,因为惩罚不够高,或者分为单个评级组。可能是 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_