python 基于GPS空间相似度的K-means轨迹聚类

python 基于空间相似度的K-means轨迹聚类

这里分享一些轨迹聚类的基本方法,涉及轨迹距离的定义、kmeans聚类应用。
需要使用的python库如下

import pandas as pd
import numpy as np
import random
import os
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.spatial.distance import cdist
from itertools import combinations
from joblib import Parallel, delayed
from tqdm import tqdm

数据读取

假设数据是每一条轨迹一个excel文件,包括经纬度、速度、方向的航班数据。我们从文件中读取该数据,保存在字典中。
获取数据的地址,假设在多个文件中

def get_alldata_path(path):
    all_path = pd.DataFrame(columns=['path_root','path0','path1','path2','path','datalist'])
    path0 = os.listdir(path)
    for path_temp0 in path0:
        path1 =  os.listdir(path+path_temp0)
        for path_temp1 in path1:
            path2 =  os.listdir(path+path_temp0+'\\'+path_temp1)
            for path_temp2 in path2:
                path3 =  os.listdir(path+path_temp0+'\\'+path_temp1+'\\'+path_temp2)
                all_path.loc[all_path.shape[0]] = [path,path_temp0,path_temp1,path_temp2,
                                                       path+path_temp0+'\\'+path_temp1+'\\'+path_temp2+'\\',
                                                       path3]
    return all_path

这样你就可以得到你的数据的地址,方便后面读取需要的数据

#设置数据根目录
path = 'yourpath'
#获取所有数据地址
data_path = get_alldata_path(path)

读取数据,保存成字典格式,字典的key是这条轨迹的名称,value值是一个DataFrame,需要包含经纬度信息。

def read_data(data_path,idxs):
     '''
     功能:读取数据
     '''
     data = {}
     for idx in idxs:
         path_idx = data_path['path'][idx]
         for dataname in data_path['datalist'][idx]:
              temp = pd.read_excel(path_idx+dataname,header=None)
              temp = temp.loc[:,[4,5,6,8]]
              temp.replace('none',np.nan,inplace=True)
              temp.replace('Trak',np.nan,inplace=True)
              temp = temp.dropna().astype(float)
              temp.columns = ['GPSLongitude','GPSLatitude','direction','speed']
              data[str(idx)+'_'+dataname] = temp
     return data

读取你想要的数据,前面读取到的地址也是一个DataFrame,选择你想要进行聚类的数据读取进来。

#读取你想要的数据
idxs = [0,1,2]
data = read_data(data_path,idxs)

定义不同轨迹间的距离

这里使用了双向的Hausdorff距离(双向豪斯多夫距离)
给定两条轨迹A和B,其中轨迹A上有n个点,轨迹B上有m个点。它们之间的空间相似距离d定义为:
在这里插入图片描述
其中,di ,j 是一条轨迹上的第 i个点到另一条轨迹上的 第 j 个 点之间的多因素欧氏距离。可见, 如果轨迹 A 和 B 越相似, 它们之间的距离就越小, 反之则越大。

def OneWayHausdorffDistance(ptSetA, ptSetB):
    # 计算任意向量之间的距离,假设ptSetA有n个向量,ptSetB有m个向量
    # 得到矩阵C(n行m列)Cij代表A中都第i个向量到B中第j向量都距离
    dist = cdist(ptSetA, ptSetB, metric='euclidean')
    # np.min(dist,axis=1):计算每一行的的最小值
    # 即:固定点集A的值,求点集A中到集合B的最小值
    return np.max(np.min(dist, axis=1))
	# 计算双向的Hausdorff距离=====>H(ptSetA,ptSetB)=max(h(ptSetA,ptSetB),h(ptSetB,ptSetA))
	# ptSetA:输入的第一个点集
	# ptSetB:输入的第二个点集
	# Hausdorff距离度量了两个点集间的最大不匹配程度
def HausdorffDistance(ptSetA, ptSetB):
    # 计算双向的Hausdorff距离距离
    
    res = np.array([
        OneWayHausdorffDistance(ptSetA, ptSetB),
        OneWayHausdorffDistance(ptSetB, ptSetA)
    ])
    return np.max(res)  

计算距离矩阵

每个轨迹数据都包含经纬度、速度、方向,分别计算距离,然后根据一定的比例相加,活动最终的距离。

