【大數據】城市公交網絡分析與可視化(五):獲取公交平均路線長度、站點數、站距

內容簡介

也不前情提要了,本博客內容高度概括就是:通過直接遍歷法和依據文本法獲取一個城市的“所有”線路名,並計算這些線路的平均路線長度、平均站點數、平均站距(“直線係數”將在下一篇博客中討論)

正文

1、獲取公交信息練習代碼

(1)程序任務
通過高德地圖PAI,爬取一個城市所有(可選)公交基本信息,並保存到表格中。採用循環遍歷一定範圍的數字實現,不封裝函數(好處是,方便查看變量信息,及時發現錯誤,利於修改)

(2)可直接出結果的代碼
遍歷青島前10路公交基本信息

import requests
import json
import pandas as pd
import time

if __name__=="__main__":
    t0=time.time()#用於記錄開始時間
    
    bus_num=0  #計算有效遍歷的公交數
    city='青島' #需要查詢公交信息的城市
    for_num=10 #遍歷的線路數[1路,for_num路],通常公交線路數小於1000,具體可參考8684等網站
    
    all_bus=pd.DataFrame()
    for i in range(1,for_num+1):  
        line=str(i)+'路'
        try:
            #1、獲取數據
            url = 'https://restapi.amap.com/v3/bus/linename?s=rsv3&extensions=all&key=a5b7479db5b24fd68cedcf24f482c156&output=json&city={}&offset=1&keywords={}&platform=JS'.format(city,line)
            r = requests.get(url).text
            rt = json.loads(r)
            #2、讀取當前公交線路主要信息
            dt = {}
            dt['line_name'] = rt['buslines'][0]['name'] #公交線路名字
            dt['bounds'] = rt['buslines'][0]['bounds'] #行車區間(非始發站,終點站座標)
            dt['distance'] = rt['buslines'][0]['distance'] #全程長度
            dt['station'] = int(rt['buslines'][0]['busstops'][-1]['sequence']) #全程站點數
            
            bus_num+=1 #有效公交數+1
            df2=pd.DataFrame(dt,index=[bus_num])  #返回pd.DataFrame()類型
            all_bus=pd.concat([all_bus,df2])  #不加這個'路'可能優先獲取地鐵
        except:
            print('沒有{}公交'.format(line)) #正常情況下,這條語句不會執行
            
        
    print("Bus_info函數遍歷{}前{}路公交,有效公交線路數爲:{}個".format(city,for_num,bus_num))
    all_bus.to_csv("{}前{}路公交(有效線路數:{})基本信息.csv".format(city,for_num,bus_num),encoding='utf-8-sig')
    t1=time.time()#用於記錄結束時間
    print("用時:%.2fs"%(t1-t0))

(3)程序結果:
在這裏插入圖片描述
在這裏插入圖片描述
(4)小結
大部分內容在之前的部分都介紹過了,這裏獲取站點數的代碼值得好好品味一下(可以通過循環得到,但這樣顯得有些多餘),啥也別說了,“數據就是程序員的生命”

dt['station'] = int(rt['buslines'][0]['busstops'][-1]['sequence']) 

2、循環爬取公交信息(封裝函數)

(1)程序內容介紹:
循環青島市的1000路以內的公交(有的線路沒有,且有的線路包含),獲取這些線路的:路線名、行車區間、路程長度、站點數、區間直線距離等數據,並計算出這些公交線路的平均長度、平均站點數、平均站距,最後在python同目錄下保存爲表格。

(2)代碼
小說明:我本意是希望大家拿我的代碼就能出結果的,且不希望代碼出現大的錯誤,但畢竟寫的內容多了,改的地方也多,代碼習慣也在培養的初期,加上生活中瑣事也多,肯定是沒精力做校對的,emmmmm,就這樣

import requests
import json
import pandas as pd
import time
from math import sin, asin, cos, radians, fabs, sqrt

#自己寫的用於記錄時間函數
def record_time(flag):
    if flag==0:
        global t0
        t0=time.time()
    else:
        t1=time.time()
        print("用時:%.2fs"%(t1-t0))  
    print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))

#python計算兩點間直線距離
def Geodistance(lng1,lat1,lng2,lat2):
    lng1, lat1, lng2, lat2 = map(radians, [lng1, lat1, lng2, lat2])
    dlon=lng2-lng1
    dlat=lat2-lat1
    a=sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 
    dis=2*asin(sqrt(a))*6371*1000
    return dis

