器→工具, 工具软件, 开源项目, 数据, 术→技巧

交通数据可视化包TransBigData

钱魏Way · · 29 次浏览

TransBigData简介

TransBigData是一个为交通时空大数据处理、分析和可视化而开发的Python包。TransBigData为处理常见的交通时空大数据(如出租车GPS数据、共享单车数据和公交车GPS数据)提供了快速而简洁的方法。TransBigData为交通时空大数据分析的各个阶段提供了多种处理方法,代码简洁、高效、灵活、易用,可以用简洁的代码实现复杂的数据任务。对于一些特定类型的数据,TransBigData还提供了针对特定需求的工具,如从出租车GPS数据中提取出租车行程的起点和终点信息(OD),从公交车GPS数据中识别到离站信息。

技术特点

  • 面向交通时空大数据分析不同阶段的处理需求提供不同处理功能。
  • 代码简洁、高效、灵活、易用,通过简短的代码即可实现复杂的数据任务。

主要功能

目前,TransBigData主要提供以下方法:

  • 数据质量分析: 提供快速获取数据集一般信息的方法,包括数据量、时间段和采样间隔。
  • 数据预处理: 提供清洗多种类型的数据错误的方法。
  • 数据栅格化: 提供在研究区域内生成多种类型的地理网格(矩形网格、六角形网格)的方法。提供快速算法将GPS数据映射到生成的网格上。
  • 数据聚合集计: 提供将GPS数据和OD数据聚合到地理多边形的方法。
  • 数据可视化: 内置的可视化功能,利用可视化包keplergl,用简单的代码在Jupyter笔记本上交互式地可视化数据。
  • 轨迹数据处理: 提供处理轨迹数据的方法,包括从GPS点生成轨迹线型,轨迹增密等。
  • 地图底图: 提供在matplotlib上显示Mapbox地图底图的方法。

TransBigData核心方法

数据栅格化

transbigdata.area_to_grid(location, accuracy=500, method='rect', params='auto')

在边界或形状中生成矩形栅格

参数:

  • location(bounds(List) or shape(GeoDataFrame)) – 生成栅格的位置。如果边界为 [lon1, lat1, lon2, lat2](WGS84),其中 lon1 , lat1 是左下角坐标,lon2 , lat2 是右上角坐标 如果是形状,则应为 GeoDataFrame
  • accuracy(number) – 栅格尺寸(米)
  • method(str) – 直角、三角或六角
  • params(list or dict) – 栅格化参数。给出格网参数时,将不使用精度。

返回:

  • grid(GeoDataFrame) – Grid GeoDataFrame、LONCOL 和 LATCOL 是栅格的索引,HBLON 和 HBLAT 是栅格的中心
  • 参数(列表或字典) – 栅格参数。
transbigdata.area_to_params(location, accuracy=500, method='rect')

生成栅格化参数

参数:

  • location(bounds(List) or shape(GeoDataFrame)) – 生成栅格的位置。如果边界为 [lon1, lat1, lon2, lat2](WGS84),其中 lon1 , lat1 是左下角坐标,lon2 , lat2 是右上角坐标 如果是形状,则应为 GeoDataFrame
  • accuracy(number) – 栅格尺寸(米)
  • method(str) – 直角、三角或六角

返回:

  • 参数 – 栅格参数。。

返回类型:

  • list or dict
transbigdata.GPS_to_grid(lon, lat, params)

将 GPS 数据与栅格匹配。输入是经度、纬度和格网参数列。输出是栅格 ID。

  • 参数:
  • lon(Series) – 经度栏
  • lat(Series) – 纬度栏
  • params(list or dict) – 栅格化参数。

返回:

  • 矩形栅格,[LONCOL,LATCOL](list) – 两列 LONCOL 和 LATCOL 一起可以指定栅格。
  • 三角形和六边形栅格,[loncol_1,loncol_2,loncol_3](list) – 栅格纬度的索引。两列 LONCOL 和 LATCOL 一起可以指定一个栅格。
transbigdata.grid_to_centre(gridid, params)

栅格的中心位置。输入是格网ID和参数,输出是格网中心位置。

参数:

  • gridid(list) – 如果“矩形栅格”[LONCOL,LATCOL]:系列 两列 LONCOL 和 LATCOL 一起可以指定栅格。 如果“三角形和六边形栅格” [loncol_1,loncol_2,loncol_3] :系列 栅格纬度的索引。两列 LONCOL 和 LATCOL 一起可以指定一个栅格。
  • params(list or dict) – 栅格化参数。

返回:

  • HBLON(Series) – 栅格中心的经度
  • HBLAT(Series) – 栅格中心的纬度

transbigdata.grid_to_polygon(gridid, params)

基于格网 ID 生成几何列。输入是栅格 ID,输出是几何图形。支持矩形、三角形和六边形栅格

