DCIC-赛题二赛后总结

赛题说明

赛题名称

A城市巡游车与网约车运营特征对比分析

在这里插入图片描述

赛题说明

参赛者需依据赛事方提供的出租车(包括巡游车和网约车)GPS和订单数据

  1. 一是综合应用统计分析方法分别对所提供的巡游车和网约车运营的时间、空间分布特征进行量化计算,包括计算2年的每年工作日取日平均,非工作日取日平均和节假日取日平均,三种类型各自平均的时变分布变化,三种时间类型按网格划分的平均空间分布(网格划分颗粒度选手自选),并分别对比分析所提供的网约车、巡游车,计算2年每年按工作日取日平均,非工作日取日平均和节假日取日平均三种类型的日均空驶率、订单平均运距、订单平均运行时长、上下客点分布密度等时变特性
  2. 二是根据巡游车和网约车的时空运营特征,并尝试对巡游车与网约车的融合发展提出相关建议。在分析过程,参赛者必须用到但不局限于提供的数据,可自行加入自有数据进行参赛,但需说明自带数据来源并保证数据合法合规使用

任务解析

每年工作日取日平均,非工作日取日平均和节假日取日平均,三种情况下出租车&网约车:

  1. 运营时间规律:出车时间和运行时间;

  2. 空间分布规律:城市分布规律,订单分布规律;

  3. 日均空驶率:空驶里程(没有载客)在车辆总运行里程中所占的比例;

  4. 订单平均运距:订单平均距离计算;

  5. 订单平均运行时长:订单平时时长计算;

  6. 上下客点分布密度:上下车位置分布;

  7. 对出租车和网约车的调度、融合发展提出建议,比如如何进行订单调度?识别打不到车的位置

数据清单

  1. 2019年端午节前后10天(5月31日至6月9日)A城市巡游车和网约车的GPS数据和订单数据
  2. 2020年端午节前后10天(6月18日至6月27日)A城市巡游车和网约车的GPS数据和订单数据

在这里插入图片描述

算法分析

这里将着重介绍空驶率,订单时长,运距和上下客地点的分析

1.1 巡游车日均空驶率计算

这里以2019年为例,首先载入全部数据的名称:

path_list= ['taxiOrder20190531.csv',
            'taxiOrder20190601.csv',
            'taxiOrder20190602.csv',
            'taxiOrder20190603.csv',
            'taxiOrder20190604.csv',
            'taxiOrder20190605.csv',
            'taxiOrder20190606.csv',
            'taxiOrder20190607.csv',
            'taxiOrder20190608.csv',
            'taxiOrder20190609.csv']

以下是每天运营的巡游车数目(对gps数据进行查看很容易可得出):

nums_car = [6765, 6751, 6714, 6723, 6765, 6761, 6764, 6738, 6737, 6716]

下面对每一天的空驶率进行计算,计算公式如下:

在这里插入图片描述

L1表示某天的所有空驶里程,L2表示某天的所有行驶里程,n表示天数。注意上面这个公式所计算的是单个巡游车的空驶率。也就是说我们需要筛选出每一个巡游车的数据,分别计算其空驶率,最后再平均才是当日所有巡游车的空驶率。在这里,单个巡游车的空驶率公式为:

NOPASS_MILE/(PASS_MILE+NOPASS_MILE)

另外还要注意有些车并不存在在订单里,但这些车的确运营了,那就说明这些车当天并没有等到客,也就是说一直都是空驶,那么他们的空驶率为1,因此最后在计算的时候得加上这些车。以下为计算的代码:

day_mean =[]
for i in tqdm(range(len(path_list))):
    taxiOrder = pd.read_csv(INPUT_PATH + path_list[i])
    taxiOrder = taxiOrder.rename(columns={'CAR_NO':'CARNO'})
    taxiOrder.sort_values(by=['CARNO'], inplace=True)
    taxiOrder.reset_index(inplace=True, drop=True)
    car_list = taxiOrder['CARNO'].unique()
    pert_list =[]
    for car in car_list:
        single_car = taxiOrder[taxiOrder['CARNO']==car]
        pert = single_car['NOPASS_MILE'].sum()/(single_car['NOPASS_MILE'].sum()+single_car['PASS_MILE'].sum())
        pert_list.append(pert)
    pert_list.extend([1]*(nums_car[i]-len(pert_list)))
    print("订单空驶率",np.mean(pert_list))
    day_mean.append(pert_list)   

计算完每天的空驶率,也就可以得到相应的工作日,周末和端午节日均空驶率。

1.2 网约车空驶率部分代码分析

这里我们以2020年为例:

其实网约车和巡游车的空驶率计算方法相似,但是在网约车的数据里有很多的脏数据,需要进行处理。比如在网约车的订单运距DRIVE_MILE一列出现了字符类型,在将这些行的数据筛选出来后发现,这些数据是完全的脏数据,可以直接删除。