#或許公交信息:線路名、始發站、終點站、行車區間(座標)、路程、行車區間直線距離
def Bus_inf(city,line):
    global bus_num  #全局變量,用於計算公交數目
    try:
        #獲取數據
        url = 'https://restapi.amap.com/v3/bus/linename?s=rsv3&extensions=all&key=a5b7479db5b24fd68cedcf24f482c156&output=json&city={}&offset=1&keywords={}&platform=JS'.format(city,line)
        r = requests.get(url).text
        rt = json.loads(r)
        #讀取當前公交線路主要信息
        dt = {}
        dt['line_name'] = rt['buslines'][0]['name'] #公交線路名字
        dt['start_stop'] = rt['buslines'][0]['start_stop'] #始發站
        dt['end_stop'] = rt['buslines'][0]['end_stop'] #終點站
        dt['bounds'] = rt['buslines'][0]['bounds'] #行車區間(非始發站,終點站座標)
        dt['distance'] = rt['buslines'][0]['distance'] #全程長度
        dt['station'] = int(rt['buslines'][0]['busstops'][-1]['sequence']) #全程站點數(包括始發站和終點站)

        lng1,lat1 =rt['buslines'][0]['bounds'].split(';')[0].split(',')
        lng2,lat2 =rt['buslines'][0]['bounds'].split(';')[1].split(',')
        dt['straight_dis'] =  Geodistance(float(lng1),float(lat1),float(lng2),float(lat2)) #計算行車區間bounds的直線距離
        bus_num+=1 #有效公交數+1

        return pd.DataFrame(dt,index=[bus_num]) #下標index爲“第幾條公交線”
    except:
        print('沒有{}公交'.format(line)) #正常情況下,這條語句不會執行
        return pd.DataFrame()  #讀取數據失敗,返回空的

def Bus_analysis(bus_info):
    #路程數據需要轉float類型
    print('公交線路的平均長度:{:.6f} km'.format(bus_info["distance"].astype(float).mean())) 
    print('公交線路的平均站點數:{:.6f} 個'.format(bus_info['station'].mean()))
    
    #錯誤站距求解方法:先求出每條線路的平均站距,相加,再除總線路數,得到的總的平均站距是不合適的!(涉及到加權問題)
    #站距求解方法1(不妥):平均長度/平均站點數  (這裏的站點數包括了起始站和終點站,求平均站距時每條線路應該去掉一個站!)
    print('公交線路的平均站距(求法1)爲:{:.6f} km/站'.format(bus_info["distance"].astype(float).mean()/bus_info['station'].mean()))
    #求站距方法2(正確):路線總長/(求出所有站點數總和-線路數)    
    print('公交線路的平均站距(求法2)爲:{:.6f} km/站'.format(bus_info["distance"].astype(float).sum()/(bus_info['station'].sum()-bus_num)))

    

if __name__=="__main__":
    record_time(0)
    
    bus_num=0  #設置全局變量數值(通常默認就是0)
    city='青島' #需要查詢公交信息的城市
    for_num=10 #遍歷的線路數[1路,for_num路],通常公交線路數小於1000,具體可參考8684等網站
    all_buslines=pd.DataFrame()     
    for i in range(1,for_num+1):
        all_buslines=pd.concat([all_buslines,Bus_inf(city,str(i)+'路')])  #不加這個'路'可能優先獲取地鐵
    
    print("Bus_info函數遍歷{}前{}路公交,有效公交線路數爲:{}個的情況下:".format(city,for_num,bus_num))
    Bus_analysis(all_buslines)
    
    #all_buslines.to_csv("{}前{}路公交(有效線路數:{})基本信息.csv".format(city,for_num,bus_num),encoding='utf-8-sig')
    
    record_time(1)
    

(3)運行部分結果:
①424條公交線路
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

②10條線路情況
在這裏插入圖片描述

③如果只想知道市區線路呢?

3、獲取已知線路名的公交信息

(1)說明
通過for循環獲取公交線路名簡單粗暴,但如果想知道定點區域的詳細線路,或許就要一些別的方法了——通過8684網青島公交獲取市區所有路線名,保存到文本中。

(2)稍微修改的代碼
直接拿來就能用的代碼畢竟是少數啊~~~

import requests
import json
import pandas as pd
import time
from math import sin, asin, cos, radians, fabs, sqrt

#自己寫的用於記錄時間函數
def record_time(flag):
    if flag==0:
        global t0
        t0=time.time()
    else:
        t1=time.time()
        print("用時:%.2fs"%(t1-t0))  
    print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(time.time())))

#python計算兩點間直線距離
def Geodistance(lng1,lat1,lng2,lat2):
    lng1, lat1, lng2, lat2 = map(radians, [lng1, lat1, lng2, lat2])
    dlon=lng2-lng1
    dlat=lat2-lat1
    a=sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 
    dis=2*asin(sqrt(a))*6371*1000
    return dis