参数:

  • gridid(list) – 如果“矩形栅格”[LONCOL,LATCOL]:系列 两列 LONCOL 和 LATCOL 一起可以指定栅格。 如果“三角形和六边形栅格” [loncol_1,loncol_2,loncol_3] :系列 栅格纬度的索引。两列 LONCOL 和 LATCOL 一起可以指定一个栅格。
  • params(list or dict) – 栅格化参数。

返回:

  • geometry– 栅格地理多边形的列

返回类型:

  • Series
transbigdata.grid_to_area(data, shape, params, col=['LONCOL', 'LATCOL'])

输入格网 ID 两列、地理面和格网参数。输出是栅格。

参数:

  • data(DataFrame) – 数据,有两列栅格ID
  • shape(GeoDataFrame) – 地理多边形
  • params(list or dict) – 栅格化参数。
  • col(List) – 列名称 [LONCOL,LATCOL] 用于矩形栅格,或 [loncol_1,loncol_2,loncol_3] 表示三和六栅格

返回:

  • data1– 数据栅格化和映射到相应的地理多边形

返回类型:

  • DataFrame
transbigdata.grid_to_params(grid)

从栅格重新生成栅格化参数。

参数:

  • grid(GeoDataFrame) – transbigdata生成的栅格

返回:

  • 参数 – 栅格参数。

返回类型:

  • list or dict
transbigdata.grid_params_optimize(data, initialparams, col=['uid', 'lon', 'lat'], optmethod='centerdist', printlog=False, sample=0, pop=15, max_iter=50, w=0.1, c1=0.5, c2=0.5)

优化栅格参数

参数:

  • data(DataFrame) – 轨迹数据
  • initialparams(List) – 初始栅格参数
  • col(List) – 列名[uid,lon,lat]
  • optmethod(str) – 优化方法:centerdist, gini, gridscount
  • printlog(bool) – 是否打印明细结果
  • sample(int) – 采样数据作为输入,为0则不采样
  • pop– 来自 scikit-opt 的 PSO 中的参数
  • max_iter– 来自 scikit-opt 的 PSO 中的参数
  • w– 来自 scikit-opt 的 PSO 中的参数
  • c1– 来自 scikit-opt 的 PSO 中的参数
  • c2– 来自 scikit-opt 的 PSO 中的参数

返回:

  • params_optimized– 优化的参数

返回类型:

  • List

数据可视化

数据点分布可视化

transbigdata.visualization_data(data, col=['lon', 'lat'], accuracy=500, height=500, maptype='point', zoom='auto')

输入是数据点,此函数将聚合然后可视化

参数:

  • data(DataFrame) – 数据点
  • col(List) – 列名。用户可以按[经度,纬度]的顺序选择非权重的起点-目的地(OD)数据。为此,聚合是自动的。或者,用户也可以输入加权OD数据,按[经度、纬度、计数]的顺序排列。
  • zoom(number) – 地图缩放级别(可选)。
  • height(number) – 地图框的高度
  • accuracy(number) – 栅格大小
  • maptype(str) – 地图类型,‘点’或‘热图’

返回:

  • vmap– keplergl 提供的可视化

返回类型:

  • keplergl.KeplerGl

轨道可视化

transbigdata.visualization_trip(trajdata, col=['Lng', 'Lat', 'ID', 'Time'], zoom='auto', height=500)

输入是轨迹数据和列名称。输出是基于开普勒的可视化结果

参数:

  • trajdata(DataFrame) – 轨迹点数据
  • col(List) – 列名称,按 [经度、纬度、车辆 ID、时间] 的顺序排列
  • zoom(number) – 地图缩放级别
  • height(number) – 地图框的高度

返回:

  • vmap– keplergl 提供的可视化

返回类型:

  • keplergl.KeplerGl

OD可视化

transbigdata.visualization_od(oddata, col=['slon', 'slat', 'elon', 'elat'], zoom='auto', height=500, accuracy=500, mincount=0)

输入是 OD 数据和列。输出是基于开普勒的可视化结果

参数:

  • oddata(DataFrame) – 外径数据
  • col(List) – 列名。用户可以按[原点经度、原点纬度、目的地经度、目的地纬度]的顺序选择非权重的起点-目的地(OD)数据。为此,聚合是自动的。或者,用户也可以输入加权OD数据,按[原点经度、原点纬度、目的地经度、目的地纬度、计数]的顺序排列。
  • zoom(number) – 地图缩放级别(可选)。
  • height(number) – 地图框的高度
  • accuracy(number) – 栅格大小
  • mincount(number) – 最小OD数,OD数少的不显示

返回:

  • vmap– keplergl 提供的可视化

返回类型:

  • keplergl.KeplerGl

活动分析

transbigdata.plot_activity(data, col=['stime', 'etime', 'group'], figsize=(10, 5), dpi=250, shuffle=True, xticks_rotation=0, xticks_gap=1, yticks_gap=1, fontsize=12)

