Try   HackMD

交通時空大數據_基礎

須預先下載的Modules:
pip install matplotlibpip install pandaspip install geopandas

整理出需要的data

若資料集有太多不需要的資訊,可以進行這一步驟整理資料
若不整理也可以直接使用的程式碼

  • 車輛ID car_id
  • 時間 _time
  • 經度 lon
  • 緯度 lat
  • 速度 cal_spd
import os import shutil import pandas as pd # 指定資料夾路徑 input_folder = 'C:\\NCKU\\data_analyze\\NSYU_september\\send' output_folder = 'C:\\NCKU\\data_analyze\\NSYU_september\\new' # 確保輸出資料夾存在,若不存在就建立 os.makedirs(output_folder, exist_ok=True) # 遍歷資料夾中的所有檔案 for filename in os.listdir(input_folder): if filename.endswith('.csv'): input_filepath = os.path.join(input_folder, filename) data = pd.read_csv(input_filepath) selected_data = data[['car_id', '_time', 'lon', 'lat', 'cal_spd']] output_filepath = os.path.join(output_folder, filename) # 將整理後的資料另存為檔案 selected_data.to_csv(output_filepath, index=False) print("finish")

時間完整性評估

hourly_data_statistics.py

處理時間

  • 使用日期格式轉換

    ​​​​data['Hour'] = pd.to_datetime(data['_time']).dt.hour
    
  • 使用字串分割

    日期格式是:2020-09-04 23:53:44
    索引從0開始,故小時(23)為11(包括)~13(不包括)

    ​​​​data['Hour'] = data['_time'].str.slice(11, 13)
    
  • 使用apply方法 (須較長的處理時間)

    定義一function,會對dataframe的每列進行操作

    ​​​​def f(r):
    ​​​​    return r[11:13]
    
    ​​​​data['Hour'] = data['_time'].apply(f).astype(int)
    

    亦可使用lambda匿名函數
    axis=1 表示你要對每一行應用 lambda 函數

    ​​​​data['Hour'] = data.apply(lambda row: row['_time'][11:13], axis=1)
    
  • 完整程式碼

    .astype(int) 將結果轉換為整數型別
    字串分割、

    ​​​​import os ​​​​import pandas as pd ​​​​# 指定資料路徑 ​​​​input_data = 'C:\\NCKU\\data_analyze\\NSYU_september\\new\\merge0509.csv' ​​​​data = pd.read_csv(input_data) ​​​​# 處理時間 ​​​​# 使用日期格式轉換 ​​​​data['Hour'] = pd.to_datetime(data['_time']).dt.hour ​​​​# 使用字串分割 ​​​​data['Hour'] = data['_time'].str.slice(11, 13).astype(int) ​​​​# 使用apply方法 ​​​​def f(r): ​​​​ return r[11:13] ​​​​data['Hour'] = data['_time'].apply(f).astype(int) ​​​​#lambda ​​​​data['Hour'] = data.apply(lambda row: row['_time'][11:13], axis=1).astype(int) ​​​​print(data)

統計每小時的數據量

統計每個小時內有多少唯一的 'car_id' (同一台車不重複計算)

  • 使用 groupby() ,按照 'Hour' 欄位對資料進行分組
  • reset_index() 重置索引,使 'Hour' 成為一列
  • transpose()將資料表轉置(可以不用)
Hourcount = data.groupby('Hour')['car_id'].count() Hourcount = Hourcount.rename('count').reset_index() print(Hourcount.transpose())

image

繪製圖表

# 繪製圖表 # 建立圖表:fig = plt.figure(尺寸, dpi=大小) fig = plt.figure(figsize=(8, 4), dpi=100) # 建立子圖ax = fig.add_subplot(111) ax = fig.add_subplot(111) # 111 : nrows x ncols x index(將圖表分割成 1 行 1 列的網格(1x1),並選擇其中的第一個子圖) # plt.plot(x軸, y軸, 樣式) plt.plot(Hourcount['Hour'], Hourcount['count'], 'k-') # 折線 plt.plot(Hourcount['Hour'], Hourcount['count'], 'k.') # 散點 plt.bar(Hourcount['Hour'], Hourcount['count']) # 柱狀 plt.ylabel("Data Volume") plt.xlabel("Hour") # plt.xticks(要放幾個刻度, 刻度的標籤(X 軸上的數字)) plt.xticks(range(24), range(24)) plt.title("Hourly data volume") # plt.ylim(Y軸的下限, Y軸的上限) plt.ylim(0, 20000) plt.show()

image

完整程式碼