def DistanceMat(data,w=[0.7,0.2,0.1]):
     '''
     功能:计算轨迹段的距离矩阵
     输出:距离矩阵
     '''
     #要计算的组合
     ptCom = list(combinations(list(data.keys()),2))
     #基于轨迹的距离
     distance_tra = Parallel(n_jobs=8,verbose=False)(delayed(HausdorffDistance)(
                    data[ptSet1][['GPSLongitude','GPSLatitude']],data[ptSet2][['GPSLongitude','GPSLatitude']]
                    ) for ptSet1,ptSet2 in ptCom)
     distancemat_tra = pd.DataFrame(ptCom)
     distancemat_tra['distance'] = distance_tra 
     distancemat_tra = distancemat_tra.pivot(index=0,columns=1,values='distance')
     for pt1 in data.keys():
          distancemat_tra.loc[str(pt1),str(pt1)] = 0
     distancemat_tra = distancemat_tra.fillna(0)
     distancemat_tra = distancemat_tra.loc[list(data.keys()),list(data.keys())]
     distancemat_tra = distancemat_tra+distancemat_tra.T
     
     #基于方向的距离
     distance_speed = Parallel(n_jobs=8,verbose=False)(delayed(HausdorffDistance)(
                    data[ptSet1][['speed']],data[ptSet2][['speed']]
                    ) for ptSet1,ptSet2 in ptCom)
     distancemat_speed = pd.DataFrame(ptCom)
     distancemat_speed['distance'] = distance_speed 
     distancemat_speed = distancemat_speed.pivot(index=0,columns=1,values='distance')
     for pt1 in data.keys():
          distancemat_speed.loc[str(pt1),str(pt1)] = 0
     distancemat_speed = distancemat_speed.fillna(0)
     distancemat_speed = distancemat_speed.loc[list(data.keys()),list(data.keys())]
     distancemat_speed = distancemat_speed+distancemat_speed.T
     #基于方向的距离
     distance_direction = Parallel(n_jobs=8,verbose=False)(delayed(HausdorffDistance)(
                    data[ptSet1][['direction']],data[ptSet2][['direction']]
                    ) for ptSet1,ptSet2 in ptCom)
     distancemat_direction = pd.DataFrame(ptCom)
     distancemat_direction['distance'] = distance_direction 
     distancemat_direction = distancemat_direction.pivot(index=0,columns=1,values='distance')
     for pt1 in data.keys():
          distancemat_direction.loc[str(pt1),str(pt1)] = 0
     distancemat_direction = distancemat_direction.fillna(0)
     distancemat_direction = distancemat_direction.loc[list(data.keys()),list(data.keys())]
     distancemat_direction = distancemat_direction+distancemat_direction.T
     distancemat_tra = (distancemat_tra-distancemat_tra.min().min())/(distancemat_tra.max().max()-distancemat_tra.min().min())
     distancemat_speed = (distancemat_speed-distancemat_speed.min().min())/(distancemat_speed.max().max()-distancemat_speed.min().min())
     distancemat_direction = (distancemat_direction-distancemat_direction.min().min())/(distancemat_direction.max().max()-distancemat_direction.min().min())
     distancemat = w[0]*distancemat_tra+w[1]*distancemat_speed+w[2]*distancemat_direction 
     return distancemat

使用前面读取的数据,计算不同轨迹间的距离矩阵,缺点在于计算时间会随着轨迹数的增大而指数增长。

distancemat = DistanceMat(data,w=[0.7,0.2,0.1])

k-means聚类

获得了不同轨迹间的距离矩阵后,就可以进行聚类了。这里选择k-means,为了得到更好的结果,聚类前的聚类中心选取也经过了一些设计,排除了随机选择,而是选择尽可能远的轨迹点作为 初始中心。
初始化聚类“中心”。随机选取一条轨迹作为第一类的中心, 即选取一个轨迹序列作为聚类的初始“中心。然后在剩下的 L - 1 个序列中选取一个序列 X 2 作为第二类的中心 C 2 , 设定一个阈值 q, 使其到第一类的中心 C 1 的距离大于q。

class KMeans:
    def __init__(self,n_clusters=5,Q=74018,max_iter=150):
         self.n_clusters = n_clusters #聚类数
         self.Q = Q
         self.max_iter = max_iter    # 最大迭代数
         
    def fit(self,distancemat):
         #选择初始中心
         best_c = random.sample(distancemat.columns.tolist(),1)    
         for i in range(self.n_clusters-1):
              best_c += random.sample(distancemat.loc[(distancemat[best_c[-1]]>self.Q)&(~distancemat.index.isin(best_c))].index.tolist(),1)  
         center_init = distancemat[best_c] #选择最小的样本组合为初始质心
         self._init_center = center_init
         #迭代停止条件
         iter_ = 0
         run = True
         #开始迭代
         while (iter_<self.max_iter)&(run==True):
              #聚类聚类标签更新
              labels_ = np.argmin(center_init.values,axis=1)
              #聚类中心更新
              best_c_ = [distancemat.iloc[labels_== i,labels_==i].sum().idxmin() for i in range(self.n_clusters)]
              center_init_ = distancemat[best_c_]
              #停止条件
              iter_ += 1
              if best_c_ == best_c:
                   run = False
              center_init = center_init_.copy()
              best_c = best_c_.copy()
         #记录数据
         self.labels_ = np.argmin(center_init.values,axis=1)
         self.center_tra = center_init.columns.values
         self.num_iter = iter_
         self.sse = sum([sum(center_init.iloc[self.labels_==i,i]) for i in range(self.n_clusters)])