绘制个体的活动图

参数:

  • data(DataFrame) – 一个人的活动信息
  • col(List) – 列名[starttime,endtime,group],`group`控制颜色分组
  • figsize(List) – 体形尺寸
  • dpi(Number) – 图形的dpi
  • shuffle(bool) – 是否将活动顺序随机打乱
  • xticks_rotation(Number) – xticks的旋转角度
  • xticks_gap(Number) – xticks 的间隙
  • yticks_gap(Number) – yticks 的间隙
  • fontsize(Number) – xticks 和 yticks 的字体大小

特定数据处理方法

手机数据处理

transbigdata.mobile_stay_duration(staydata, col=['stime', 'etime'], start_hour=8, end_hour=20)

输入停留点数据以识别夜间和白天的持续时间。

参数:

  • staydata(DataFrame) – 停留数据
  • col(List) – 列名,顺序为[‘starttime’,’endtime’]
  • start_hour(Number) – 一天中的开始时间
  • end_hour(Number) – 一天时间的结束时间

返回:

  • duration_night(Series) – 夜间持续时间
  • duration_day(Series) – 白天的持续时间
transbigdata.mobile_identify_home(staydata, col=['uid', 'stime', 'etime', 'LONCOL', 'LATCOL'], start_hour=8, end_hour=20)

从手机停留数据中识别居住地位置。规则是确定夜间持续时间最长的位置。

参数:

  • staydata(DataFrame) – 停留数据
  • col(List) – 列名,按 [‘uid’, ‘stime’, ‘etime’, ‘locationtag1’, ‘locationtag2’, …] 的顺序排列。可以有多个“位置标签”列来指定位置。
  • start_hour(Number) – 一天时间的开始时间和结束时间
  • end_hour(Number) – 一天时间的开始时间和结束时间

返回:

  • home– 手机用户的家位置

返回类型:

  • DataFrame
transbigdata.mobile_identify_work(staydata, col=['uid', 'stime', 'etime', 'LONCOL', 'LATCOL'], minhour=3, start_hour=8, end_hour=20, workdaystart=0, workdayend=4)

从手机停留数据中识别工作地点。规则是确定工作日白天持续时间最长的位置(平均持续时间应超过“minhour”)。

参数:

  • staydata(DataFrame) – 停留数据
  • col(List) – 列名,按 [‘uid’、stime’、’etime’、’locationtag1’、’locationtag2’, …] 的顺序排列。可以有多个“locationtag”列来指定位置。
  • minhour(Number) – 工作日的最短持续时间(小时)。
  • workdaystart(Number) – 一周中工作日的开始和结束。
  • workdayend(Number) – 一周中工作日的开始和结束。
  • start_hour(Number) – 一天时间的开始时间和结束时间
  • end_hour(Number) – 一天时间的开始时间和结束时间

返回:

  • work– 手机用户的工作地

返回类型:

  • DataFrame

出租汽车GPS数据处理

transbigdata.clean_taxi_status(data, col=['VehicleNum', 'Time', 'OpenStatus'], timelimit=None)

从出租车数据中删除乘客携带状态的瞬时变化记录。这些异常记录会影响旅行订单判断。判断方法:如果同一车辆上一条记录和下一条记录的乘客状态与该记录不同,则应删除该记录。

参数:

  • data(DataFrame) – 数据
  • col(List) – 列名,顺序为[‘VehicleNum’, ‘Time’, ‘OpenStatus’]
  • timelimit(number) – 可选,以秒为单位。如果上一条记录和下一条记录之间的时间小于时间阈值,则将删除该记录

返回:

  • data1– 清理后的数据

返回类型:

  • DataFrame
transbigdata.taxigps_to_od(data, col=['VehicleNum', 'Stime', 'Lng', 'Lat', 'OpenStatus'])

输入出租车GPS数据,提取OD信息

参数:

  • data(DataFrame) – 出租车GPS数据
  • col(List) – 列名在数据中,需要按顺序排列[车辆ID、时间、经度、纬度、乘客状态]

返回:

  • oddata– OD信息

返回类型:

  • DataFrame
transbigdata.taxigps_traj_point(data, oddata, col=['Vehicleid', 'Time', 'Lng', 'Lat', 'OpenStatus'])

输入出租车数据和OD数据,提取配送和闲置行程的轨迹点

参数:

  • data(DataFrame) – 出租车GPS数据,col变量指定的字段名
  • oddata(DataFrame) – 出租车OD数据
  • col(List) – 栏目名称,需按顺序排列[车辆ID、时间、经度、纬度、旅客状态]

返回:

  • data_deliver(DataFrame) – 送货行程的轨迹点
  • data_idle(DataFrame) – 空闲行程的轨迹点

共享单车数据处理