另外在WAIT_MILE一列中有很多缺失数据。通过数据分析和生活实际我们可以判断其可以直接以0进行代替。因为对于网约车来说,很多是载客过程中已经接到订单,所有空驶距离没法上报,设为空值。但很明显这里空驶距离为0。

具体空驶率计算就是WAIT_MILE/(DRIVE_MILE+WAIT_MILE),其他就与巡游车一致。以下列出网约车数据处理部分代码:

day_mean =[]
for i in range(len(path_list)):
    wycOrder = pd.read_csv(INPUT_PATH + path_list[i],usecols=[2,11,13])
    wycOrder = wycOrder.rename(columns={'CAR_NO':'CARNO'})
    wycOrder = wycOrder[wycOrder['DRIVE_MILE'].apply(lambda x: '-' not in str(x) and '|' not in str(x) and 
                                                                       '路' not in str(x))]
    wycOrder['DRIVE_MILE'] = wycOrder['DRIVE_MILE'].astype(float)
    wycOrder['WAIT_MILE'] = wycOrder['WAIT_MILE'].astype(float)
    wycOrder = wycOrder.fillna(0)
    wycOrder.sort_values(by=['CARNO'], inplace=True)
    wycOrder.reset_index(inplace=True, drop=True)
    car_list = wycOrder['CARNO'].unique()
    pert_list =[]
    for car in tqdm(car_list):
        single_car = wycOrder[wycOrder['CARNO']==car]
        pert = (single_car['WAIT_MILE'].sum()+0.0001)/(single_car['WAIT_MILE'].sum()+single_car['DRIVE_MILE'].sum()+0.0001)
        pert_list.append(pert)
    pert_list.extend([1]*(nums_car[i]-len(pert_list)))
    print("订单空驶率",np.mean(pert_list))
    day_mean.append(pert_list)   
1.3 结果与结果分析

在这里插入图片描述
在这里插入图片描述

从上图可以发现,网约车的空驶率要明显低于巡游车的空驶率。根据本人推测以及结合实际情况的分析,巡游车与网约车空驶率这么大的差距源于两者的营业方式。网约车一般都是接收的网络订单,很多情况下并不会在空车情况下到处寻找是否有在路边扬招的乘客。而巡游车则不同,巡游车很少有网络订单,在空车情况下往往会去路上来回空驶,寻找扬招的乘客。当然由于其统一的外观也比较容易接到扬招的乘客。因此,正是上面的差别导致了网约车在空车情况下不会过多的进行空驶,只需要等待网络订单即可,而巡游车在空车情况下往往会进行大量的空驶来寻找乘客。

在端午假期期间的空驶率要高于其他两个时间段的空驶率,可能由于端午假期期间人流量相对往常要少很多,再加上假期期间不限号,很多人选择私家车出行,也导致出租车网约车接不到客从而空驶的情况。

2020年的空驶率要微高于2019年的,可能是由于今年疫情影响,尤其是对于巡游车,客户不会聚集等车而是分散,所以使得空驶率提高。

2.1 订单运距与时长计算

通过分析发现,巡游车的数据较为干净。所以这里计算订单的平均运距直接将订单数据中的PASS_MILE那一列数据求平均。同样的平均时长直接是下车时间减去上车时间然后求平均。以下是相关代码:

# 巡游车计算
def cal_taxi(df):
    df['GETON_DATE'] = pd.to_datetime(df['GETON_DATE'])
    df['GETOFF_DATE'] = pd.to_datetime(df['GETOFF_DATE'])

    print(df['PASS_MILE'].mean(),'|',
        ((df['GETOFF_DATE'] - df['GETON_DATE']).dt.seconds / 60.0).mean())

对于网约车平均运距的计算也相似,但是需要对数据进行预处理,在上面计算空驶率的时候我们已经了解到,在网约车的DRIVE_MILE一列有一些脏数据,那么和上面同样的处理方法,直接删除然后对这一列求平均即可得到平均运距

对于网约车订单平均时长的计算也是一样,但是在分析数据时我们发现有很多条数据的上车时间DEP_TIME竟然小于DEST_TIME,这明显是错误的数据。但是查看他们的行驶时间会发现,他们的行驶时间都很大。这里根据实际情况我们猜测该订单其实跨越了两天,因此这里我们直接使用行驶时间作参考。最后得到的订单时长中,我们又将超出一天的订单时间改为一天时间,记为1440分钟。以下为网约车订单平均运距和平均时长的代码:

