添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1. 问题背景

交通研究中,基础数据的获取尤为重要。数据获取方式近年来也有着天翻地覆的变化。

以获取出行者OD(origin, destination)的OD调查为例,获取OD能够定位城市各区域的交通发生与吸引情况,从而进行交通规划、城市管理等工作。传统OD获取方法如问卷调查法面向范围小、耗费人力物力、结果准确性差。随着含有GPS等传感器的智能手机的不断普及,与互联网经济的快速发展,滴滴、摩拜、高德等企业在运营中获取到了大量数据,这些数据获取难度小,覆盖范围广,采用“众包”的思路即获取到大量可以用于交通研究并指导实践,从而达到缓解城市交通拥堵的目的。

本次,我将利用订单数据(反映OD)进行空间聚类,从而利用出租车上下车点的数量、密度,识别城市的热点地区,进行交通生成的时空分析。

2. 出租车轨迹与订单数据

目前部分公司通过申请途径开放的数据主要有 轨迹数据 订单数据 两类。轨迹数据每隔一定的时间(约6-10s)记录一次,订单数据则每完成一个订单为一条记录。

本次选用 2016.12.22周四上海市 一天的 订单数据 进行分析,由于未能获取到能多数据,本案例旨在抛砖引玉,并记录与地理信息相关数据的处理方法。

2.1 订单数据

以下是本次使用的 订单数据 的字段:

针对原始数据,首先需要进行数据预处理工作:

可以使用 绘图 统计量分析 相关性分析 等的手段,其中绘图可以借助Python的matplotlib库,统计量分析可以使用pandas自带的分析功能,相关性分析可以借助scikit-learn库的相关功能。

  • 使用sklearn进行特征工程
  • Pandas统计分析
  • Matplotlib tutorial

    3.1 导入模块、数据、查看异常数据

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #导入所需要的模块
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    %matplotlib inline
    import geopandas as gpd
    import osmnx as ox
    import time
    from geopandas.tools import sjoin
    import datetime
    from math import radians, cos, sin, asin, sqrt
    plt.style.use('ggplot')

    由于数据不存在缺失(空缺),未记录的“订单结束时间e_time”被记录为’0000-00-00 00:00:00’,因此在此查看未正确记录的数据量。以下结果表明,问题数据量达近30%,需采用一定手段进行处理。

    1
    2
    3
    4
    5
    Thurdata=pd.read_csv('THURSDAY.csv')
    #判断是否是问题数据,并添加属性列
    Thurdata['judge'] = Thurdata.e_time.apply(lambda x: 1 if x=='0000-00-00 00:00:00' else 0)
    #查看问题数据量
    Thurdata['judge'].value_counts()
    0    18568
    1     7072
    Name: judge, dtype: int64
    

    目前缺失值、异常值的主要处理手段有:

  • 删除异常值——此处由于异常值量较多,直接删除会造成结果偏差
  • 采用前、后值填补——由于订单数据间不存在关联性,此方法不合适
  • 采用均值填充——可以采用‘开始时间+平均时间间隔’的方式来填充
  • 采用其他关系填充
  • 3.2 异常数据的筛选与结束时间的回归补全

    在本步骤中:

    Step1 :对于结束时间e_time正常的数据,计算通行时间 $time_interval=e_time-s_time$;结束时间e_time异常的数据,采用np.nan填充

    Step2 :查看通行时间数据特点,删除通行时间异常的数据(time_intervel<180s或time_interval>14400s)180s或time_interval>

    Step3 :计算起终点间直线距离(经纬度距离计算公式)

    Step4 :查看起终点间直线距离的特点,删除直线距离异常的数据(LineDistance<10m或linedistance>100km)10m或linedistance>

    Step5 :回归分析直线距离与通行时间的关系,并利用回归公式填充结束时间e_time异常的数据

    Step6 :借助地理信息计算路网中起终点间的最短路,并记录。

    Step7 :回归分析路网最短距离与通行时间的关系,并利用回归公式重新填充结束时间e_time异常的数据,整合正常数据与填充异常结束时间值的数据

    Step1 计算通行时间

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def dataInterval(data1,data2):
    d1 = datetime.datetime.strptime(data1,'%m/%d/%Y %H:%M')#字符串形式转化为DateTime形式
    d2 = datetime.datetime.strptime(data2,'%m/%d/%Y %H:%M')
    delta = d2 - d1
    return delta.seconds#将timedelta转化为秒计数

    def getInterval(arrLike): #用来计算通行时间调用的函数
    s_time = arrLike['s_time']
    e_time = arrLike['e_time']
    error = arrLike['judge']
    if error==0:#正常数据
    interval = dataInterval(s_time.strip(),e_time.strip()) #去掉两端空白
    else:#问题数据
    interval = np.nan
    return interval
    Thurdata['TimeInterval'] = Thurdata.apply(getInterval,axis=1)#时间差计算

    Step2 查看通行时间特点,删除异常数据

    1
    2
    timeinterval=np.array(Thurdata['TimeInterval'])
    timeinterval = np.sort(timeinterval[~np.isnan(timeinterval)])#将非空数据找出并进行排序
    1
    2
    3
    4
    plt.plot(timeinterval,'.')
    plt.ylabel('timeinterval(s)')
    plt.xlabel('sample')
    plt.title('Timeinterval of all the samples');#时间间隔

    将通行时间排序后绘图,不难发现随着样本数量的变化,时间间隔增加先处于平稳状态而后激增。绘制未排序的时间间隔散点图也可以发现,大部分记录通行时间均在8000s以内,考虑上海市地理状况与采取出租出行的实际状况,同时考虑样本数量,我们去除[0,180s]与14400s及以上的数据。

    1
    2
    3
    4
    5
    6
    timeinterval=np.array(Thurdata['TimeInterval'])
    timeinterval = timeinterval[~np.isnan(timeinterval)]
    plt.plot(timeinterval,'.')
    plt.ylabel('timeinterval(s)')
    plt.xlabel('sample')
    plt.title('Timeinterval of all the samples(no sort)');
    1
    2
    3
    4
    5
    6
    7
    timeinterval=np.array(Thurdata['TimeInterval'])
    timeinterval = timeinterval[~np.isnan(timeinterval)]
    c_ti=timeinterval[(timeinterval>180)&(timeinterval<14400)]#考虑可能的乘车时间(大于3min小于4小时)
    plt.hist(c_ti,bins=50,edgecolor='black')#滤除时间间隔
    plt.xlabel('timeinterval')
    plt.ylabel('numberofsample(s)')
    plt.title('The histogram of the normal Timeinterval(180s,14400s)');

    分别绘制通行时间在(180s,14400s)与(180s,5400s)区间范围内的分布直方图,从图中我们可以进一步看到,通行[500s,900s]区间内的样本数较多,随着通行时间的增加,总体样本数呈现由较高水平先增加后减小的趋势。(bins=100效果更明显)

    1
    2
    3
    4
    5
    6
    7
    timeinterval=np.array(Thurdata['TimeInterval'])
    timeinterval = timeinterval[~np.isnan(timeinterval)]
    c_ti=timeinterval[(timeinterval>180)&(timeinterval<5400)]#考虑可能的乘车时间(大于3min小于4小时)
    plt.hist(c_ti,bins=50,edgecolor='black')#滤除时间间隔
    plt.xlabel('timeinterval(s)')
    plt.ylabel('numberofsample')
    plt.title('The histogram of the normal Timeinterval(180s,5400s)');

    Step3 直线距离计算

    采用 Haversine formula 公式,利用两点经纬度坐标,计算距离。并将直线距离写入数据表的‘LD’列。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def countdistance(long1,la1,long2,la2):#地球上直线距离的计算
    #deltasigma=np.arctan(np.sqrt((np.cos(la2)*np.sin(long2-long1))**2+(np.cos(la1)*np.sin(la2)-np.sin(la1)*np.cos(la2)*np.cos(long2-long1))**2)/(np.sin(la1)*np.sin(la2)+np.cos(la1)*np.cos(la2)*np.cos(long2-long1)))
    #lineardistance=6372.795*deltasigma
    long1, la1, long2, la2 = map(radians, [long1, la1, long2, la2])
    dlong = long2 - long1
    dla = la2 - la1
    a = sin(dla/2)**2 + cos(la1) * cos(la2) * sin(dlong/2)**2
    c = 2 * asin(sqrt(a))
    r = 6371.0088 # 地球平均半径,单位为公里
    lineardistance=c * r * 1000 #精度为m
    return lineardistance
    def getLD(arrLike): #指定df调用距离计算函数
    long1 = arrLike['s_long']
    la1 = arrLike['s_la']
    long2 = arrLike['e_long']
    la2 = arrLike['e_la']
    LD = countdistance(long1,la1,long2,la2)
    return LD
    Thurdata['LD'] = Thurdata.apply(getLD,axis=1)

    Step4 查看直线距离特点,删除异常数据

    采用与通行时间相同的分析方式,对于订单数据每条的起终点间距离进行分析,数据变化趋势与通行时间大致相同。指定直线距离(10m,100km)范围内的为正常数据。

    1
    2
    3
    4
    5
    plt.plot(Thurdata['LD'],'.',c='royalblue',alpha=0.6)
    plt.ylim(10,100000)
    plt.xlabel('sample')
    plt.ylabel('LineDistance(m)')
    plt.title('LineDistance of all normal(10m,100km) samples(no sort)');
    1
    2
    3
    4
    5
    6
    LineDistance=np.array(Thurdata.LD)
    distance =np.sort(LineDistance[(LineDistance<100000)&(LineDistance>10)])
    plt.plot(distance,'.',c='royalblue')
    plt.xlabel('sample')
    plt.ylabel('LineDistance(m)')
    plt.title('LineDistance of all normal(10m,100km) samples');
    1
    2
    3
    4
    plt.hist(distance,bins=50,facecolor='royalblue',edgecolor='k')#滤除时间间隔
    plt.ylabel('numberofsample')
    plt.xlabel('LineDistance(m)')
    plt.title('The histogram of the normal LineDistance(10m,100km)');

    Step5 回归分析直线距离与通行时间之间的关系

    在以上分析中,我们不难推测 距离与通行时间 可能存在一定线性关系。这一推测在实际运用中不难解释,在不考虑拥堵等外部因素影响及圈运行的情况下,距离约远,通行时间越长。

    筛选正常数据用于回归 (正常的含义:timeinterval、LineDistance、endtime正常)

    1
    Regdata=Thurdata.loc[(Thurdata.LD<100000)&(Thurdata.LD>10)&(Thurdata.TimeInterval<14400)&(Thurdata.TimeInterval>180)]

    计算各个变量间的相关系数,通行时间与直线距离间的相关系数达0.708670,可见二者相关性强。因此,选取 直线距离 通行时间 进行线性回归,并通过此线性回归关系填补异常的结束时间数据。

    1
    2
    3
    4
    5
    #正常数据的直线距离与通行时间散点分布图
    plt.plot(Regdata.LD,Regdata.TimeInterval,'.',c='k',alpha=0.6)
    plt.xlabel('LineDistance')
    plt.ylabel('timeinterval')
    plt.title('The relationship between LineDistance and Timeinterval');

    可以使用sklearn的回归函数功能进行回归分析,此时R方约为0.5,回归效果如下图所示:

    1
    2
    3
    4
    5
    6
    7
    8
    from sklearn.linear_model import LinearRegression
    #regr为回归过程,fit(x,y)进行回归
    x=np.array(Regdata.LD).reshape(-1, 1)
    y=np.array(Regdata.TimeInterval).reshape(-1, 1)
    regr = LinearRegression().fit(x,y)
    plt.plot(x,y,'.',color='black',alpha=0.6)
    #用predic预测,这里预测输入x对应的值,进行画线
    plt.plot(x, regr.predict(x), color='red', linewidth=1);

    此时我们可以,利用以上线性回归关系,预测出行时间,与starttime求和得到endtime,将缺失endtime的问题数据补全。之后将原本的正常数据(范围正常、无缺失)与补全后的问题数据合并得到新数据。

    1
    2
    3
    errordata=Thurdata.loc[(Thurdata.judge==1)&(Thurdata.LD<100000)&(Thurdata.LD>10)]
    errordata.loc[:,'TimeInterval']=regr.predict(np.array(errordata.LD).reshape(-1, 1))#注意使用sklearn需要reshape
    errordata.TimeInterval= errordata.TimeInterval.apply(np.round)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    def fillendtime(data1,interval):
    d1 = datetime.datetime.strptime(data1,'%m/%d/%Y %H:%M')
    delta = datetime.timedelta(seconds=interval)
    d2 = d1 + delta
    d2 = d2.strftime('%m/%d/%Y %H:%M')
    return d2

    def getendtime(arrLike): #用来计算日期间隔天数的调用的函数
    s_time = arrLike['s_time']
    interval = arrLike['TimeInterval']
    e_time = fillendtime(s_time.strip(),interval) #注意去掉两端空白
    return e_time
    errordata['e_time'] =errordata.apply(getendtime,axis=1)#结束时间
    Thursnewdata = pd.concat([Regdata,errordata])
    # Thursnewdata.to_csv('ThursuseLD.csv') #保存现状csv文件

    (PS:由于路网规模较大 ,以下求算最短路过程大约需要4-5小时,因此可直接跳过以下回归步骤进入聚类部分)

    Step6 结合地理信息(路网)求算两点间的最短路网距离

    由于两点间的直线距离与两点间通过路网连通的实际距离存在差异,因此考虑结合实际的GIS信息数据,寻找起终点间的最短路网距离。并试图建立 路网最短距离与通行时间 之间的关系,从而填补异常数据。

    使用第三方模块osmnx可以帮助我们在线获取某一区域的底图。获取上海市路网,此过程大约用时20min,时间与路网规模有关。获取所得的网络如下图所示。可以保存为shp或SVG等格式的文件以便下次使用。

    1
    2
    3
    place = 'Shanghai,China'
    Shanghai = ox.graph_from_place(place,which_result=2)
    ox.plot_graph(Shanghai)

    networkx第三方模块给了我们求算路网间两点最短路的工具,因此我们需要进行:

    Step1 :地图匹配:找到位于路网上的与起终点距离最近的点

    Step2 :计算最短距离:计算Step1中路网上两点间的距离

    1
    2
    3
    4
    start_long = np.array(Thursnewdata.s_long).reshape(-1,1)
    start_la = np.array(Thursnewdata.s_la).reshape(-1,1)
    end_long = np.array(Thursnewdata.e_long).reshape(-1,1)
    end_la = np.array(Thursnewdata.e_la).reshape(-1,1)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import networkx as nx
    shortestroutelength=[]
    for i in range(start_long.shape[0]):
    origin_point = (start_la[i][0], start_long[i][0])
    destination_point = (end_la[i][0], end_long[i][0])
    origin_node = ox.get_nearest_node(Shanghai, origin_point)
    destination_node = ox.get_nearest_node(Shanghai, destination_point)
    try:
    length = nx.shortest_path_length(Shanghai, origin_node, destination_node, weight='length')
    except Exception as e:
    shortestroutelength.append(-1)
    else:
    shortestroutelength.append(length)
    1
    2
    Thursnewdata.SD = shortestroutelength
    #Thursnewdata.to_csv('ThursSD.csv') #保存最短路计算结果文件

    Step7 回归分析路网最短距离与通行时间的关系并填充异常数据

    以下回归过程与Step5同理。

    1
    2
    3
    4
    5
    6
    7
    Regdata2=Thursnewdata.loc[Thursnewdata.judge==0]#挑选正常数据用于回归
    x2=np.array(Regdata2.SD).reshape(-1, 1)
    y2=np.array(Regdata2.TimeInterval).reshape(-1, 1)
    regr2 = LinearRegression().fit(x2,y2)
    plt.plot(x2,y2,'.',color='black',alpha=0.6)
    #用predic预测,这里预测输入x对应的值,进行画线
    plt.plot(x2, regr2.predict(x2), color='g', linewidth=1);
    1
    2
    3
    4
    5
    6
    errordata2=Thursnewdata.loc[Thursnewdata.judge==1]
    errordata2.loc[:,'TimeInterval']=regr2.predict(np.array(errordata2.SD).reshape(-1, 1))#注意使用sklearn需要reshape
    errordata2.TimeInterval= errordata2.TimeInterval.apply(np.round)
    errordata2['e_time'] =errordata2.apply(getendtime,axis=1)#结束时间
    ThursnewdataSD = pd.concat([Regdata2,errordata2])
    # ThursnewdataSD.to_csv('ThursuseSD.csv')

    4. 聚类分析

    4.1 聚类方法选取