transbigdata.bikedata_to_od(data, col=['BIKE_ID', 'DATA_TIME', 'LONGITUDE', 'LATITUDE', 'LOCK_STATUS'], startend=None)

输入共享单车订单数据(仅在锁打开和关闭时生成数据),指定列名称,并从中提取乘车和停车信息

参数:

  • data(DataFrame) – 共享单车订单数据
  • col(List) – 列名,顺序不能更改。[“BIKE_ID”、“DATA_TIME”、“经度”、“纬度”、“LOCK_STATUS]
  • startend(List) – 观察周期的开始时间和结束时间,例如 [‘2018-08-27 00:00:00’、’2018-08-28 00:00:00’]。如果通过,则考虑骑行和停车情况(从观察期开始到自行车首次出现)和(从自行车最后一次出现到观察期结束)。

返回:

  • move_data(DataFrame) – 骑行数据
  • stop_data(DataFrame) – 停车数据

公交GPS数据处理

transbigdata.busgps_arriveinfo(data, line, stop, col=['VehicleId', 'GPSDateTime', 'lon', 'lat', 'stopname'], stopbuffer=200, mintime=300, disgap=200, project_epsg='auto', timegap=1800, projectoutput=False)

输入公交GPS数据、公交线路和车站GeoDataFrame,该方法可以识别公交车的到达和出发信息

参数:

  • data(DataFrame) – 总线全球定位系统数据。它应该是来自一条公交路线的数据,并且需要包含车辆ID,GPS时间,纬度和经度(wgs84)
  • line(GeoDataFrame) – 公交线路的GeoDataFrame
  • stop(GeoDataFrame) – 公交车站的GeoDataFrame
  • col(List) – 列名称,按 [车辆 ID、时间、经度、纬度、车站名称] 的顺序排列
  • stopbuffer(number) – 米。当车辆在此一定距离内接近车站时,视为到达车站。
  • mintime(number) – 秒。在短时间内,巴士再次到达公交车站,将不被视为再次到达
  • disgap(number) – 米。车辆前点和后点之间的距离,用于确定车辆是否在移动
  • project_epsg(number) – 匹配算法将数据转换为投影坐标系来计算距离,这里给出投影坐标系的epsg代码
  • timegap(number) – 秒。车辆没有出现多长时间,它将被视为新车
  • projectoutput(bool) – 是否输出投影数据

返回:

  • arrive_info– 公交到发信息

返回类型:

  • DataFrame
transbigdata.busgps_onewaytime(arrive_info, start, end, col=['VehicleId', 'stopname', 'arrivetime', 'leavetime'])

输入出发信息表drive_info和车站信息表停靠点计算单程行车时间

参数:

  • arrive_info(DataFrame) – 发车信息表drive_info
  • start(Str) – 起始站名
  • end(Str) – 终点站名
  • col(List) – 列名[车号,站名,到达时间,离开时间]

返回:

  • onewaytime– 公交车单程时间

返回类型:

  • DataFrame

TransBigData实战

使用数据集:深圳市2013年10月22日大约600辆出租车轨迹数据。此部分数据可网上获取。

导入要用到的包

import transbigdata as tbd
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
import pprint

tbd.set_mapboxtoken('')
tbd.set_imgsavepath('/home/biaodianfu/')

加载数据

data = pd.read_csv('shenzhen_taxi_gps.csv', header=None)
data.columns = ['VehicleNum', 'Time', 'Lng', 'Lat', 'OpenStatus', 'Speed']
data.head()

输出数据集的一般信息

tbd.data_summary(data, col=['VehicleNum', 'Time'], show_sample_duration=False, roundnum=4)

输出内容为:

Amount of data
-----------------
Total number of data items:  544999
Total number of individuals:  180
Data volume of individuals(Mean):  3027.7722
Data volume of individuals(Upper quartile):  4056.25
Data volume of individuals(Median):  2600.5
Data volume of individuals(Lower quartile):  1595.75

Data time period
-----------------
Start time:  00:00:00
End time:  23:59:59

数据点分布可视化

tbd.visualization_data(data,col = ['Lng','Lat'], accuracy=20)

tbd.visualization_data(data,col = ['Lng','Lat'], accuracy=1000)

可以看到不同精度下数据统计存在差异。

获取深圳的行政区geojson

数据来源:https://datav.aliyun.com/portal/school/atlas/area_selector

sz = gpd.read_file('440300_full.json')
sz.crs = None
sz.head()
sz.plot()

将深圳的区域绘制到地图上

bounds = [113.6, 22.4, 114.8, 22.9] #设定研究范围的边界
fig = plt.figure(1, (6, 6), dpi=800)
ax = plt.subplot(111)
plt.sca(ax)
tbd.plot_map(plt, bounds, zoom=12, style=6)
sz.plot(ax=ax, alpha=0.5)
plt.axis('off');

数据预处理

# 数据预处理,删除深圳区域以外数据
data = tbd.clean_outofshape(data, sz, col=['Lng', 'Lat'], accuracy=500)

