对于赛题给定的单车订单数据和单车轨迹而言,其并没有给定具体的街道信息,只在单车停车点给定了街道信息。因此我们可以选择使用停车点数据做街道信息匹配,也可以用外部数据做匹配,操作都是一致的。
我们可以使用DCIC2020年给的厦门市道路矢量数据来完成:
import shapely, geopandas, fiona
import seaborn as sns
%pylab inline
shp_df = geopandas.GeoDataFrame.from_file("xiamen_road/xm_road_edit20200826.shp")
shp_df.plot(figsize=(8, 8))
还可以提取具体道路经纬度:
shp_df['START_LONGITUDE'] = shp_df['geometry'].apply(lambda x: x.coords[0][0])
shp_df['START_LATITUDE'] = shp_df['geometry'].apply(lambda x: x.coords[0][1])
shp_df['END_LONGITUDE'] = shp_df['geometry'].apply(lambda x: x.coords[1][0])
shp_df['END_LATITUDE'] = shp_df['geometry'].apply(lambda x: x.coords[1][1])
shp_df = shp_df.drop(['UserID', 'geometry'], axis=1)
road_name | START_LONGITUDE | START_LATITUDE | END_LONGITUDE | END_LATITUDE |
---|---|---|---|---|
安仁大道(西向东) | 117.986519 | 24.612696 | 117.989647 | 24.610982 |
安仁大道(西向东) | 117.996934 | 24.605661 | 117.997052 | 24.605552 |
安仁大道(西向东) | 117.997052 | 24.605552 | 117.997149 | 24.605466 |
安仁大道(西向东) | 117.999725 | 24.602609 | 118.002706 | 24.598994 |
安仁大道(西向东) | 118.002706 | 24.598994 | 118.002770 | 24.598916 |
... | ... | ... | ... | ... |
圆一路 | 118.186581 | 24.504305 | 118.186646 | 24.505749 |
安东路 | 118.000649 | 24.614283 | 118.000729 | 24.614362 |
长岸路辅路(北向南) | 118.088231 | 24.511117 | 118.088089 | 24.510614 |
长岸路辅路(北向南) | 118.087805 | 24.509671 | 118.087780 | 24.509593 |
殿前一路(西向东) | 118.096068 | 24.532458 | 118.095621 | 24.532743 |
对于骑行轨迹与道路匹配,我们仍然可以使用KNN/或HNSW的方法:
from sklearn.neighbors import NearestNeighbors
knn = NearestNeighbors(metric = "haversine", n_jobs=-1, algorithm='brute')
knn.fit(np.vstack([shp_df[['START_LATITUDE', 'START_LONGITUDE']].values,
shp_df[['END_LATITUDE', 'END_LONGITUDE']].values]
))
import hnswlib
import numpy as np
p = hnswlib.Index(space='l2', dim=2)
p.init_index(max_elements=300000, ef_construction=1000, M=32)
p.set_ef(1024)
p.set_num_threads(14)
p.add_items(np.vstack([shp_df[['START_LATITUDE', 'START_LONGITUDE']].values,
shp_df[['END_LATITUDE', 'END_LONGITUDE']].values]
))
当然也可以使用KNN + 停车点的数据完成上述步骤,也可以使用百度梯度的API完成。
index, dist = p.knn_query(bike_track[['LATITUDE','LONGITUDE']].values[:], k=1)
index = index.reshape(-1)
bike_track['road_name'] = shp_df.iloc[index % len(shp_df)]['road_name'].values
在做完具体的道路匹配后,可以直接完成频率计算:
bike_track[bike_track['day'] == 21]['road_name'].value_counts().head(10)
路名称 | 频率 |
---|---|
厦禾路(东向西) | 53924 |
吕岭路(西向东) | 50296 |
金尚路(南向北) | 36511 |
云顶北路(南向北) | 34136 |
仙岳路辅路(西向东) | 32861 |
湖滨南路(东向西) | 32219 |
仙岳路辅路(东向西) | 28889 |
湖滨南路(西向东) | 28785 |
金尚路(北向南) | 28378 |
嘉禾路(北向南) | 26169 |
骑行友好度可以从两个角度来考虑,一是密集骑行路线,二是街道单位骑行花费时间,前者已经计算完成,这里我们关注后者。
$$街道单位骑行花费时间 = \frac{街道距离}{单位骑行时间}$$
bike_use_ratio = bike_track.groupby(['road_name','BICYCLE_ID'])['date'].count()*15.0/3615/4
bike_use_ratio = bike_use_ratio.reset_index()
bike_use_ratio = bike_use_ratio.groupby(['road_name'])['date'].mean()
from geopy.distance import geodesic
# 根据停车点 范围 计算具体的面积
shp_df['Length'] = shp_df.apply(lambda x: geodesic(
(x['START_LATITUDE'], x['START_LONGITUDE']), (x['END_LATITUDE'], x['END_LONGITUDE'])
).meters, axis=1)
shp_legth = shp_df.groupby(['road_name'])[['Length']].sum()
bike_use_ratio = pd.merge(bike_use_ratio, shp_legth, on='road_name', how='left')
bike_use_ratio['friendliness'] = bike_use_ratio['Length'] / bike_use_ratio['date']
bike_use_ratio.sort_values(by='friendliness', ascending=False)
bike_use_ratio = bike_use_ratio[['road_name', 'friendliness']]
© 2019-2023 coggle.club 版权所有 京ICP备20022947 京公网安备 11030102010643号