Python实现城市公交网络分析与可视化
2022-11-15 canyinms.com 【 字体:大 中 小 】
数据获取自高德地图API,包含了天津市公交线路和站点名称及其经纬度数据。
import pandas as pd df = pd.read_excel('site_information.xlsx') df.head()
字段说明:
线路名称:公交线路的名称
上下行:0表示上行;1表示下行
站序号:公交线路上行或下行依次经过站的序号
站名称:站点名称
经度(分):站点的经度
纬度(分):站点的纬度
数据字段少,结构也比较简单,下面来充分了解我们的数据和进行预处理。
总的数据有 30396 条,站名称缺失了 5 条,纬度(分)缺失了 1 条,经度(分)缺失了 38 条,为了处理方便,直接把有缺失值的行删除。
经纬度数据是7031.982、2348.1016这样的,需要将其转换为以度为单位。
df2 = df1.copy() df2['经度(分)'] = df1['经度(分)'].apply(float) / 60 df2['纬度(分)'] = df1['纬度(分)'].apply(float) / 60 df2.head()
处理后的数据里,共有 618 条公交线路,4851个站点数据。
重新保存为处理后数据
df2.to_excel("处理后数据.xlsx", index=False)二、数据分析
分析天津市公交站点的分布情况
# -*- coding: UTF-8 -*- """ import pandas as pd import matplotlib.pyplot as plt import matplotlib as mpl import random df = pd.read_excel("处理后数据.xlsx") x_data = df['经度(分)'] y_data = df['纬度(分)'] colors = ['#FF0000', '#0000CD', '#00BFFF', '#008000', '#FF1493', '#FFD700', '#FF4500', '#00FA9A', '#191970', '#9932CC'] colors = [random.choice(colors) for i in range(len(x_data))] mpl.rcParams['font.family'] = 'SimHei' plt.style.use('ggplot') # 设置大小 plt.figure(figsize=(12, 6), dpi=200) # 绘制散点图 经度 纬度 传进去 设置 颜色 点的大小 plt.scatter(x_data, y_data, marker="o", s=9., c=colors) # 添加描述信息 x轴 y轴 标题 plt.xlabel("经度") plt.ylabel("纬度") plt.title("天津市公交站点分布情况") plt.savefig('经纬度散点图.png') plt.show()
结果如下:
通过 matplotlib 绘制散点图可视化天津市公交站点的分布情况,容易看出天津市的公交热点分布区域。为了能更形象地分析公交线路网络,我们可以将数据可视化在实际地图上,利用 Pyecharts 的BMap。
# -*- coding: UTF-8 -*- """ import pandas as pd from pyecharts.charts import BMap from pyecharts import options as opts from pyecharts.globals import CurrentConfig # 引用本地js资源渲染 CurrentConfig.ONLINE_HOST = 'D:/python/pyecharts-assets-master/assets/' df = pd.read_excel('处理后数据.xlsx', encoding='utf-8') df.drop_duplicates(subset='站名称', inplace=True) longitude = list(df['经度(分)']) latitude = list(df['纬度(分)']) datas = [] a = [] for i, j in zip(longitude, latitude): a.append([i, j]) datas.append(a) print(datas) BAIDU_MAP_AK = "改成你的百度地图AK" c = ( BMap(init_opts=opts.InitOpts(width="1200px", height="800px")) .add_schema( baidu_ak=BAIDU_MAP_AK, # 申请的BAIDU_MAP_AK center=[117.20, 39.13], # 天津市经纬度中心 zoom=10, is_roam=True, ) .add( "", type_="lines", is_polyline=True, data_pair=datas, linestyle_opts=opts.LineStyleOpts(opacity=0.2, width=0.5, color='red'), # 如果不是最新版本的话可以注释下面的参数(效果差距不大) progressive=200, progressive_threshold=500, ) ) c.render('公交网络地图.html')
结果如下:
在地图上可以看到,和平区、南开区公交线路网络密集,交通便利。
公交线路网络中 i 节点代表第 i 条线路,其中节点 i 的度定义为与线路 i 可以经过换乘能够到达的线路的数目,线路网络的度大小反映了该条公交线路与其他线路的连通程度,构建算法分析公交线路网络度的分布。
# -*- coding: UTF-8 -*- """ import xlrd import matplotlib.pyplot as plt import pandas as pd import matplotlib as mpl df = pd.read_excel("site_information.xlsx") # 用pandas的操作去重 得到每条线路的名称 loc = df['线路名称'].unique() # 得到每一条线路名称的列表 line_list = list(loc) print(line_list) # 打开Excel表格 data = xlrd.open_workbook("site_information.xlsx") # print(data) #在内存中 # 获取特定Sheet 索引为0 也就是第一个表 table = data.sheets()[0] # 从零开始 # 每条线路对应有哪些站点 字典推导式 site_dic = {k: [] for k in line_list} site_list = [] for i in range(1, table.nrows): # 每一行的数据 返回的是一个列表 x = table.row_values(i) if x[1] == "0": # 上行 站点数据 每条线路对应有哪些站点 添加进列表 site_dic[x[0]].append(x[3]) site_list.append(x[3]) else: continue # print(len(site_dic)) # 618条线路 # print(len(site_list)) # 15248条站点数据 print(f"公交网络共有 {len(line_list)} 条线路") # 618条线路 # 先初始化一个统计每个节点的度的列表 与线路名称列表里的索引一一对应 node_count = [m * 0 for m in range(len(line_list))] # 以每条线路为一个节点 线路名称为键 值为一个列表 里面包含每条路线上行经过的所有站点 sites = [site for site in site_dic.values()] # print(sites) for j in range(len(sites)): # 类似冒泡法排序 比较多少趟 for k in range(j, len(sites) - 1): # 每趟比较后 往后推一个 直到比较完 和防止越界 if len(sites[j]) > len(sites[k + 1]): for x in sites[j]: if x in sites[j] and x in sites[k + 1]: # 只要这两条线路有公共站点 节点度数加1 node_count[j], node_count[k + 1] = node_count[j] + 1, node_count[k + 1] + 1 break # 两条线路对应在列表索引的值加1 这两条线的比较结束 else: for x in sites[k + 1]: if x in sites[j] and x in sites[k + 1]: # 只要这两条线路有公共站点 节点度数加1 node_count[j], node_count[k + 1] = node_count[j] + 1, node_count[k + 1] + 1 break # 两条线路对应在列表索引的值加1 这两条线的比较结束 # print(node_count) # 节点编号 与 节点的度数索引对应 node_number = [y for y in range(len(node_count))] # 线性网络度的最大值 175 print(f"线路网络的度的最大值为:{max(node_count)}") print(f"线路网络的度的最小值为:{min(node_count)}") print(f"线路网络的度的平均值为:{sum(node_count) / len(node_count)}") # 设置大小 图的像素 # 设置字体 matplotlib 不支持显示中文 自己本地设置 plt.figure(figsize=(10, 6), dpi=150) mpl.rcParams['font.family'] = 'SimHei' # 绘制每个节点度的分布 plt.bar(node_number, node_count, color="purple") # 添加描述信息 plt.xlabel("节点编号n") plt.ylabel("节点的度数K") plt.title("线路网络中各节点的度的大小分布", fontsize=15) plt.savefig("线路网络中各节点的度的大小.png") plt.show()
结果如下:
公交网络共有 618 条线路
线路网络的度的最大值为:175
线路网络的度的最小值为:0
线路网络的度的平均值为:55.41423948220065
import xlrd import matplotlib.pyplot as plt import pandas as pd import matplotlib as mpl import collections df = pd.read_excel("site_information.xlsx") # 用pandas的操作去重 得到每条线路的名称 loc = df['线路名称'].unique() # 得到每一条线路名称的列表 line_list = list(loc) print(line_list) # 打开Excel表格 data = xlrd.open_workbook("site_information.xlsx") # print(data) #在内存中 # 获取特定Sheet 索引为0 也就是第一个表 table = data.sheets()[0] # 从零开始 # 每条线路对应有哪些站点 字典推导式 site_dic = {k: [] for k in line_list} site_list = [] for i in range(1, table.nrows): # 每一行的数据 返回的是一个列表 x = table.row_values(i) if x[1] == "0": # 上行 站点数据 每条线路对应有哪些站点 添加进列表 site_dic[x[0]].append(x[3]) site_list.append(x[3]) else: continue # print(len(site_dic)) # 618条线路 # print(len(site_list)) # 15248条站点数据 # 先初始化一个统计每个节点的度的列表 与线路名称列表里的索引一一对应 node_count = [m * 0 for m in range(len(line_list))] # 以每条线路为一个节点 线路名称为键 值为一个列表 里面包含每条路线上行经过的所有站点 sites = [site for site in site_dic.values()] # print(sites) for j in range(len(sites)): # 类似冒泡法排序 比较多少趟 for k in range(j, len(sites) - 1): # 每趟比较后 往后推一个 直到比较完 和防止越界 if len(sites[j]) > len(sites[k + 1]): for x in sites[j]: if x in sites[j] and x in sites[k + 1]: # 只要这两条线路有公共站点 节点度数加1 node_count[j], node_count[k + 1] = node_count[j] + 1, node_count[k + 1] + 1 break # 两条线路对应在列表索引的值加1 这两条线的比较结束 else: for x in sites[k + 1]: if x in sites[j] and x in sites[k + 1]: # 只要这两条线路有公共站点 节点度数加1 node_count[j], node_count[k + 1] = node_count[j] + 1, node_count[k + 1] + 1 break # 两条线路对应在列表索引的值加1 这两条线的比较结束 # print(node_count) # 节点编号 与 节点的度数索引对应 node_number = [y for y in range(len(node_count))] # 线性网络度的最大值 175 # print(max(node_count)) # 设置大小 图的像素 # 设置字体 matplotlib 不支持显示中文 自己本地设置 plt.figure(figsize=(10, 6), dpi=150) mpl.rcParams['font.family'] = 'SimHei' # 分析节点的度K的概率分布 # 统计节点的度为K的 分别有多少个 node_count = collections.Counter(node_count) node_count = node_count.most_common() # 点 node_dic = {_k: _v for _k, _v in node_count} # 按键从小到大排序 得到一个列表 节点的度 sort_node = sorted(node_dic) # 按顺序得到键对应的值 即有相同节点的度的个数 sort_num = [node_dic[q] for q in sort_node] # 概率分布中度平均值 总的度数加起来 / 个数 # print(sum(sort_node)/len(sort_node)) # 概率分布中最大的度值 也就个数最多那个 print(f"概率分布中概率最大的度值为:{max(sort_num)}") probability = [s1 / sum(sort_num) for s1 in sort_num] # 概率分布 print(probability) # 天津市公交线路节点概率分布图像 plt.bar(sort_node, probability, color="red") # 添加描述信息 plt.xlabel("节点的度K") plt.ylabel("节点度为K的概率P(K)") plt.title("线路网络中节点度的概率分布", fontsize=15) plt.savefig("线路网络中节点度的概率分布.png") plt.show()
结果如下:
概率分布中概率最大的度值为:16
天津市公交线路网络的度分布如上图所示,本文收集的天津市线路网络共有 618 条线路组成,线路网络的度的最大值为175。概率分布中概率最大的度值为16,度平均值为55.41,表明天津市公交网络提供的换乘机会较多,使得可达性较高。其中概率较大的度值大多集中在 7~26 之间。使得节点强度分布相对来说不够均匀,造成天津市很多路段公交线路较少,少数路段经过线路过于密集,造成资源的浪费。
聚类系数是研究节点邻居之间的连接紧密程度,因此不必考虑边的方向。对于有向图,将其当成无向图来处理。网络聚类系数大,表明网络中节点与其附近节点之间的连接紧密度程度高,即与实际站点之间的公交线路连接密集。计算得到天津公交复杂网络的聚类系数为0.091,相对其他城市较低。
根据公式:
同规模的随机网络聚集系数约为0.00044,进一步体现了网络的小世界特性。
import xlrd import matplotlib.pyplot as plt import pandas as pd import matplotlib as mpl # 读取数据 df = pd.read_excel("site_information.xlsx") # 用pandas的操作去重 得到每条线路的名称 loc = df['线路名称'].drop_duplicates() # 得到每一条线路名称的列表 按照Excel表里以次下去的顺序 line_list = list(loc) # print(line_list) # 打开Excel表格 data = xlrd.open_workbook("site_information.xlsx") # print(data) #在内存中 # 获取特定Sheet 索引为0 也就是第一个表 table = data.sheets()[0] # 从零开始 # 每条线路对应有哪些站点 字典推导式 site_dic = {k: [] for k in line_list} site_list = [] for i in range(1, table.nrows): # 每一行的数据 返回的是一个列表 x = table.row_values(i) if x[1] == "0": # 只取上行站点数据 每条线路对应有哪些站点 添加进列表 site_dic[x[0]].append(x[3]) site_list.append(x[3]) else: continue # print(len(site_dic)) # 618条线路 # print(len(site_list)) # 15248条站点数据 # 先初始化一个统计每个节点的度的列表 与线路名称列表里的索引一一对应 node_count = [m * 0 for m in range(len(line_list))] # 以每条线路为一个节点 线路名称为键 值为一个列表 里面包含每条路线上行经过的所有站点 sites = [site for site in site_dic.values()] # print(sites) # 统计各节点的度 for j in range(len(sites) - 1): # 类似冒泡法排序 比较多少趟 for k in range(j, len(sites) - 1): # 每趟比较后 往后推一个 直到比较完 和防止越界 if len(sites[j]) > len(sites[k + 1]): for x in sites[j]: if x in sites[j] and x in sites[k + 1]: # 只要这两条线路有公共站点 节点度数加1 node_count[j], node_count[k + 1] = node_count[j] + 1, node_count[k + 1] + 1 break # 两条线路对应在列表索引的值加1 这两条线的比较结束 else: for x in sites[k + 1]: if x in sites[j] and x in sites[k + 1]: # 只要这两条线路有公共站点 节点度数加1 node_count[j], node_count[k + 1] = node_count[j] + 1, node_count[k + 1] + 1 break # 两条线路对应在列表索引的值加1 这两条线的比较结束 # 找到该节点的邻居节点 邻居节点间实际的边数 Ei = [] # 对每条线路进行找邻接节点 并统计其邻接节点点实际的边数 for a in range(len(sites)): neighbor = [] if node_count[a] == 0: Ei.append(0) continue if node_count[a] == 1: Ei.append(0) continue for b in range(len(sites)): if a == b: # 自身 不比 continue if len(sites[a]) > len(sites[b]): # 从站点多的线路里选取站点 看是否有公共站点 for x in sites[a]: if x in sites[a] and x in sites[b]: # 找到邻居节点 neighbor.append(sites[b]) break else: for x in sites[b]: if x in sites[a] and x in sites[b]: # 找到邻居节点 neighbor.append(sites[b]) break # 在邻居节点中判断这些节点的实际边数 又类似前面的方法 判断两两是否相连 count = 0 for c in range(len(neighbor) - 1): for d in range(c, len(neighbor) - 1): # 每趟比较后 往后推一个 直到比较完 和防止越界 try: if len(sites[c]) > len(sites[d + 1]): for y in sites[c]: if y in sites[c] and y in sites[d + 1]: # 邻居节点这两个也相连 count += 1 break else: continue else: for y in sites[d + 1]: if y in sites[c] and y in sites[d + 1]: # 邻居节点这两个也相连 count += 1 break else: continue except IndexError: break Ei.append(count) # 每个节点的邻居节点间实际相连的边数 # print(Ei) # 节点编号 与 节点的度数索引对应 node_number = [y for y in range(len(node_count))] # 设置字体 matplotlib 不支持显示中文 自己本地设置 mpl.rcParams['font.family'] = 'SimHei' # 设置大小 图的像素 plt.figure(figsize=(10, 6), dpi=150) # 公交线路网络的聚类系数分布图像 相邻节点的连通程度 Ci = [] for m in range(len(node_number)): if node_count[m] == 0: Ci.append(0) elif node_count[m] == 1: Ci.append(0) else: # 2 * 该节点邻居节点实际连接边数 / 最大边数 Ci.append(2 * Ei[m] / (node_count[m] * (node_count[m] - 1))) # 各节点邻居节点的连通程度 计算平均聚类系数 print("天津市公交线路网络平均聚类系数为:{:.4f}".format(sum(Ci) / len(Ci))) plt.bar(node_number, Ci, color="blue") # 添加描述信息 plt.xlabel("节点编号n") plt.ylabel("节点的聚类系数") plt.title("线路网络中各节点的聚类系数分布", fontsize=15) plt.savefig("聚类系数分布.png") plt.show()
结果如下:
天津市公交线路网络平均聚类系数为:0.0906
猜你喜欢
2023年高考需注意这些事项 注意事项有哪些?
2023年高考时间是几月几日 全国高考什么时候?
高考倒计时1天 几号开始高考?
晋江8个考点实行交通管制 此举有何意义?
高考生哪六样东西千万别发朋友圈?
黑龙江高考2023时间安排表 黑龙江高考是全国几卷?
《教育资源免费送!开学季大放送》
小学教育,如何激发孩子学习兴趣?
了解最新职业培训趋势,掌握未来就业机会!
教如何填报高考志愿
橘子洲旅游攻略 长沙橘子洲怎么玩
西安周边旅游攻略 西安周边最值得去的地方
毛坦厂中学万人雨中送考 毛坦厂送考头车车牌91666祝福考生
珠峰被救女子不愿支付全部救援费用 珠峰攀登者为救遇险女子放弃登顶
登珠峰获救女子只愿承担4成救援费是怎么回事 登珠峰获救女子只愿承担4成救援费具体情况
国内出现偏肺病毒感染 尚无治疗药物
男子连吃100天麦当劳减重53斤是真的吗 男子连吃100天麦当劳为什么能减重53斤
本周狗屎运最旺的星座 本周狗屎运最旺的四大星座
印度耗资百亿卢比的大桥又塌了是什么情况 印度耗资百亿卢比的大桥又塌了是为什么
一个村考出300多大学生6个北大 湖南一小村考上一本奖4000二本3000