# 从出租车数据中删除乘客携带状态的瞬时变化记录。
# 这些异常记录会影响旅行订单判断。
# 判断方法:如果同一车辆上一条记录和下一条记录的乘客状态与该记录不同,则应删除该记录。
data = tbd.clean_taxi_status(data, col=['VehicleNum', 'Time', 'OpenStatus'])

clean_taxi_status的代码实现:

def clean_taxi_status(data, col=['VehicleNum', 'Time', 'OpenStatus'],
                      timelimit=None):
    '''
    Deletes records of instantaneous changes in passenger carrying status from
    taxi data. These abnormal records can affect travel order judgments.
    Judgement method: If the passenger status of the previous record and the
    next record are different from this record for the same vehicle, then this
    record should be deleted.

    Parameters
    -------
    data : DataFrame
        Data
    col : List
        Column names, in the order of [‘VehicleNum’, ‘Time’, ‘OpenStatus’]
    timelimit : number
        Optional, in seconds. If the time between the previous record and the
        next record is less than the time threshold, then it will be deleted

    Returns
    -------
    data1 : DataFrame
        Cleaned data
    '''
    data1 = data.copy()
    [VehicleNum, Time, OpenStatus] = col
    #Sort the VehicleNum and Time columns
    data1 = data1.sort_values(by=[VehicleNum,Time])
    if timelimit:
        data1[Time] = pd.to_datetime(data1[Time])
        data1 = data1[
            -((data1[OpenStatus].shift(-1) == data1[OpenStatus].shift()) &
              (data1[OpenStatus] != data1[OpenStatus].shift()) &
              (data1[VehicleNum].shift(-1) == data1[VehicleNum].shift()) &
              (data1[VehicleNum] == data1[VehicleNum].shift()) &
              ((data1[Time].shift(-1) - data1[Time].shift()
                ).dt.total_seconds() <= timelimit)
              )]
    else:
        data1 = data1[
            -((data1[OpenStatus].shift(-1) == data1[OpenStatus].shift()) &
              (data1[OpenStatus] != data1[OpenStatus].shift()) &
              (data1[VehicleNum].shift(-1) == data1[VehicleNum].shift()) &
              (data1[VehicleNum] == data1[VehicleNum].shift()))]
    return data1

对数据进行栅格化处理

深圳地图栅格化:

grid, params = tbd.area_to_grid(sz) # 栅格参数,accuracy 默认为500米
pprint.pprint(params)
grid.head()

可视化刚才创建的方形栅格:

GPS点栅格化:

data['LONCOL'], data['LATCOL'] = tbd.GPS_to_grid(data['Lng'], data['Lat'], params)
data.head()

统计栅格内的车票数量,并生成数据集:

datatest = data.groupby(['LONCOL', 'LATCOL'])['VehicleNum'].count().reset_index()
datatest['geometry'] = tbd.grid_to_polygon([datatest['LONCOL'], datatest['LATCOL']], params) #生成栅格地理图形
datatest = gpd.GeoDataFrame(datatest)
datatest.head()

绘制栅格后的数据

fig = plt.figure(1, (16, 6), dpi=600)
ax = plt.subplot(111)
# tbd.plot_map(plt, bounds, zoom=14, style=1)
datatest.plot(ax=ax, column='VehicleNum', legend=True)
plt.xticks([], fontsize=10)
plt.yticks([], fontsize=10)
plt.title('Counting of Taxi GPS Trajectory Points', fontsize=12);

在网格基础上添加深圳行政区边界:

fig = plt.figure(1, (16, 6), dpi=600)
ax = plt.subplot(111)
# tbd.plot_map(plt, bounds, zoom=14, style=1)
datatest.plot(ax=ax, column='VehicleNum', legend=True)
sz.plot(ax=ax, edgecolor=(0, 0, 0, 1), facecolor=(0, 0, 0, 0.2), linewidths=0.5)
plt.xticks([], fontsize=10)
plt.yticks([], fontsize=10)
plt.title('Counting of Taxi GPS Trajectory Points', fontsize=12);

改分位数分类法显示数据:

fig = plt.figure(1, (16, 6), dpi=600)
ax = plt.subplot(111)
# tbd.plot_map(plt, bounds, zoom=14, style=1)
datatest.plot(ax=ax, column='VehicleNum', legend=True, scheme='quantiles')
sz.plot(ax=ax, edgecolor=(0, 0, 0, 1), facecolor=(0, 0, 0, 0.2), linewidths=0.5)
plt.xticks([], fontsize=10)
plt.yticks([], fontsize=10)
plt.title('Counting of Taxi GPS Trajectory Points', fontsize=12);

改变图像的图例颜色:

fig = plt.figure(1, (16, 6), dpi=600)
ax = plt.subplot(111)
# tbd.plot_map(plt, bounds, zoom=14, style=1)
datatest.plot(ax=ax, column='VehicleNum', legend=True, cmap='OrRd', scheme='quantiles')
sz.plot(ax=ax, edgecolor=(0, 0, 0, 1), facecolor=(0, 0, 0, 0.2), linewidths=0.5)
plt.xticks([], fontsize=10)
plt.yticks([], fontsize=10)
plt.title('Counting of Taxi GPS Trajectory Points', fontsize=12);

在此基础上添加地图底图:

fig = plt.figure(1, (16, 6), dpi=600)
ax = plt.subplot(111)
tbd.plot_map(plt, bounds, zoom=14, style=1)
datatest.plot(ax=ax, column='VehicleNum', legend=True, cmap='OrRd', scheme='quantiles')
sz.plot(ax=ax, edgecolor=(0, 0, 0, 1), facecolor=(0, 0, 0, 0.2), linewidths=0.5)
plt.xticks([], fontsize=10)
plt.yticks([], fontsize=10)
plt.title('Counting of Taxi GPS Trajectory Points', fontsize=12);

提取OD信息

oddata = tbd.taxigps_to_od(data,col = ['VehicleNum', 'Time', 'Lng', 'Lat', 'OpenStatus'])

taxigps_to_od函数的代码实现:

def taxigps_to_od(data,
                  col=['VehicleNum', 'Stime', 'Lng', 'Lat', 'OpenStatus']):
    '''
    Input Taxi GPS data, extract OD information

    Parameters
    -------
    data : DataFrame
        Taxi GPS data
    col : List
        Column names in the data, need to be in order [vehicle id, time,
        longitude, latitude, passenger status]

    Returns
    -------
    oddata : DataFrame
        OD information
    '''
    [VehicleNum, Stime, Lng, Lat, OpenStatus] = col
    data1 = data[col]
    data1 = data1.sort_values(by=[VehicleNum, Stime])
    data1['StatusChange'] = data1[OpenStatus] - data1[OpenStatus].shift()
    oddata = data1[((data1['StatusChange'] == -1) |
                   (data1['StatusChange'] == 1)) &
                   (data1[VehicleNum].shift() == data1[VehicleNum])]
    oddata = oddata.drop([OpenStatus], axis=1)
    oddata.columns = [VehicleNum, 'stime', 'slon', 'slat', 'StatusChange']
    oddata['etime'] = oddata['stime'].shift(-1)
    oddata['elon'] = oddata['slon'].shift(-1)
    oddata['elat'] = oddata['slat'].shift(-1)
    oddata = oddata[(oddata['StatusChange'] == 1) &
                    (oddata[VehicleNum] == oddata[VehicleNum].shift(-1))]
    oddata = oddata.drop('StatusChange', axis=1)
    oddata['ID'] = range(len(oddata))
    return oddata

可视化OD信息

tbd.visualization_od(oddata,accuracy=3000,height = 600)
# a=tbd.visualization_od(oddata,accuracy=3000)
# a.save_to_html(file_name='od_visua.html')

按栅格进行OD的展示:

params=tbd.area_to_params(bounds,accuracy = 3000)
od_gdf = tbd.odagg_grid(oddata, params)
od_gdf.head()

绘制OD展示信息:

fig = plt.figure(1, (16, 6), dpi=800) 
ax = plt.subplot(111)
plt.sca(ax)
od_gdf.plot(ax=ax, column='count', legend=True,cmap = 'Blues_r')
plt.xticks([], fontsize=10)
plt.yticks([], fontsize=10)
plt.title('OD Trips', fontsize=12);
sz.plot(ax=ax, edgecolor=(0, 0, 0, 1), facecolor=(0, 0, 0, 0.2), linewidths=0.5)
# tbd.plot_map(plt, bounds, zoom=12, style=1)

调整下标尺颜色:

fig = plt.figure(1, (16, 6), dpi=800) 
ax = plt.subplot(111)
plt.sca(ax)
od_gdf.plot(ax=ax, column='count', legend=True,cmap = 'autumn_r')
plt.xticks([], fontsize=10)
plt.yticks([], fontsize=10)
plt.title('OD Trips', fontsize=12);
sz.plot(ax=ax, edgecolor=(0, 0, 0, 1), facecolor=(0, 0, 0, 0.2), linewidths=0.5)
# tbd.plot_map(plt, bounds, zoom=12, style=1)

可以看到上述展示中线路过细,尝试采取按照行政区进行OD的展示:

od_gdf = tbd.odagg_shape(oddata,sz,round_accuracy=6)
od_gdf.plot(column = 'count')

优化可视化内容:

fig = plt.figure(1, (10, 10), dpi=800)
ax = plt.subplot(111)
plt.sca(ax)
#添加地图底图
# tbd.plot_map(plt, bounds, zoom=12, style=1)

