# 交通時空大數據_基礎 > 須預先下載的Modules: > `pip install matplotlib`、`pip install pandas`、`pip install geopandas` ## 整理出需要的data > 若資料集有太多不需要的資訊,可以進行這一步驟整理資料 > 若不整理也可以直接使用的程式碼 * 車輛ID `car_id` * 時間 `_time` * 經度 `lon` * 緯度 `lat` * 速度 `cal_spd` ```python= 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 ### 處理時間 * 使用日期格式轉換 ```python data['Hour'] = pd.to_datetime(data['_time']).dt.hour ``` * 使用字串分割 > 日期格式是:2020-09-04 23:53:44 > 索引從0開始,故小時(23)為11(包括)~13(不包括) ```python data['Hour'] = data['_time'].str.slice(11, 13) ``` * 使用apply方法 (須較長的處理時間) > 定義一function,會對dataframe的每列進行操作 ```python def f(r): return r[11:13] data['Hour'] = data['_time'].apply(f).astype(int) ``` > 亦可使用lambda匿名函數 > `axis=1` 表示你要對每一行應用 lambda 函數 ```python data['Hour'] = data.apply(lambda row: row['_time'][11:13], axis=1) ``` * 完整程式碼 > `.astype(int)` 將結果轉換為整數型別 > 字串分割、 ```python= 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()`將資料表轉置(可以不用) ```python= Hourcount = data.groupby('Hour')['car_id'].count() Hourcount = Hourcount.rename('count').reset_index() print(Hourcount.transpose()) ``` ![image](https://hackmd.io/_uploads/SJNFfH5Fp.png) ### 繪製圖表 ```python= # 繪製圖表 # 建立圖表: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](https://hackmd.io/_uploads/r1A0_rqFT.png) ### 完整程式碼 ```python= 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 keplergl`、`pip install -U transbigdata` ![image](https://hackmd.io/_uploads/SyGlsc9Fp.png ) ```python= 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](https://hackmd.io/_uploads/Bkc8Di5K6.png) ```python= 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](https://hackmd.io/_uploads/r1C5-nqFT.png) ```python= 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](https://hackmd.io/_uploads/Sk3WVhqta.png) ```python= 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()` 可以繪製單變量數據的核密度估計曲線,也可以繪製雙變量數據的核密度等值線圖或二維核密度圖。它的用法如下: ```python 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()` 可以幫助你快速了解數據的分佈情況,並且支持許多參數來定制化你的圖形。 ---