import os import pandas as pd import matplotlib.pyplot as plt # 指定資料路徑 input_data = 'merge0509.csv' data = pd.read_csv(input_data) # 處理時間 data['Hour'] = data['_time'].str.slice(11, 13).astype(int) # 每小時的數據量 Hourcount = data.groupby('Hour')['car_id'].count() Hourcount = Hourcount.rename('count').reset_index() print(Hourcount.transpose()) # 繪製圖表 # 建立圖表:fig = plt.figure(尺寸, dpi=大小) fig = plt.figure(figsize=(8, 4), dpi=100) # 建立子圖ax = fig.add_subplot(111) ax = fig.add_subplot(111) # 111 : nrows x ncols x index(將圖表分割成 1 行 1 列的網格(1x1),並選擇其中的第一個子圖) # plt.plot(x軸, y軸, 樣式) plt.plot(Hourcount['Hour'], Hourcount['count'], 'k-') # 折線 plt.plot(Hourcount['Hour'], Hourcount['count'], 'k.') # 散點 plt.bar(Hourcount['Hour'], Hourcount['count']) # 柱狀 plt.ylabel("Data Volume") plt.xlabel("Hour") # plt.xticks(要放幾個刻度, 刻度的標籤(X 軸上的數字)) plt.xticks(range(24), range(24)) plt.title("Hourly data volume") # plt.ylim(Y軸的下限, Y軸的上限) plt.ylim(0, 20000) plt.show()

空間完整性評估

建立空間分布柵格圖

spatial_distribution_grid_map.py
須預先下載的Modules:pip install keplerglpip install -U transbigdata

image

import geopandas as gpd import transbigdata as tbd import pandas as pd import matplotlib.pyplot as plt input_csv = 'merge0509.csv' input_shp = 'shp\\kuohsiung.shp' data = pd.read_csv(input_csv, dtype={'car_id': int, '_time': str, 'lon': float, 'lat': float, 'cal_spd': float}) sz = gpd.read_file(input_shp) sz = sz.to_crs(epsg=4326) #一定要用WGS84 # 篩選剔除研究範圍外之數據 # accuracy為柵格大小,單位為m -> 越小精度越高 accuracy = 500 data = tbd.clean_outofshape(data, sz, col=['lon', 'lat'], accuracy = accuracy) # 定義範圍、柵格參數(需用WGS84) minx, miny, maxx, maxy = sz.total_bounds bound = [minx, miny, maxx, maxy] params = tbd.area_to_params(bound, accuracy = accuracy) print(params) #將GPS資料對應至柵格 data['LonCol'], data['LatCol'] = tbd.GPS_to_grid(data['lon'], data['lat'], params) grid_agg = data.groupby(['LonCol', 'LatCol'])['car_id'].count().reset_index() grid_agg['geometry'] = tbd.grid_to_polygon([grid_agg['LonCol'], grid_agg['LatCol']], params) grid_agg = gpd.GeoDataFrame(grid_agg) fig = plt.figure(1, figsize=(8, 8), dpi=100) ax = fig.add_subplot(111) # tbd.plot_map(plt, bound, zoom = 11, style = 11) 界接地圖 sz.plot(ax = ax, edgecolor = (0,0,0,0), facecolor = (0,0,0,0.1), linewidth = 0.5) cax = plt.axes([0.1, 0.33, 0.02, 0.3]) plt.title('Data count') plt.sca(ax) grid_agg.plot(column = 'car_id', cmap = 'autumn_r', ax=ax, cax = cax, legend=True) tbd.plotscale(ax, bounds = bound, textsize = 10, compasssize = 1, accuracy = 2000, rect = [0.6, 0.03], zorder = 10) plt.axis('off') plt.xlim(bound[0], bound[2]) plt.ylim(bound[1], bound[3]) plt.show()

建立空間分布散點圖

spatial_distribution_scatter_plot.py
須預先下載的Modules:pip install seaborn

image