def cal_wyc(df):
    df = df[['DEST_TIME', 'DEP_TIME', 'DRIVE_MILE']].dropna()

    if df['DEST_TIME'].dtype != np.int64:
        df = df[df['DEST_TIME'].apply(len) == 14]
        df = df[df['DEST_TIME'].apply(lambda x: x.isdigit())]

    df['DEP_TIME'] = pd.to_datetime(df['DEP_TIME'], format='%Y%m%d%H%M%S')
    df['DEST_TIME'] = pd.to_datetime(df['DEST_TIME'], format='%Y%m%d%H%M%S')

    df = df[df['DRIVE_MILE'].apply(lambda x: '-' not in str(x) and '|' not in str(x) and 
                                                                       '路' not in str(x))]
    df['DRIVE_MILE'] = df['DRIVE_MILE'].astype(float)
    driveMile_mean = df['DRIVE_MILE'].dropna().mean()
    
    df['dev'] = (df['DEST_TIME'] - df['DEP_TIME']).dt.seconds / 60.0
    df.loc[df['DEST_TIME']<df['DEP_TIME'],'dev'] = 0
    df.loc[df['dev']>1440,'dev']=1440

    print(driveMile_mean,'|',
          df['dev'].mean())
2.2 结果与结果分析

在这里插入图片描述

在这里插入图片描述

网约车的平均运距要显著高于巡游车,这是很好理解的,网约车在用户下单后去接用户那段距离也是算在订单运距里面的。而且巡游车一般不跑远路,这也是一个原因。

网约车数据方面,可以直观看出端午假期期间的运距高于其他两个假期,这可能是由于端午假期很多人出门旅游或者回乡探亲的原因。

另外2020年巡游车的订单平均运距普遍低于2019年,而2020网约车的订单平均运距高于2019年

在这里插入图片描述

在这里插入图片描述

从上面两图可以发现网约车的订单时长明显高于巡游车。而且两车在工作日的订单时长都要高于其他的两个时段

3.1 上下客地点可视化实现

这里对于上下客分布地点的分析我们采用了两种可视化分析方案,一是对热门下车地区进行可视化,而是对上下客地点分布密度进行可视化。两者我们都使用了python中的folium库。

以2020年的巡游车上车点为例,首先读取数据,这里因为我们仅仅需要上下客地点的经纬度,所以只读取相关的列即可:

path_list= [  'taxiOrder20200618.csv',

              'taxiOrder20200619.csv',

              'taxiOrder20200620.csv',

              'taxiOrder20200621.csv',

              'taxiOrder20200622.csv',

              'taxiOrder20200623.csv',

              'taxiOrder20200624.csv',

              'taxiOrder20200625.csv',

              'taxiOrder20200626.csv',

              'taxiOrder20200627.csv']

taxiOrder2020 = pd.concat([

  pd.read_csv(INPUT_PATH + x,usecols = [0,2,3,5,6]) for x in path_list

])

接着我们需要对数据进行筛选,删除经纬度异常的数据,然后对于经纬度我们还选取4为有效小数,是为后面可视化分布密度服务的。其实这里也可以用geohash,效果相似。

taxiOrder2020[['GETON_LONGITUDE','GETON_LATITUDE','GETOFF_LONGITUDE','GETOFF_LATITUDE']] = taxiOrder2020[['GETON_LONGITUDE','GETON_LATITUDE','GETOFF_LONGITUDE','GETOFF_LATITUDE']].round(4)

taxiOrder2020 = taxiOrder2020.loc[(taxiOrder2020['GETON_LONGITUDE']>110)&

                                 (taxiOrder2020['GETON_LONGITUDE']<128)&

                                 (taxiOrder2020['GETON_LATITUDE']>15)&

                                 (taxiOrder2020['GETON_LATITUDE']<35),:]

接下来对相同的上车地点进行聚合,以条目为统计值,这样我们可以获得相近区域(因为取了四位小数)的上车点的订单数,将其排序即可得到热门的上车点。然后选取前100个上车地点并标记到地图上即可实现对热门下车点的可视化。

taxi_grouped_geton = taxiOrder2020[['CAR_NO','GETON_LONGITUDE','GETON_LATITUDE']].groupby(by = ['GETON_LONGITUDE','GETON_LATITUDE']).count().sort_values(by ='CAR_NO',ascending = False)

taxi_grouped_lonla = pd.DataFrame({'GETON_LATITUDE':[item[1] for item in taxi_grouped_geton.index[:100]],

                                  'GETON_LONGITUDE':[item[0] for item in taxi_grouped_geton.index[:100]]})

 

map_hooray = folium.Map(location=[24.482426, 118.1], zoom_start=12)


tooltip = 'Click me!'

i = 1

for tup in taxi_grouped_lonla[['GETON_LATITUDE','GETON_LONGITUDE']].values:

  folium.Marker(tup, popup="rank:"+str(i), tooltip=tooltip).add_to(map_hooray)

  i+=1

map_hooray