#或許公交信息:線路名、始發站、終點站、行車區間(座標)、路程、行車區間直線距離
def Bus_inf(city,line):
    global bus_num  #全局變量,用於計算公交數目
    try:
        url = 'https://restapi.amap.com/v3/bus/linename?s=rsv3&extensions=all&key=a5b7479db5b24fd68cedcf24f482c156&output=json&city={}&offset=1&keywords={}&platform=JS'.format(city,line)
        r = requests.get(url).text
        rt = json.loads(r)
        dt = {}
        dt['line_name'] = rt['buslines'][0]['name'] #公交線路名字
        dt['bounds'] = rt['buslines'][0]['bounds'] #行車區間(始發站,終點站座標)
        dt['distance'] = rt['buslines'][0]['distance'] #全程長度
        dt['station'] = int(rt['buslines'][0]['busstops'][-1]['sequence']) #全程站點數(包括始發站和終點站)

        lng1,lat1 =rt['buslines'][0]['bounds'].split(';')[0].split(',')
        lng2,lat2 =rt['buslines'][0]['bounds'].split(';')[1].split(',')
        dt['straight_dis'] =  Geodistance(float(lng1),float(lat1),float(lng2),float(lat2)) #計算行車區間bounds的直線距離
        #if '環形線' in dt['line_name']:
        #    print(dt['line_name'])
        bus_num+=1 #有效公交數+1

        return pd.DataFrame(dt,index=[bus_num]) #下標index爲“第幾條公交線”
    except:
        print('沒有{}公交'.format(line)) #已知路線名的情況下,這條語句不會執行!
        return pd.DataFrame()  #讀取數據失敗,返回空的

def Bus_analysis(bus_info):
    print('公交線路的平均長度:{:.6f}km'.format(bus_info["distance"].astype(float).mean())) 
    print('公交線路的平均站點數:{:.6f}個'.format(bus_info['station'].mean())) 
    #注意:這裏的站點數包括了起始站和終點站,求平均站距時每條線路應該去掉一個站!
    #正確做法:路程/(求出所有站點數總和-線路數)
    print('公交線路的平均站距爲:{:.6f}km/站'.format(bus_info["distance"].astype(float).sum()/(bus_info['station'].sum()-bus_num)))

if __name__=="__main__":
    record_time(0)
    
    bus_num=0  #在已知文本的情況下,直接輸出文本元素個數就是路線數,這裏延續以前代碼思路,故沒修改
    all_buslines=pd.DataFrame()
    city='青島市'
    #獲取已知線路的公交文本
    with open("公交線路.txt", "r", encoding="utf-8") as f:
        bus_name = f.readlines()
    bus_name = bus_name[0].split(",") 
    for i in bus_name:  
        all_buslines=pd.concat([all_buslines,Bus_inf(city,i)]) 
        
    Bus_analysis(all_buslines)
    
    print("通過文本獲取青島市區{}條路線基本信息成功!".format(len(bus_name)))
    all_bus.to_csv("通過文本獲取的青島市區{}條路線基本信息.csv".format(len(bus_name)),encoding='utf-8')
    
    record_time(1)
    

(3)實驗結果
對比前面全市找的424條線路(包括不少郊區線),市區線路的平均站距確實會短一點

在這裏插入圖片描述

4、多城市對比分析

直接遍歷或許會有一些數據的缺失,但要是認真想想,這也可以當作是“隨機取樣”的過程,而且有的公交線路名字複雜,屬於特殊線路,也算是排除異常數據了。
①北京
在這裏插入圖片描述
②突然不想分析了

對比分析見下一篇吧,加上“非直線係數”一塊分析。

說明一下,我的這個系列博客很多地方是需要結合着一起來看的!


(補充知識)通過兩點座標求直線距離

地球是一個近乎標準的橢球體,它的赤道半徑爲6378.140千米,極半徑爲 6356.755千米,平均半徑6371.004千米。如果我們假設地球是一個完美的球體,那麼它的半徑就是地球的平均半徑,記爲R。如果以0度經線爲基準,那麼根據地球表面任意兩點的經緯度就可以計算出這兩點間的地表距離。
按照0度經線的基準,東經取經度的正值(Longitude),西經取經度負值(-Longitude),北緯取90-緯度值(90- Latitude),南緯取90+緯度值(90+Latitude),那麼根據三角推導,可以得到計算兩點距離的如下公式:
在這裏插入圖片描述

其中lat1與lat2分別爲兩點的維度,lon1與lon2分別爲兩點的經度,6378爲地球半徑,計算的結果爲千米,如果要的計算結果爲米,在後面乘以1000即可。不過在寫代碼的時候要將經緯度的值轉換爲弧度。