#绘制colorbar
cax = plt.axes([0.05, 0.33, 0.02, 0.3])
plt.title('OD\nMatrix')
plt.sca(ax)

#绘制OD
od_gdf.plot(ax=ax, vmax=100, column='count', cax=cax, legend=True)

#绘制小区底图
sz.plot(ax=ax, edgecolor=(0, 0, 0, 1), facecolor=(0, 0, 0, 0.2), linewidths=0.5)

#添加比例尺和指北针
tbd.plotscale(ax, bounds=bounds, textsize=10, compasssize=1, accuracy=2000, rect=[0.06, 0.03], zorder=10)
plt.axis('off')
plt.xlim(bounds[0], bounds[2])
plt.ylim(bounds[1], bounds[3])
plt.show()

车辆载客与空闲的数据展示

data_deliver, data_idle = tbd.taxigps_traj_point(data,oddata,col=['VehicleNum','Time','Lng','Lat','OpenStatus'])

taxigps_traj_point的代码实现:

def taxigps_traj_point(data, oddata,
                       col=['Vehicleid', 'Time', 'Lng', 'Lat', 'OpenStatus']):
    '''
    Input Taxi data and OD data to extract the trajectory points for delivery
    and idle trips

    Parameters
    -------
    data : DataFrame
        Taxi GPS data, field name specified by col variable
    oddata : DataFrame
        Taxi OD data
    col : List
        Column names, need to be in order [vehicle id, time, longitude,
        latitude, passenger status]

    Returns
    -------
    data_deliver : DataFrame
        Trajectory points for delivery trips
    data_idle : DataFrame
        Trajectory points for idle trips
    '''
    VehicleNum, Time, Lng, Lat, OpenStatus = col
    oddata1 = oddata.copy()
    odata = oddata1[[VehicleNum, 'stime', 'slon', 'slat', 'ID']].copy()
    odata.columns = [VehicleNum, Time, Lng, Lat, 'ID']
    odata.loc[:, 'flag'] = 1
    odata.loc[:, OpenStatus] = -1
    ddata = oddata1[[VehicleNum, 'etime', 'elon', 'elat', 'ID']].copy()
    ddata.columns = [VehicleNum, Time, Lng, Lat, 'ID']
    ddata.loc[:, 'flag'] = -1
    ddata.loc[:, OpenStatus] = -1
    data1 = pd.concat([data, odata, ddata])
    data1 = data1.sort_values(by=[VehicleNum, Time, OpenStatus])
    data1['flag'] = data1['flag'].fillna(0)
    data1['flag'] = data1.groupby(VehicleNum)['flag'].cumsum()
    data1['ID'] = data1['ID'].ffill()
    data_deliver = data1[(data1['flag'] == 1) &
                         (-data1['ID'].isnull()) & (data1[OpenStatus] != -1)]
    data_idle = data1[(data1['flag'] == 0) &
                      (-data1['ID'].isnull()) & (data1[OpenStatus] != -1)]
    return data_deliver, data_idle

展示载客行程:

#创建图框
fig = plt.figure(1, (10, 10), dpi=800)
ax = plt.subplot(111)
plt.sca(ax)

#添加地图底图
# tbd.plot_map(plt, bounds, zoom=12, style=1)

#绘制colorbar
cax = plt.axes([0.05, 0.33, 0.02, 0.3])
plt.title('OD\nMatrix')
plt.sca(ax)

#绘制OD
traj_deliver = tbd.traj_to_linestring(data_deliver)
traj_deliver.plot(ax=ax);
#绘制小区底图
sz.plot(ax=ax, edgecolor=(0, 0, 0, 1), facecolor=(0, 0, 0, 0.2), linewidths=0.5)

#添加比例尺和指北针
tbd.plotscale(ax, bounds=bounds, textsize=10, compasssize=1, accuracy=2000, rect=[0.06, 0.03], zorder=10)
plt.axis('off')
plt.xlim(bounds[0], bounds[2])
plt.ylim(bounds[1], bounds[3])
plt.title('Driver carrying passengers', fontsize=12);
plt.show()

司机空载路径:

#创建图框
fig = plt.figure(1, (10, 10), dpi=800)
ax = plt.subplot(111)
plt.sca(ax)

#添加地图底图
#tbd.plot_map(plt, bounds, zoom=12, style=1)

#绘制colorbar
cax = plt.axes([0.05, 0.33, 0.02, 0.3])
plt.title('OD\nMatrix')
plt.sca(ax)

#绘制OD
traj_deliver = tbd.traj_to_linestring(data_idle)
traj_deliver.plot(ax=ax);
#绘制小区底图
sz.plot(ax=ax, edgecolor=(0, 0, 0, 1), facecolor=(0, 0, 0, 0.2), linewidths=0.5)

