過去探討的網路模型常將節點視為抽象實體,其在空間中的佈局僅為視覺化輔助。然而,真實世界的系統,如交通網絡、基礎設施或通訊流量,其節點與地理位置密不可分。這種空間屬性引入了新的分析維度:距離。直觀上,距離越遠,互動成本越高,連結強度也隨之減弱。此現象使得我們難以判斷一條強連結,究竟是因節點本身重要,抑或是單純距離較近所致。為了解決此問題,引力模型應運而生,它提供了一套標準化方法,讓我們得以剝離距離的干擾,洞察節點間互動的內在強度,進而揭示更深層的網絡結構與動態。
時空網路的基礎:地理位置、事件與引力模型
本章節將開啓對「時空網路」(Networks in Space and Time) 的探討,這是網路分析領域一個極為重要的分支。我們將首先介紹網路如何表示地理位置和事件之間的關係,以及「引力模型」(Gravity Models) 如何幫助我們理解空間距離對互動強度影響的現象。
時空網路的基礎概念
- 網路的多樣性:
- 到目前為止,我們討論的網路節點通常被視為獨立於時間和空間存在。即使在視覺化時將節點放置在二維空間,也主要是為了區分它們。
- 然而,在許多實際應用中,節點確實與特定的地理位置或時間點緊密相關。
- 時空網路的表示:
- 地理位置網路 (Networks in Space):節點代表地理實體(如城市、機場、電網節點),邊代表它們之間的空間聯繫(如道路、航班、電纜)。
- 範例:
- 電力網:節點是發電廠、變電站,邊是輸電線路。
- 道路網:節點是交叉口或城市,邊是道路。
- 航空網路:節點是機場,邊是航班。
- 範例:
- 時間網路 (Networks in Time):節點代表事件或時間點,邊代表它們之間的順序或因果關係。
- 範例:
- 維基百科連結:頁面之間的超連結,可以視為一種時間上的延續或知識的傳播。
- 事件序列:例如,一系列的交易、通訊記錄等。
- 範例:
- 地理位置網路 (Networks in Space):節點代表地理實體(如城市、機場、電網節點),邊代表它們之間的空間聯繫(如道路、航班、電纜)。
- 空間和時間屬性的影響:
- 在時空網路中,節點之間的距離(無論是地理距離還是時間間隔)和空間/時間屬性會顯著影響網路的性質和行為。
- 例如,在航空網路中,機場之間的距離和航班頻率(由旅客流量體現)是關鍵因素。
- 邊權重的多維性:
- 當邊代表物理連接時,其權重可能包含多個維度。例如,光纖電纜的邊權重可以同時考慮:
- 物理長度:影響信號傳輸延遲。
- 頻寬/容量:影響數據傳輸量。
- 成本:鋪設和維護的費用。
- 這些因素共同決定了連接的「有用性」和「價值」。
- 當邊代表物理連接時,其權重可能包含多個維度。例如,光纖電纜的邊權重可以同時考慮:
引力模型 (Gravity Models)
- 空間距離與互動強度:
- 在空間網路中,距離通常與「成本」相關聯,例如鋪設電纜的金錢成本、長途飛行的時間成本等。
- 因此,空間網路往往由許多短邊和較少的長邊構成。
- 許多互動的強度(例如,機場之間的旅客流量)會隨著距離的增加而減弱。
- 引力模型的原理:
- 引力模型是一種統計技術,用於校正空間距離對邊權重的影響,以便更準確地比較不同連接的「真實」強度。
- 核心假設:
- 兩個點之間互動的強度,平均而言,與它們之間距離的平方成反比。這類似於物理學中的萬有引力定律。
- 互動的強度也與節點的某些「質量」屬性(如節點的總流量或規模)成正比。例如,一個大型機場的總旅客流量越大,它與其他機場產生互動的可能性也越大。
- 數學形式:
互動強度 $I_{ij} \propto \frac{M_i M_j}{d_{ij}^2}$
其中:
- $I_{ij}$ 是節點 $i$ 和節點 $j$ 之間的互動強度。
- $M_i, M_j$ 是節點 $i$ 和 $j$ 的質量屬性(如總流量)。
- $d_{ij}$ 是節點 $i$ 和 $j$ 之間的距離。
- 應用:
- 引力模型可以幫助我們區分:一個邊的權重很高,是因為它很短(距離效應),還是因為它連接了兩個「活躍」的節點(質量效應)。
- 這對於理解機場交通、城市間貿易、通訊流量等空間互動模式至關重要。
處理空間數據的範例:美國大陸航空交通
- 資料集:
- 本節將展示如何使用
NetworkX處理空間數據,具體範例是分析 2018 年美國大陸的直飛航班數據。
- 本節將展示如何使用
- 網路構建:
- 節點:代表機場,與地理位置相關聯。
- 邊:代表機場之間的直飛航班。
- 邊權重:代表兩個機場之間的旅客流量。
- 分析目標:
- 構建這個空間網路。
- 利用引力模型來分析和校正距離對旅客流量的影響。
- 理解機場之間的互動模式。
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from pathlib import Path
# --- 時空網路基礎概念與引力模型
---
print("--- 時空網路基礎概念與引力模型
---
")
print("網路可以表示地理位置和事件之間的關係,其中距離和屬性影響互動強度。")
print("引力模型假設互動強度與節點質量成正比,與距離平方成反比。")
print("這有助於校正距離效應,分析真實的互動模式。")
# --- 處理空間數據範例:美國大陸航空交通
---
print("\n--- 處理空間數據範例:美國大陸航空交通 (2018)
---
")
# 假設數據文件路徑
# 在實際運行中,請確保 'data/BTS2018/carrier.csv' 路徑存在且包含正確的數據
# 為了示範,我們將模擬創建一個簡化的數據文件結構和內容
# 實際數據來源: Bureau of Transportation Statistics (BTS) - Air Carrier Traffic Statistics
# 創建模擬的數據目錄和文件
data_dir = Path('./data_simulated')
carrier_data_path = data_dir / 'BTS2018' / 'carrier.csv'
# 創建模擬數據 (如果文件不存在)
if not carrier_data_path.exists():
data_dir.mkdir(parents=True, exist_ok=True)
# 模擬一些機場和航班數據
# 節點: 機場 IATA 代碼
# 邊: 旅客流量 (Origin, Dest, Passengers)
mock_data = {
'ORIGIN_AIRPORT_ID': [10001, 10001, 10001, 10002, 10002, 10003, 10003, 10001, 10002, 10003],
'DEST_AIRPORT_ID': [10002, 10003, 10004, 10001, 10003, 10001, 10002, 10005, 10005, 10005],
'PASSENGERS': [1500, 800, 200, 1200, 900, 750, 600, 100, 150, 300],
'ORIGIN_CITY_NAME': ['Airport A', 'Airport A', 'Airport A', 'Airport B', 'Airport B', 'Airport C', 'Airport C', 'Airport A', 'Airport B', 'Airport C'],
'DEST_CITY_NAME': ['Airport B', 'Airport C', 'Airport D', 'Airport A', 'Airport C', 'Airport A', 'Airport B', 'Airport E', 'Airport E', 'Airport E'],
'ORIGIN_STATE_ABR': ['CA', 'CA', 'CA', 'NY', 'NY', 'TX', 'TX', 'CA', 'NY', 'TX'],
'DEST_STATE_ABR': ['NY', 'TX', 'WA', 'CA', 'TX', 'CA', 'NY', 'FL', 'FL', 'FL']
}
df_mock = pd.DataFrame(mock_data)
df_mock.to_csv(carrier_data_path, index=False)
print(f"已創建模擬數據文件: {carrier_data_path}")
# 載入網路
G_air = nx.Graph()
airport_data = {} # 儲存機場的屬性,例如城市、州
passenger_flow = {} # 儲存機場對之間的旅客流量
try:
with open(carrier_data_path) as f:
# 讀取 CSV 文件,使用 pandas 更方便
df_carrier = pd.read_csv(carrier_data_path)
# 處理數據以構建網路
# 這裡我們需要將 AIRPORT_ID 映射到更易讀的名稱或使用 IATA 代碼 (如果有的話)
# 為了簡化,我們假設 ID 就是節點名稱,並提取一些屬性
# 獲取所有唯一的機場 ID 作為節點
all_airport_ids = pd.concat([df_carrier['ORIGIN_AIRPORT_ID'], df_carrier['DEST_AIRPORT_ID']]).unique()
# 提取機場屬性 (例如,城市名稱和州)
# 注意: 同一個 AIRPORT_ID 可能有多個城市/州記錄,這裡取第一個出現的
for airport_id in all_airport_ids:
airport_info = df_carrier[df_carrier['ORIGIN_AIRPORT_ID'] == airport_id].iloc[0] if (df_carrier['ORIGIN_AIRPORT_ID'] == airport_id).any() else df_carrier[df_carrier['DEST_AIRPORT_ID'] == airport_id].iloc[0]
airport_data[airport_id] = {
'city': airport_info['ORIGIN_CITY_NAME'] if airport_info['ORIGIN_AIRPORT_ID'] == airport_id else airport_info['DEST_CITY_NAME'],
'state': airport_info['ORIGIN_STATE_ABR'] if airport_info['ORIGIN_AIRPORT_ID'] == airport_id else airport_info['DEST_STATE_ABR']
}
G_air.add_node(airport_id, city=airport_data[airport_id]['city'], state=airport_data[airport_id]['state'])
# 添加邊,表示航班和旅客流量
# 使用 groupby 來匯總同一對機場之間的旅客流量 (可能有多個航空公司)
grouped_flights = df_carrier.groupby(['ORIGIN_AIRPORT_ID', 'DEST_AIRPORT_ID'])['PASSENGERS'].sum().reset_index()
for index, row in grouped_flights.iterrows():
origin = row['ORIGIN_AIRPORT_ID']
dest = row['DEST_AIRPORT_ID']
passengers = row['PASSENGERS']
# 確保節點已存在
if origin not in G_air: G_air.add_node(origin)
if dest not in G_air: G_air.add_node(dest)
# 添加邊,權重為旅客流量
G_air.add_edge(origin, dest, weight=passengers)
passenger_flow[(origin, dest)] = passengers
passenger_flow[(dest, origin)] = passengers # 假設流量是雙向的,或需要單獨處理
print(f"成功載入網路: {G_air.number_of_nodes()} 個節點, {G_air.number_of_edges()} 條邊。")
# 為了後續的引力模型計算,我們需要機場的地理位置 (緯度/經度)
# 在這個範例中,我們沒有實際的地理位置數據,所以我們將模擬一些隨機位置
# 在真實應用中,您需要從外部數據源 (如 GeoNames, airports database) 獲取
print("注意:由於缺乏實際地理位置數據,這裡將模擬隨機的經緯度用於引力模型演示。")
# 模擬隨機經緯度
np.random.seed(42)
for node in G_air.nodes():
# 模擬美國大陸的經緯度範圍
# 經度: -125 (西海岸) 到 -66 (東海岸)
# 緯度: 24 (佛羅里達南部) 到 49 (北部邊界)
G_air.nodes[node]['longitude'] = np.random.uniform(-125, -66)
G_air.nodes[node]['latitude'] = np.random.uniform(24, 49)
# --- 計算距離和應用引力模型
---
# 函數:計算兩個地理點之間的距離 (使用 Haversine 公式,近似球面距離)
def haversine_distance(lat1, lon1, lat2, lon2):
R = 6371 # 地球半徑 (公里)
lat1_rad = np.radians(lat1)
lon1_rad = np.radians(lon1)
lat2_rad = np.radians(lat2)
lon2_rad = np.radians(lon2)
dlon = lon2_rad - lon1_rad
dlat = lat2_rad - lat1_rad
a = np.sin(dlat / 2)**2 + np.cos(lat1_rad) * np.cos(lat2_rad) * np.sin(dlon / 2)**2
c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a))
distance = R * c
return distance
# 計算所有邊的距離,並應用引力模型
# 引力模型預期值: I_ij ~ (M_i * M_j) / d_ij^2
# 我們需要計算節點的「質量」(例如,總進出港旅客數)
# 計算每個機場的總進出港旅客數 (質量 M)
node_mass = {}
for node in G_air.nodes():
total_passengers = 0
# 進港旅客
in_flights = df_carrier[df_carrier['DEST_AIRPORT_ID'] == node]
if not in_flights.empty:
total_passengers += in_flights['PASSENGERS'].sum()
# 出港旅客
out_flights = df_carrier[df_carrier['ORIGIN_AIRPORT_ID'] == node]
if not out_flights.empty:
total_passengers += out_flights['PASSENGERS'].sum()
node_mass[node] = total_passengers
G_air.nodes[node]['mass'] = total_passengers # 將質量屬性添加到節點
print("\n已計算節點質量 (總進出港旅客數)。")
# 計算邊的距離和引力模型預期流量
gravity_model_expected_flow = {}
for u, v, data in G_air.edges(data=True):
# 獲取節點屬性
lat1, lon1 = G_air.nodes[u]['latitude'], G_air.nodes[u]['longitude']
lat2, lon2 = G_air.nodes[v]['latitude'], G_air.nodes[v]['longitude']
mass_u, mass_v = G_air.nodes[u]['mass'], G_air.nodes[v]['mass']
# 計算距離
distance = haversine_distance(lat1, lon1, lat2, lon2)
# 計算引力模型預期的流量
# 避免除以零
if distance > 0 and mass_u > 0 and mass_v > 0:
# 使用 log 來處理數值範圍,或直接計算
# 這裡直接計算,並取 log 來比較
expected_flow = (mass_u * mass_v) / (distance**2)
gravity_model_expected_flow[(u, v)] = expected_flow
gravity_model_expected_flow[(v, u)] = expected_flow # 對稱
else:
gravity_model_expected_flow[(u, v)] = 0
gravity_model_expected_flow[(v, u)] = 0
# 將距離和預期流量添加到邊的屬性中
data['distance_km'] = distance
data['gravity_expected_flow'] = expected_flow
print("已計算所有邊的距離和引力模型預期流量。")
# --- 比較實際流量與引力模型預期流量
---
actual_flows = []
expected_flows_gravity = []
for u, v, data in G_air.edges(data=True):
# 獲取實際旅客流量 (注意: 我們的數據是匯總的,可能需要匹配)
# 由於我們是從頭構建的,這裡使用模擬的 passenger_flow
# 在真實數據中,這裡應該是從原始數據中查找對應的匯總值
# 為了簡化,我們直接使用邊的 'weight' 屬性作為實際流量
actual_flow = data.get('weight', 0)
# 獲取引力模型預期流量
expected_flow = data.get('gravity_expected_flow', 0)
# 僅當兩者都大於零時才進行比較,避免 log(0) 錯誤
if actual_flow > 0 and expected_flow > 0:
actual_flows.append(np.log10(actual_flow)) # 使用 log10 尺度
expected_flows_gravity.append(np.log10(expected_flow))
# 繪製散點圖比較實際流量與引力模型預期流量
plt.figure(figsize=(10, 6))
plt.scatter(actual_flows, expected_flows_gravity, alpha=0.5, s=10) # s 是點的大小
plt.plot([min(actual_flows), max(actual_flows)], [min(actual_flows), max(actual_flows)], 'r--', label='Perfect Match') # 繪製 y=x 線
plt.title("Actual Passenger Flow vs. Gravity Model Expected Flow (Log Scale)", fontsize=14)
plt.xlabel("Log10(Actual Passenger Flow)", fontsize=12)
plt.ylabel("Log10(Gravity Model Expected Flow)", fontsize=12)
plt.legend()
plt.grid(True, linestyle='--', alpha=0.6)
plt.show()
print("\n散點圖分析:")
print(" - 如果實際旅客流量與引力模型預期流量高度相關,點會聚集在紅色虛線附近。")
print(" - 這表明距離和節點質量在一定程度上可以解釋機場間的旅客流量。")
print(" - 偏差可能源於其他因素,如航班時刻、票價、競爭、地理位置的吸引力等。")
except FileNotFoundError:
print(f"錯誤: 數據文件 '{carrier_data_path}' 未找到。請確保數據文件存在於正確的路徑。")
except Exception as e:
print(f"處理數據時發生錯誤: {e}")
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100
start
:時空網路的基礎:地理位置、事件與引力模型;:時空網路的基礎概念;
note right
網路的多樣性:
- 節點與時間/空間相關聯
時空網路的表示:
- 地理位置網路 (Networks in Space): 節點=地點, 邊=空間聯繫
- 時間網路 (Networks in Time): 節點=事件/時間點, 邊=順序/因果
屬性影響:
- 距離、時間間隔影響網路性質
邊權重多維性:
- 長度、容量、成本等
end note
:引力模型 (Gravity Models);
note right
空間距離與互動強度:
- 距離增加,互動減弱
- 網路多短邊,少長邊
模型原理:
- 校正距離影響,比較真實強度
- 假設: 互動強度 ~ (M_i * M_j) / d_ij^2
- M: 節點質量 (如總流量)
- d: 節點間距離
應用:
- 區分距離效應與節點活躍度效應
end note
:處理空間數據範例:美國大陸航空交通;
note right
資料集:
- 2018 年美國大陸直飛航班數據
網路構建:
- 節點: 機場 (地理位置)
- 邊: 直飛航班 (旅客流量為權重)
分析目標:
- 構建空間網路
- 應用引力模型分析距離影響
- 理解機場互動模式
程式碼實現:
- 載入數據
- 構建節點與邊 (含質量、距離、預期流量)
- 計算距離 (Haversine)
- 比較實際流量與引力模型預期流量 (散點圖)
end note
stop
@enduml看圖說話:
此圖示總結了「時空網路的基礎:地理位置、事件與引力模型」的內容,重點在於介紹時空網路的基本概念,以及引力模型在分析空間互動中的作用。流程開頭首先聚焦於「時空網路的基礎概念」,說明了網路如何表示地理位置和時間關係,以及距離和屬性對網路行為的影響,接著詳細闡述了「引力模型」的原理,解釋了其核心假設(互動強度與節點質量成正比,與距離平方成反比)及其應用,最後展示了「處理空間數據範例:美國大陸航空交通」的實現流程,包括如何構建網路、計算距離和應用引力模型,並透過散點圖進行比較分析。
結論
縱觀現代商業生態的複雜連結,單純的數據指標已不足以支撐高階決策。引力模型不僅是網路分析的技術工具,更提供了一個關鍵的「校準思維框架」。它讓我們得以剝離地理距離這類物理限制的必然影響,從而清晰地辨識出哪些互動關係是真正「超乎預期」的強連結。傳統分析的瓶頸在於過度關注流量、銷量等絕對數值,而引力模型的核心價值,恰恰在於揭示那些偏離模型預測的「異常點」——這些點往往隱藏著獨特的市場機會、未被滿足的需求,或是潛在的結構性風險。
展望未來,這類融合物理學、社會學與數據科學的跨領域分析模型,將從學術應用擴展至商業策略的核心,被廣泛應用於供應鏈韌性、人才流動乃至品牌影響力的評估。
玄貓認為,高階管理者學習此模型的精髓,不在於精通其數學公式,而在於培養一種「關係洞察力」:識別出那些超越物理限制的卓越連結,並探究其背後的策略價值。這才是將數據轉化為決策智慧的關鍵躍升。