import geopandas as gpd import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import matplotlib as mpl import transbigdata as tbd input_csv = 'merge0509.csv' input_shp = 'shp\\kuohsiung.shp' data = pd.read_csv(input_csv, dtype={'car_id': int, '_time': str, 'lon': float, 'lat': float, 'cal_spd': float}) sz = gpd.read_file(input_shp) sz = sz.to_crs(epsg=4326) #经纬度小数点保留三位小数 data = data[['lon','lat']].round(3).copy() # 集計每個小範圍內的數據量 data['count'] = 1 data = data.groupby(['lon', 'lat'])['count'].count().reset_index() # 排序數據,讓數據量小的放在最上面畫,數據大的放在最下面最後畫 data = data.sort_values(by='count') fig = plt.figure(1, (8, 8), dpi=80) ax = plt.subplot(111) plt.sca(ax) # 繪製行政區劃的邊界 minx, miny, maxx, maxy = sz.total_bounds bounds = [minx, miny, maxx, maxy] sz.plot(ax=ax, edgecolor=(0, 0, 0, 0), facecolor=(0, 0, 0, 0.1), linewidths=0.5) # 定義 colorbar pallete_name = "BuPu" colors = sns.color_palette(pallete_name, 3) colors.reverse() cmap = mpl.colors.LinearSegmentedColormap.from_list(pallete_name, colors) vmax = data['count'].quantile(0.99) norm = mpl.colors.Normalize(vmin=0, vmax=vmax) # 繪製散點圖 plt.scatter(data['lon'], data['lat'], s=1, alpha=1, c=data['count'], cmap=cmap, norm=norm) # 添加比例尺和指北針 tbd.plotscale(ax, bounds=bounds, textsize=10, compasssize=1, accuracy=2000, rect=[0.6, 0.03]) plt.axis('off') plt.xlim(bounds[0], bounds[2]) plt.ylim(bounds[1], bounds[3]) # 繪製 colorbar cax = plt.axes([0.15, 0.33, 0.02, 0.3]) plt.colorbar(cax=cax) plt.title('count') plt.show()

建立空間分布熱力圖

spatial_distribution_heatmap.py

image

import geopandas as gpd import pandas as pd import matplotlib.pyplot as plt import matplotlib as mpl import transbigdata as tbd import numpy as np # 指定文件路径 input_csv = 'merge0509.csv' input_shp = 'shp\\kuohsiung.shp' data = pd.read_csv(input_csv, dtype={'car_id': int, '_time': str, 'lon': float, 'lat': float, 'cal_spd': float}) sz = gpd.read_file(input_shp) sz = sz.to_crs(epsg=4326) # 經緯度小數點保留三位小數 data = data[['lon','lat']].round(3).copy() # 集計每個小範圍內的數據量 data['count'] = 1 data = data.groupby(['lon', 'lat'])['count'].count().reset_index() data = data.sort_values(by='count') # 轉換數據透視表,變成矩陣格式 d = data.pivot(columns = 'lon',index = 'lat',values = 'count').fillna(0) # 取對數,縮小最大最小值之間的差距 z = np.log(d.values) x = d.columns y = d.index levels = np.linspace(0, z.max(), 25) fig = plt.figure(1, (8, 8), dpi=80) ax = plt.subplot(111) plt.sca(ax) fig.tight_layout(rect=(0.05, 0.1, 1, 0.9)) # 調整整體空白 # 繪製行政區劃的邊界 minx, miny, maxx, maxy = sz.total_bounds bounds = [minx, miny, maxx, maxy] sz.plot(ax=ax, edgecolor=(0, 0, 0, 0), facecolor=(0, 0, 0, 0.1), linewidths=0.5) # 定義colorbar cmap = mpl.colors.LinearSegmentedColormap.from_list('cmap', ['#9DCC42', '#FFFE03', '#F7941D', '#E9420E', '#FF0000'], 256) # 繪製等高線圖 plt.contourf(x, y, z, levels=levels, cmap=cmap, origin='lower') # 添加比例尺和指北針 tbd.plotscale(ax, bounds=bounds, textsize=10, compasssize=1, accuracy=2000, rect=[0.6, 0.03]) plt.axis('off') plt.xlim(bounds[0], bounds[2]) plt.ylim(bounds[1], bounds[3]) # 繪製colorbar cax = plt.axes([0.13, 0.32, 0.02, 0.3]) cbar = plt.colorbar(cax=cax) # cbar = plt.colorbar(cax=cax, ticks=np.linspace(z.min(), z.max(), 5), format="%1.1f") # 調整colorbar的顯示標記位置 val = [1, 10, 100, 1000] pos = np.log(np.array(val)) # 在什麼位置顯示標記 cbar.set_ticks(pos) # 標記顯示什麼內容 cbar.set_ticklabels(val) plt.title('Count') plt.show()

建立空間核密度

spatial_kernel_density.py

image