def Geodistance(lng1,lat1,lng2,lat2):
    lng1, lat1, lng2, lat2 = map(radians, [lng1, lat1, lng2, lat2])
    dlon=lng2-lng1
    dlat=lat2-lat1
    a=sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 
    dis=2*asin(sqrt(a))*6371*1000
    return dis

附件

青島市區所有線路(注意時效性)

‘1路’,‘11路’,‘12路’,‘12路[區間]’,‘15路’,‘101路’,‘102路’,‘103路’,‘104路’,‘105路’,‘110路[區間]’,‘114路’,‘115路’,‘115路[區間]’,‘116路’,‘117路’,‘19路’,‘120路’,‘121路’,‘125路’,‘126路’,‘128路’,‘129路’,‘130路’,‘123路’,‘110路’,‘131路’,‘111路’,‘119路’,‘16路’,‘122路’,‘103路[定時快車]’,‘102路[定時快車]’,‘18路’,‘2路[汽車]’,‘2路[電車]’,‘20路’,‘21路’,‘22路’,‘23路’,‘24路’,‘25路’,‘26路’,‘28路’,‘29路’,‘205路’,‘206路’,‘207路’,‘208路’,‘209路’,‘210路’,‘213路’,‘214路’,‘215路’,‘216路’,‘218路’,‘219路’,‘220路’,‘221路’,‘222路’,‘223路’,‘224路’,‘225路’,‘226路’,‘227路’,‘228路’,‘229路’,‘230路’,‘231路’,‘232路’,‘233路’,‘216路[定時快車]’,‘3路’,‘30路’,‘31路’,‘32路’,‘36路’,‘302路’,‘303路’,‘304路’,‘307路’,‘308路’,‘310路’,‘312路’,‘313路’,‘314路’,‘316路’,‘317路’,‘318路’,‘319路’,‘320路’,‘321路’,‘322路’,‘361路’,‘362路’,‘363路’,‘364路’,‘326路’,‘366路’,‘367路’,‘368路’,‘369路’,‘370路’,‘371路’,‘372路’,‘373路’,‘374路’,‘375路’,‘378路’,‘381路’,‘380路’,‘379路’,‘382路’,‘385路’,‘325路’,‘383路’,‘384路’,‘386路’,‘387路’,‘327路’,‘363路[定時快車]’,‘318路[定時快車]’,‘363路[大站快車]’,‘318路[大站快車]’,‘302路[定時快車]’,‘313路[定時快車]’,‘321路[定時快車]’,‘374路[定時快車]’,‘388路’,‘389路’,‘390路’,‘4路’,‘405路’,‘403路’,‘402路’,‘401路’,‘461路’,‘462路’,‘463路’,‘465路’,‘407路’,‘408路’,‘409路’,‘410路’,‘466路’,‘411路’,‘467路’,‘468路’,‘469路’,‘470路’,‘412路’,‘413路’,‘406路’,‘404路’,‘414路’,‘471路’,‘415路’,‘5路’,‘501路’,‘503路’,‘502路’,‘503路[定時快車]’,‘605路’,‘610路’,‘611路’,‘612路’,‘613路’,‘616路’,‘615路’,‘618路’,‘619路’,‘620路’,‘621路’,‘623路’,‘622路’,‘625路’,‘629路’,‘630路’,‘627路’,‘631路’,‘632路’,‘626路’,‘628路’,‘633路’,‘635路’,‘636路’,‘614路’,‘624路’,‘637路’,‘639路’,‘638路’,‘640路’,‘641路’,‘634路’,‘642路’,‘643路’,‘644路’,‘7路’,‘761路’,‘765路’,‘762路’,‘763路’,‘766路’,‘767路’,‘769路’,‘770路’,‘771路’,‘772路’,‘768路’,‘773路’,‘774路’,‘775路’,‘772路[區間]’,‘779路’,‘777路’,‘778路’,‘8路’,‘901路’,‘902路’,‘903路’,‘904路’,‘905路’,‘908路’,‘906路’,‘907路’,‘909路’,‘910路’,‘912路’,‘913路’,‘915路’,‘916路’,‘917路’,‘919路’,‘929路’,‘930路’,‘921路’,‘920路’,‘922路’,‘923路’,‘924路’,‘925路’,‘918路’,‘926路’,‘927路’,‘928路’,‘932路’,‘931路’,‘933路’,‘935路’,‘936路’,‘937路’,‘938路’,‘934路’,‘939路’,‘941路’,‘942路’,‘914路’,‘944路’,‘940路’,‘943路’,‘都市觀光1線’,‘都市觀光4線’,‘高新快線’

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章