共享单车停车需求识别与地图匹配
在我们日常生活中,大家都骑过共享单车,现在的共享单车app里面都有这样一个功能:在地图上规划出了很多的单车停放栅栏(停放点),用户需要把单车停在停放栅栏内,否则就要扣钱交调度费。但有时候停放栅栏的规划并不合理,城市里有些地区的停放栅栏比较密集,有些地方的停放栅栏却很稀疏,骑车骑到一个较远的地方,却找不到停放点,用户就只能多花钱,同样地,对于共享单车公司来说,停车栅栏规划的不合理,也加大了调度的人力时间。所以根据单车停放需求来就合理规划停车栅栏就很重要。如果我们能识别出来某些停车需求量很大的地区,这些地区的停放栅栏却很稀少,那就可以方便共享单车公司作进一步的业务调整。同时,我们也可以动态地探索停放需求量变化规律,协同调整。
要达到上述目标,需要完成以下任务:
- 在某个时间点(比如早晨8点),有哪些单车处于停放状态?
- 这些停放状态的车,哪些停在停放栅栏内部,哪些在停放栅栏外部?
- 在停放栅栏外部的那些单车,距离它们各自最近的停放栅栏是哪些?单车和最近停放栅栏之间的距离是多少?(寻优匹配问题)
- 不同时段单车停放点需求量怎么变化?哪些区域的停放需求更高?停放栅栏怎样增加?停车需求量随时间如何变化?
该项目用到两份数据:
1.厦门市某共享单车公司2020年12月25日的共享单车订单数据
2.厦门市共享单车电子栅栏地理信息数据。
数据大致信息大致如下:
- 共享单车订单数据:
样本量:232806*5
变量 | 含义 | 类型 |
---|---|---|
BIKE_ID | 单车订单号 | object |
LATITUDE | 单车所在位置纬度 | float64 |
LONGTITUDE | 单车所在位置经度 | float64 |
LOCK_STATUS | 单车开/锁状态 | int64 |
DATA_TIME | 单车改变状态的时间 | object |
- 电子栅栏地理信息数据:
样本量:14071*2
变量 | 含义 | 类型 |
---|---|---|
FENCE_ID | 停车栅栏ID | object |
FENCE_LOC | 停车栅栏位置 | object |
用到的python算法包有:
1.pandas
2.numpy
3.matplotlib
4.scipy : kd tree寻优算法
5.geopandas:处理空间地理数据的利器,基本可以替代Arcgis或者QGIS
6.shapely.geometry: 存储点、线、面要素
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import geopandas as gpd
from shapely.geometry import Polygon,Point,LineString
import scipy
地理空间数据可视化:kepler.gl
一、共享单车GPS信息数据处理
共享单车GPS数据是一个dataframe,状况如下:
这份数据的记录逻辑是这样的:共享单车的锁状态改变一次,数据库就会生成一条这辆单车的状态数据。比如有人扫A车开锁了,这时数据库里面就会记录一条A车的信息:单车的ID、所在位置的经纬度信息、锁状态为0(开锁),状态改变时的时间;用户骑行结束锁了A车,又会生成一条关于A车的数据:单车的ID、所在位置的经纬度信息、锁状态为1(上锁),状态改变时的时间。
2.1单车运行轨迹处理
我们想看每一辆车的行动记录,用sort函数先对BIKE_ID进行排序,再在每辆单车的记录当中对DATA_TIME进行排序,这样就能看到每辆单车在一天之内行动轨迹、状态变化。
data_bike=data_bike.sort_values(by=['BIKE_ID','DATA_TIME'])
2.2 停放状态单车的提取
由于我们想要的是单车停放信息的数据,所以该dataframe里面处于停放状态的记录是我们需要提取出来的。而且我们希望知道它停放的时段,也就是某一辆车从停放状态转变为开锁状态。即LOCK_STATUS从1变为0的这一段时间。这里需要用.shift()函数对dataframe做一个上移操作,这时处理时序数据经常会用到的技巧,看下面图示:
把原来的dataframe上移一个数据,让每条数据和它自身的下一条数据拼接在一起,就像红框框起来的数据是我们想要的,它刻画了车1从停放(状态1)变为开锁(状态0)的这一段时间。我们要把数据中这样的记录提取出来,用来分析单车停放需求。车1从开锁(状态0)变为停放(状态1)的这一段时间是在骑行的,这一段动态的变化我们不需要,删掉这样的数据。还有前面是两辆不同车辆拼接在一起的记录也要删掉(车1拼接车2),没有什么用处。
代码如下:
for i in ['BIKE_ID','LATITUDE','LONGTITUDE','LOCK_STATUS','DATA_TIME']:
#每一列上移一位
data_bike[i+'1']=data_bike[i].shift(-1)
#提取出前后车辆ID一致的单车记录
data_bike=data_bike[data_bike['BIKE_ID']==data_bike['BIKE_ID1']]
#提取出锁状态从停放(1)变为开锁(0)的单车记录
data_bike=data_bike[data_bike['LOCK_STATUS']==1]
#取出有用的列:单车ID、单车停放位置的经纬度、单车开始停车的时间、单车结束停车(开锁)的时间
data_bike=data_bike[['BIKE_ID','LATITUDE','LONGTITUDE','DATA_TIME','DATA_TIME1']]
#DATA_TIME是开始停车的时间,DATA_TIME1是停车结束的时间
得到的datdaframe如下:
提取出来的单车记录就只剩25000左右的数量了。
2.3 某一个时间点处于停放状态单车的提取
比如要提出在早晨8:00处于停放状态的单车记录,这样操作:
timestamp='2020/12/25 8:00:00'
parking_points=data_bike[(data_bike['DATA_TIME']<=timestamp)&(data_bike['DATA_TIME1']>=timestamp)