import geopandas as gpd import pandas as pd import matplotlib as mpl import matplotlib.pyplot as plt import transbigdata as tbd import seaborn as sns import numpy as np input_csv = 'merge0509.csv' input_shp = 'shp\\kuohsiung.shp' data = pd.read_csv(input_csv, dtype={'car_id': int, '_time': str, 'lon': float, 'lat': float, 'cal_spd': float}) sz = gpd.read_file(input_shp) sz = sz.to_crs(epsg=4326) data = data[['lon','lat']].round(3).copy() data['count'] = 1 data = data.groupby(['lon', 'lat'])['count'].count().reset_index() # 建立畫布 fig = plt.figure(1, (10, 10), dpi=60) ax = plt.subplot(111) plt.sca(ax) fig.tight_layout(rect=(0.05, 0.1, 1, 0.9)) # 調整整體空白 # 繪製行政區劃的邊界 minx, miny, maxx, maxy = sz.total_bounds bounds = [minx, miny, maxx, maxy] sz.plot(ax=ax, edgecolor=(0, 0, 0, 0), facecolor=(0, 0, 0, 0.1), linewidths=0.5) # 色彩映射的數據 cmap = mpl.colors.LinearSegmentedColormap.from_list('cmap', ['#9DCC42', '#FFFE03', '#F7941D', '#E9420E', '#FF0000'], 256) # 設定顯示範圍 plt.axis('off') plt.xlim(bounds[0], bounds[2]) plt.ylim(bounds[1], bounds[3]) # 定義 colorbar 位置 cax = plt.axes([0.13, 0.32, 0.02, 0.3]) # 繪製二維核密度圖 sns.kdeplot(x='lon', y='lat', # 指定x與y座標所在的欄位 data=data[data['count'] > 1], # 來源資料,篩選去除太小的值 weights='count', # 設定權重所在欄位 alpha=0.8, # 透明度 gridsize=120, # 繪圖精細度,越高越慢 bw=0.03, # 高斯核大小(經緯度),越小越精細 cmap=cmap, # 定義色彩映射 ax=ax, # 指定繪圖位置 shade=True, # 是否填充等高線間的顏色 shade_lowest=False, # 最底層不顯示顏色 cbar=True, # 顯示 colorbar cbar_ax=cax # 指定 colorbar 位置 ) # 顯示圖形 plt.show()

Seaborn

Seaborn是基於matplotlib的Python視覺化函式庫

  • sns.kdeplot() https://seaborn.pydata.org/generated/seaborn.kdeplot.html

  • 來ChatGPT的介紹與作者的補充:)

    sns.kdeplot() 是 seaborn 庫中的一個函數,用於繪製單變量或雙變量的核密度估計(Kernel Density Estimation)。核密度估計是一種用來估計概率密度函數的非參數方法,它會通過對數據進行平滑處理來估計數據的分佈情況。

    sns.kdeplot() 可以繪製單變量數據的核密度估計曲線,也可以繪製雙變量數據的核密度等值線圖或二維核密度圖。它的用法如下:

    ​​​​sns.kdeplot(data, data2=None, shade=False, vertical=False, kernel='gau', bw='scott', gridsize=100, cut=3, clip=None, legend=True, cumulative=False, shade_lowest=True, cbar=False, cbar_ax=None, cbar_kws=None, ax=None, **kwargs)
    

    主要參數包括:

    • data: 要繪製的數據,可以是一維或二維數組。
    • data2: 用於繪製雙變量核密度估計的第二組數據(可選)。
    • shade: 是否給密度曲線下方區域填充顏色,默認為 False。
    • vertical: 是否將密度估計曲線繪製為垂直方向,默認為 False。
    • kernel: 用於估計密度的核函數,可選值包括 'gau'(高斯核)、'cos'(餘弦核)、'biw'(雙重三角核)等,默認為 'gau'。
      • 即為kernel shape
      • 'biw' 等同GIS的 Quartic
      • 'tri' 等同GIS的 Triangular
      • 'epa' 等同GIS的 Epanechnikov
      • 'uni' 等同GIS的 Uniform
      • 'triw' 等同GIS的 Triweight
    • bw: 控制核密度估計帶寬的方法,可選值包括 'scott'、'silverman' 或一個標量,默認為 'scott'。
      • 即為Radius, 單位則依輸入的資料而定
      • 因這邊輸入的值是經緯度,所以這邊的單位都是度,如需使用公尺等單位,可能須再自行轉換
    • gridsize: 用於計算核密度的網格大小,默認為 100。
    • cut: 在繪製雙變量核密度估計時,用於裁剪數據的標準差數,默認為 3。
    • clip: 用於裁剪核密度估計的範圍。
    • legend: 是否顯示圖例,默認為 True。
    • cumulative: 是否繪製累積密度圖,默認為 False。
    • shade_lowest: 是否給最低密度區域填充顏色,默認為 True。
    • cbar: 是否在繪製雙變量核密度圖時繪製顏色條,默認為 False。
    • cbar_ax: 顏色條的 Axes 對象。
    • cbar_kws: 顏色條的其他參數。
    • ax: 繪製圖形的 Axes 對象,如果沒有提供,則使用當前 Axes。

    sns.kdeplot() 可以幫助你快速了解數據的分佈情況,並且支持許多參數來定制化你的圖形。