应用聚类,根据平方误差和SSE结合手肘法确定最佳的聚类数,使用最佳的聚类数获得最后聚类模型。

 #聚类,保存不同的sse
SSE = []
for i in range(2,30):
  kmeans = KMeans(n_clusters=i,Q=0.01,max_iter=150)
  kmeans.fit(distancemat)
  SSE.append(kmeans.sse)
#画图
plt.figure(0)
plt.plot(SSE)
plt.show()

#使用最好的结果进行聚类
n_clusters=12
kmeans = KMeans(n_clusters=n_clusters,Q=0.01,max_iter=150)
kmeans.fit(distancemat)
kmeans.sse  #输出sse
kmeans.labels_  #输出标签
kmeans.center_tra  #输出聚类中心

#画图,不同类的轨迹使用不同的颜色
plt.figure(1)
for i in range(n_clusters):
   for name in distancemat.columns[kmeans.labels_==i]:
        plt.plot(data[name].loc[:,'GPSLongitude'],data[name].loc[:,'GPSLatitude'],c=sns.xkcd_rgb[list(sns.xkcd_rgb.keys())[i]])
plt.show()

#保存每一个轨迹属于哪一类
kmeans_result = pd.DataFrame(columns=['label','id'])
for i in range(n_clusters):
   kmeans_result.loc[i] = [i,distancemat.columns[kmeans.labels_==i].tolist()]

  • 6
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
### 回答1: k-means是一种常用的文本聚类算法,可以用Python实现。它的基本思想是将文本数据分成k个簇,每个簇内的文本相似度较高,不同簇之间的文本相似度较低。在Python中,可以使用scikit-learn库中的KMeans类来实现k-means文本聚类。 ### 回答2: K-Means文本聚类是一种基于机器学习的文本数据分析方法。Python是一种功能强大的编程语言,它提供了丰富的自然语言处理工具和机器学习库。因此,Python被广泛用于文本聚类K-Means聚类算法是一种迭代算法,它可以将相似的文本数据聚集在一起。该算法的主要思想是基于文本数据之间的相似度来对文本进行分组。在K-Means算法中,首先需要选择一个聚类的数量K。然后通过计算每个文本数据与其所属聚类中心的距离来确定该文本数据应该属于哪个聚类,并更新聚类中心的位置。不断迭代直到满足停止条件为止,从而得到最终的聚类结果。 在Python中,可以使用Scikit-Learn库实现K-Means聚类算法。该库提供了KMeans类来实现K-Means聚类算法的所有功能。首先需要将文本数据转换为特征向量,这可以通过使用TF-IDF、词袋模型等方法来实现。然后可以实例化一个KMeans对象,设置聚类数量K、迭代次数、随机种子等参数,并使用fit方法对数据进行聚类。 除了Scikit-Learn,Python还有许多其他的自然语言处理和文本聚类库,如NLTK、Gensim、spaCy等。这些库不仅提供了K-Means聚类算法,还提供了许多其他的聚类算法和文本处理工具,可以满足不同场景下的需求。 总之,K-Means文本聚类是一种常用的文本数据分析方法,Python是一种流行的编程语言,可以使用各种机器学习和自然语言处理库来实现文本聚类。 ### 回答3: K-means算法是一种基于距离度量的聚类算法,是文本聚类中常用的方法之一。在Python中,可以使用scikit-learn库实现K-means文本聚类。 首先,需要将文本数据转换为文本向量,常用的向量化方法有词袋模型(bag-of-words)和TF-IDF模型。词袋模型将每个文本看作一个词序列,忽略词序关系,只考虑每种词出现的频率,形成一个大小为词汇表大小的向量。TF-IDF模型除了考虑词频外,还考虑每个词在文本集合中的重要程度,形成一个大小为词汇表大小的向量。 在使用K-means算法之前,需要确定聚类的个数K。可以使用常见的肘部法则(elbow method)来确定K值,即绘制SSE(Sum of Squared Errors)和K的关系图,找到拐点所在的K值,即为最优的聚类个数。 接下来,使用scikit-learn库中的KMeans模块进行文本聚类,参数包括文本向量、聚类个数K、聚类的初始化方法、最大迭代次数等。KMeans模块使用Lloyd算法进行聚类,即随机初始化K个聚类中心,然后将每个样本分配到距离最近的聚类中心中,再更新聚类中心,并重复上述过程,直到聚类中心不再变化或达到最大迭代次数为止。 聚类结果可以通过绘制词云、热力图等方式进行可视化展示,以便于对聚类结果进行理解和解释。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值