#添加比例尺和指北针
tbd.plotscale(ax, bounds=bounds, textsize=10, compasssize=1, accuracy=2000, rect=[0.06, 0.03], zorder=10)
plt.axis('off')
plt.xlim(bounds[0], bounds[2])
plt.ylim(bounds[1], bounds[3])
plt.title('Driver without load', fontsize=12);
plt.show()

司机动态轨迹图:

tbd.visualization_trip(data_deliver)  #一天当中的所有载客轨迹
# a=tbd.visualization_trip(data_deliver)
# a.save_to_html(file_name='data_visua_vedio.html')

拓展知识:GeoPandas 的scheme参数

在 GeoPandas 的 plot 方法中,scheme 参数用于对数据进行分类(分箱),这是在绘制分类地图时非常有用的功能。scheme 参数通常与 column 参数一起使用,以便根据某一列的数据值进行分类显示。

GeoPandas 中的 scheme 参数依赖于 mapclassify 库来实现数据分类。mapclassify 提供了多种分类算法,如自然断点、分位数、等间距等。

以下是一些常见的分类方法:

  • BoxPlot: 盒形图分类法
  • EqualInterval: 等间隔分类法
  • FisherJenks: Fisher-Jenks 最优分类法
  • HeadTailBreaks: 头尾分类法
  • JenksCaspall: Jenks-Caspall 最优分类法
  • MaxP: 最大概率分类法
  • NaturalBreaks: 自然断点分类法
  • Quantiles: 分位数分类法
  • StdMean: 标准差分类法

box_plot

盒形图分类法(BoxPlot)基于统计学中的盒须图(Boxplot)。这种方法将数据分为四分位数(Q1,Q2,Q3),Q1 代表第一四分位数,Q2 代表中位数(第二四分位数),Q3 代表第三四分位数。数据被分成四个区间:低于 Q1-1.5(IQR) 的区间,Q1 到 Q2 的区间,Q2 到 Q3 的区间,以及高于 Q3+1.5(IQR) 的区间。这种方法可以帮助识别异常值和数据分布的对称性。

equal_interval

等间隔分类法(Equal Interval)将数据范围划分为相等大小的区间。例如,如果你有 10 个类,且数据范围是 0 到 100,每个区间将是 10(100 / 10)。这种方法简单直观,但可能不均匀地分布数据,导致某些区间内数据过于密集,而其他区间内数据稀疏。

fisher_jenks

Fisher-Jenks 最优分类法(Fisher-Jenks)是一种迭代算法,旨在最小化类别内部的方差(变异性)并最大化类别间的差异。它通过不断尝试不同分割点来寻找最优的分类。这种方法对于分类具有连续分布的数据特别有效。

fisher_jenks_sampled

Fisher Jenks 算法的采样版本,适用于大数据集

headtail_breaks

头尾分类法(Head-Tail Breaks)是一种分箱方法,旨在突出显示数据的长尾分布。它通过识别数据中的“头”(高值)和“尾”(低值)来创建分类,以减少高密度区域的影响并突出显示低密度区域。这种方法特别适用于城市人口密度、交通流量等具有明显峰值和尾部的数据。

jenks_caspall

Jenks-Caspall 最优分类法是 Fisher-Jenks 分类法的一种变体,同样基于最小化内部方差和最大化类别间差异的原则。它通过计算每种可能的分类组合的方差来确定最佳分类。

maximum_breaks

Maximum Breaks(最大断点分类法)是一种用于地理空间数据分类的方法,其目标是通过最大化类别之间的差异来进行数据分级。这种方法通过找到数据中的断点,使得断点尽可能将数据分成具有显著差异的组。

NaturalBreaks

自然断点分类法(Natural Breaks)类似于 Fisher-Jenks,也试图最小化内部方差并最大化类别间差异。它基于数据的内在分组模式,而不是固定数量的区间。这种方法对于数据分布不均匀的情况很有用。

MaxP

MaxP 分类法(Maximum Purity)寻找每个类别的纯度最大。它试图找到一种划分方式,使得每个类别的数据点都尽可能属于同一类。这种方法适用于多分类问题,特别是当类别数量未知时。

quantiles

分位数分类法(Quantiles)将数据分为相等数量的类,每个类包含相同数量的数据点(如果数据点总数是类数的倍数)。例如,如果数据有 100 个点,且分为 4 类,那么每个类将有 25 个点。这种方法确保了每个类别的大小大致相等,但可能会忽略数据的分布特征。

percentiles

百分位数分类法(Percentiles)是一种基于数据的百分位数将数据划分成若干个区间的分类方法。每个区间包含相同比例的数据点。与分位数(Quantiles)分类法类似,但更加细化,通常用于需要更高精度的数据分类。

std_mean

标准差分类法(StdMean)基于数据的平均值(均值)和标准差。它将数据分为几个区间,每个区间内的数据点距离均值的偏差小于一定倍数的标准差。这种方法强调了数据的离群值和集中趋势。

参考链接:

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注