共享单车数据实战
为了强化数据处理能力,本周做了一次共享单车数据实战,数据为2020年厦门市共享单车的统计数据,包含共享单车订单数据和电子停车栅栏的经纬度信息数据。
梳理一下具体过程:
1、处理共享单车数据,从中提取停车需求;
2、处理电子栅栏数据,生成地理空间图形;
3、将共享单车的停车需求与电子栅栏进行地图匹配(本次练习指定时刻为上午8点)判断哪些单车停在栅栏内,哪些停在栅栏外,停在栅栏外的单车距离最近的栅栏是哪些,距离有多远,并可视化处理。
这个练习一共包括三个部分:
第一部分:从共享单车订单数据和电子停车栅栏数据进行共享单车停车需求识别。
1 import pandas as pd 2 #读取数据 3 data_bike = pd.read_csv(r'data_bike.csv',header = None) 4 #重命名数据的列 5 data_bike.columns = ['BIKE_ID','LATITUDE','LONGITUDE','LOCK_STATUS','DATA_TIME'] 6 #对数据根据车辆与时间排序 7 data_bike = data_bike.sort_values(by = ['BIKE_ID','DATA_TIME']) 8 #数据列整体上移一行,赋值给新的列 9 data_bike['BIKE_ID1'] = data_bike['BIKE_ID'].shift(-1) 10 for i in ['BIKE_ID', 'LATITUDE', 'LONGITUDE', 'LOCK_STATUS', 'DATA_TIME']: 11 data_bike[i+'1'] = data_bike[i].shift(-1) 12 #去除非同一车辆的记录 13 data_bike = data_bike[data_bike['BIKE_ID'] == data_bike['BIKE_ID1']] 14 #提取其中的停车信息 15 data_bike = data_bike[data_bike['LOCK_STATUS'] == 1] 16 #保留有用的列 17 data_bike = data_bike[['BIKE_ID','LATITUDE','LONGITUDE','DATA_TIME','DATA_TIME1']] 18 #取某一时间 19 timstamp = '2020/12/25 8:00:00' 20 #提取这一时间的停车需求 21 parking_points = data_bike[(data_bike['DATA_TIME']<=timstamp)&(data_bike['DATA_TIME1']>=timstamp)]
处理后:
可以看到不同BIKE_ID下的经纬坐标以及锁车时间点,解锁时间点。
注释:
(1).对数据根据车辆与时间排序(df.sort_values)
(2).数据列整体上移一行(df.shift)
第二部分:共享单车电子栅栏的地理信息处理。
1 import geopandas as gpd 2 from shapely.geometry import Polygon 3 import pandas as pd 4 #读取电子栅栏数据 5 data_fence = pd.read_csv(r'data_fence.csv') 6 #为电子栅栏生成多边形几何信息 7 geometry = [] 8 for i in range(len(data_fence)): 9 exec('points = ['+data_fence['FENCE_LOC'].iloc[i]+']') 10 geometry.append(Polygon(points)) 11 data_fence['geometry'] = geometry 12 #将数据表转换为geodataframe 13 data_fence = gpd.GeoDataFrame(data_fence) 14 data_fence = data_fence.drop('FENCE_LOC',axis = 1) 15 #存储数据,shp格式 16 data_fence.to_file(r'data_fence') 17 #存储数据,geojson格式 18 data_fence.to_file(r'data_fence.json',driver = 'GeoJSON',encoding = 'utf-8')
处理结果:
栅栏图形:
注释:
(1). 用shapely中的Polygon生成器从经纬度坐标生成电子栅栏(shapely.geometry.Polygon)
(2).为数据增加一列geometry,将数据从dataframe转换成geodataframe,应用geopandas中的空间操作。
(3).利用(gdf.to_file)存储地理空间数据为shp或json文件。
第三部分:共享单车与电子栅栏的地图匹配。
1、利用空间链接识别那些单车停在栅栏内,哪些单车停在栅栏外。
2、利用KDTree识别离栅栏外单车最近的停车栅栏,并计算距离。
1 import numpy as np 2 from scipy.spatial import cKDTree 3 import itertools 4 from operator import itemgetter 5 6 #geopandas的空间连接方法 7 ?gpd.sjoin 8 #将停车需求转化为geopandas,方便后续匹配 9 parking_points = gpd.GeoDataFrame(parking_points) 10 parking_points['geometry'] = gpd.points_from_xy(parking_points['LONGITUDE'],parking_points['LATITUDE']) 11 #空间连接:提取在栅栏内的停车需求 12 parking_points = gpd.sjoin(parking_points,data_fence,how = 'left') 13 #保存在栅栏内的停车需求 14 a = parking_points[-parking_points['FENCE_ID'].isnull()] 15 #保存在栅栏外的停车需求 16 b = parking_points[parking_points['FENCE_ID'].isnull()] 17 #提取每个电子栅栏的边界 18 data_fence_boundary = data_fence.copy() 19 data_fence_boundary['geometry'] = data_fence_boundary.boundary 20 #尝试绘制前几个栅栏 21 data_fence_boundary.iloc[:5].plot() 22 23 #定义函数,用cKDTree匹配点与点,点与线 24 def ckdnearest_point(gdA, gdB): 25 ''' 26 输入两个geodataframe,gdfA、gdfB均为点,该方法会为gdfA表连接上gdfB中最近的点,并添加距离字段dsit 27 ''' 28 #提取gdA中的所有点要素 29 nA = np.array(list(gdA.geometry.apply(lambda x: (x.x, x.y)))) 30 #提取gdB中的所有点要素 31 nB = np.array(list(gdB.geometry.apply(lambda x: (x.x, x.y)))) 32 #为gdB表的点建立KDTree 33 btree = cKDTree(nB) 34 #在gdB的KDTree中查询gdA的点,dist为距离,idx为gdB中离gdA最近的坐标点 35 dist, idx = btree.query(nA, k=1) 36 #构建匹配的结果 37 gdf = pd.concat( 38 [gdA.reset_index(drop=True), gdB.loc[idx, gdB.columns != 'geometry'].reset_index(drop=True), 39 pd.Series(dist, name='dist')], axis=1) 40 return gdf 41 def ckdnearest_line(gdfA, gdfB): 42 ''' 43 输入两个geodataframe,其中gdfA为点,gdfB为线,该方法会为gdfA表连接上gdfB中最近的线,并添加距离字段dsit 44 ''' 45 #提取gdA中的所有点要素 46 A = np.concatenate( 47 [np.array(geom.coords) for geom in gdfA.geometry.to_list()]) 48 #把gdfB的几何坐标提取到B,此时B为一个大list中包含多个小list,每个小list代表一个几何图形,小list中为坐标 49 #B=[[[要素1坐标1],[要素1坐标2],...],[[要素2坐标1],[要素2坐标2],...]] 50 B = [np.array(geom.coords) for geom in gdfB.geometry.to_list()] 51 #B_ix代表B中的每个坐标点分别属于B中的哪个几何图形 52 B_ix = tuple(itertools.chain.from_iterable( 53 [itertools.repeat(i, x) for i, x in enumerate(list(map(len, B)))])) 54 #把B表展开,B=[[要素1坐标1],[要素1坐标2],...,[要素2坐标2],[要素2坐标2],...] 55 B = np.concatenate(B) 56 #为B表建立KDTree 57 ckd_tree = cKDTree(B) 58 #在B的KDTree中查询A的点,dist为距离,idx为B中离A最近的坐标点 59 dist, idx = ckd_tree.query(A, k=1) 60 #由坐标点对应到几何要素 61 idx = itemgetter(*idx)(B_ix) 62 #构建匹配的结果 63 gdf = pd.concat( 64 [gdfA.reset_index(drop=True), gdfB.loc[idx, gdfB.columns != 'geometry'].reset_index(drop=True), 65 pd.Series(dist, name='dist').reset_index(drop=True)], axis=1) 66 return gdf 67 #把栅栏外的停车点匹配到最近栅栏 68 b = ckdnearest_line(b, data_fence_boundary) 69 70 #整理数据然后组合 71 a = a[['BIKE_ID', 'LATITUDE', 'LONGITUDE', 'DATA_TIME', 'DATA_TIME1', 72 'geometry','FENCE_ID']] 73 b.columns = ['BIKE_ID', 'LATITUDE', 'LONGITUDE', 'DATA_TIME', 'DATA_TIME1', 74 'geometry', 'index_right', 'FENCE_ID1', 'FENCE_ID', 'dist'] 75 b = b[['BIKE_ID', 'LATITUDE', 'LONGITUDE', 'DATA_TIME', 'DATA_TIME1', 76 'geometry','FENCE_ID', 'dist']] 77 parking_points = pd.concat([a,b]) 78 79 #可视化 80 import matplotlib.pyplot as plt81 #创建图 82 fig = plt.figure(1,(10,10),dpi = 200) 83 ax = plt.subplot(111) 84 85 #选择某一栅栏 86 fence = '育秀东路_L_A09001' 87 #绘制栅栏内外的停车需求 88 parking_points[parking_points['FENCE_ID']==fence].plot(ax = ax) 89 parking_points[(parking_points['FENCE_ID']==fence)&(parking_points['dist'].isnull())].plot(ax = ax,color = 'r') 90 91 #绘制栅栏 92 data_fence[data_fence['FENCE_ID']==fence].plot(ax = ax,color = 'g',alpha=0.3) 93 94 #显示图 95 plt.show()
图示:
停在栅栏内:
停在栅栏外:
最近栅栏及距离:
可视化图:
附录:
(1). 将停车需求转化为gpd(gpd.points_from_xy);
(2). 空间连接(gpd.sjoin);
(3). 提取电子栏栅边界(gdf.boundary);
(4). KDTree:
确定的点不一定是距离最短的,但查找速度快,算法复杂度为 o (log (n) )
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署