接着从上面聚合的数据taxi_grouped_geton,我们可以提取出密度特征,我们,我们根据相同地点出现的条数,一次又一次的往地图上画点,那么出现多的地点周围颜色就深,反之出现少的地点就浅,这样就可以实现下车地点分布密度的可视化。

这里并不建议根据数据中所有的地点信息来在地图上实现密度可视化,因为数据量很大,这样做就会使得地图上全被画满了,这样的可视化结果几乎没有区分度,看不出各个地区的密度对比。

GETON_LONGITUDE_list =[]

GETON_LATITUDE_list = []

for i in range(3000):

  times = int(taxi_grouped_geton['CAR_NO'].values[i]/100)

  GETON_LONGITUDE_list.extend([taxi_grouped_geton.index[i][0]]*times)

  GETON_LATITUDE_list.extend([taxi_grouped_geton.index[i][1]]*times)

df_heat_map = pd.DataFrame({'GETON_LATITUDE': GETON_LATITUDE_list,

                            'GETON_LONGITUDE':GETON_LONGITUDE_list})

 

map_hooray = folium.Map(location=[24.482426, 118.1], zoom_start=12)

HeatMap(df_heat_map[['GETON_LATITUDE','GETON_LONGITUDE']].values).add_to(map_hooray)

map_hooray
3.2 可视化示例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V3p4VAhS-1602447610709)(G:\比赛\DCIC\DCIC_image\image_taxi_2020_getofftop.png)]

在这里插入图片描述

融合发展建议

  1. 由于网约车数据中包含很多脏数据,我在数据分析过程中碰到了不少麻烦,所以我建议加大对网约车的信息跟踪与核实

  2. 从上面那些量化指标我们可以得出这样的结论,在可预见的未来,由于打车软件的越来越普及以及网约车方便快速,出租车也就是巡游车行业会受到巨大冲击,因此有关部门可以考虑对其进行转型。具体方案如下两种:

    • 如果条件允许的话,在每一片区域为巡游车设置固定的停靠点,当巡游车结束一个订单后应立即驶向附近的停靠点,而不是在路上空驶,增加交通压力。而乘客需要乘车时,没必要在路边扬招,而是去附近的固定停车点乘车即可。

    • 上面这个方案有一个缺点就是乘客失去了便捷,因此这里可以参考网约车的优势,将巡游车的下单方式也采取网上进行。也就是巡游车还是停在固定的停靠点,当乘客在网络上下单后,离他最近的一个停靠点会派一辆巡游车来接这位乘客。当乘客到达目的地后,同样的该巡游车需要驶往最近的停靠点。

收获

folium库的使用

建立一个基础地图:


import folium

#location 表示显示的中心地点,zoom_start为初始显示的缩放比例
m = folium.Map(location=[45.5236, -122.6750],zoom_start=12)
m

保存该地图:

m.save('index.html')

画标记点以及热度图见算法分析部分的3.1

Geohash地点表示

GeoHash将二维的经纬度转换成字符串,比如下图展示了北京9个区域的 GeoHash字符串,每一个字符串代表了某一矩形区域。 字符串越长,表示的范围越精确。

在这里插入图片描述

使用Geohash的优点在于

  1. 既能表明用户位于某个地区附近,又不至于暴露用户的精确坐标,有助于隐私保护。

  2. 也有助于热力图展示,地址聚类等

python里的使用方法:

import geohash
#将经纬度转换为geohash编码,precision的值为输出编码的长度
taxiorder2019['geohash'] = taxiorder2019.apply(lambda x: geohash.encode(x['GETON_LATITUDE'], x['GETON_LONGITUDE'], precision=8), axis=1)
taxiorder2019['geohash'].value_counts().head()

out:
    s0000000    6335
    ws7unv0q     808
    wsk584c9     746
    ws7grb9s     604
    ws7unv0r     603

解码一个GeoHash编码示例:

>>> print 'Coordinate for Geohash ezs42:', Geohash.decode('ezs42')
Coordinate for Geohash ezs42: ('42.6', '-5.6')

Geohash模块还提供精确的解码,并带有误差余量结果。 encode_exactly函数返回四个浮点值的元组。 纬度,经度,纬度误差幅度,经度误差幅度:

>>> print 'Exact coordinate for Geohash ezs42:\n', Geohash.decode_exactly('ezs42')
Exact coordinate for Geohash ezs42:
(42.60498046875, -5.60302734375, 0.02197265625, 0.02197265625)

其实这里的误差幅度也就是表示Geohash编码所表示的那个正方形区域。

参考文献

  1. Coggle数据学习。URL: https://coggle.club/learn/dcic2020/
  2. GeoHash核心原理解析。 URL: https://www.cnblogs.com/LBSer/p/3310455.html
  3. GeoHash文档。 URL: https://libraries.io/pypi/Geohash
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值