MRRA LogoMRRA
核心概念

移动图

异构移动图的概念、结构和使用

移动图

移动图是使用多类型异构图(networkx.MultiDiGraph)对"人-位置-时间-目的"关系的统一表示。它能实现移动模式的检索、推理和可解释分析。

图结构

节点类型

移动图包含多种类型的节点,每种代表移动性的不同方面:

格式u_<user_id>

代表系统中的个体用户。

# 用户节点示例
"u_user_123"
"u_user_456" 

格式g_<gy>_<gx> (基于网格)

属性

  • gy, gx:网格坐标
  • lat, lon:近似坐标(使用活动构建时)
# 位置节点示例
"g_1234_5678"  # y=1234, x=5678处的网格单元
"g_2345_6789"  # 另一个网格单元

小时节点h_<0..23> 星期几节点d_<0..6> (0=周一) 时间段节点t_<hour>_<dow>

# 时间节点示例
"h_9"          # 上午9点
"d_1"          # 周二  
"t_9_1"        # 周二上午9点

格式p_<purpose>

属性

  • name:目的描述
# 目的节点示例
"p_work"       # 工作活动
"p_dining"     # 用餐活动
"p_home"       # 家庭/住宅活动

边类型和权重

图包含多种类型的边,代表不同的关系:

用户-位置边

  • 方向:用户 → 位置
  • 权重:访问强度(基于点构建时为计数;基于活动构建时为分钟)
  • 属性activity_typeactivity_purpose

时间共现边

  • 方向:双向(loc ↔ hour/dow/timebin)
  • 权重:共现强度(分钟/计数)
  • 目的:捕捉位置通常被访问的时间

目的-上下文边

  • 方向:双向(purpose ↔ loc/hour/dow/timebin)
  • 权重:共现强度(分钟)
  • 目的:将目的与空间和时间上下文连接

用户-目的边

  • 方向:用户 → 目的
  • 权重:在该目的上花费的总时间(分钟)
  • 目的:用户活动画像

转换边

  • 位置转换:loc → loc(相邻活动转换)
  • 目的转换:purpose → purpose(相邻活动目的转换)
  • 属性:包含用户信息用于个性化分析

图构建

基本构庻

from mrra.graph.mobility_graph import MobilityGraph, GraphConfig

# 配置图参数
cfg = GraphConfig(
    grid_size_m=200,           # 网格单元大小(米)
    min_dwell_minutes=5,       # 活动的最小停留时间
    use_activities=True        # 使用基于活动的构庻
)

# 构庻图
mg = MobilityGraph(tb, cfg, activities=acts, assume_purposes_assigned=True)
G = mg.G  # 访问networkx图

配置选项

# 详细配置示例
cfg = GraphConfig(
    grid_size_m=200,              # 网格分辨率
    min_dwell_minutes=5,          # 活动检测阈值
    use_activities=True,          # 使用活动 vs 原始点
    include_transitions=True,     # 包含loc→loc边
    purpose_transitions=True,     # 包含purpose→purpose边
    temporal_granularity='hour', # 'hour'、'timebin'或'both'
    weight_by_duration=True      # 按花费时间加权边
)

使用自定义活动的高级构庻

# 使用带目的的预计算活动
from mrra.data.activity import ActivityExtractor
from mrra.analysis.activity_purpose import ActivityPurposeAssigner

# 提取并分配目的到活动
ext_cfg = dict(method="radius", radius_m=300, min_dwell_minutes=30)
acts = ActivityExtractor(tb, **ext_cfg).extract()
acts = ActivityPurposeAssigner(tb, llm=llm, concurrency=8).assign(acts)

# 使用目的丰富的活动构庻图  
cfg = GraphConfig(grid_size_m=200, use_activities=True)
mg = MobilityGraph(tb, cfg, activities=acts, assume_purposes_assigned=True)

图分析

基本图统计

# 获取基本图信息
print(f"节点数: {G.number_of_nodes()}")
print(f"边数: {G.number_of_edges()}")

# 分析节点类型
node_types = {}
for node in G.nodes():
    node_type = node.split('_')[0]
    node_types[node_type] = node_types.get(node_type, 0) + 1

print("节点类型分布:", node_types)

中心性分析

import networkx as nx

# 计算中心性指标
degree_centrality = nx.degree_centrality(G)
betweenness_centrality = nx.betweenness_centrality(G, k=1000)  # 大图采样

# 找到最中心的位置
location_nodes = [n for n in G.nodes() if n.startswith('g_')]
top_locations = sorted(
    [(n, degree_centrality[n]) for n in location_nodes], 
    key=lambda x: x[1], 
    reverse=True
)[:10]

print("最中心的位置:")
for loc, centrality in top_locations:
    print(f"  {loc}: {centrality:.4f}")

目的分析

# 分析目的模式
purpose_nodes = [n for n in G.nodes() if n.startswith('p_')]

purpose_stats = {}
for purpose in purpose_nodes:
    # 获取连接的位置
    connected_locs = [n for n in G.neighbors(purpose) if n.startswith('g_')]
    
    # 获取连接的时间  
    connected_hours = [n for n in G.neighbors(purpose) if n.startswith('h_')]
    
    purpose_stats[purpose] = {
        'locations': len(connected_locs),
        'peak_hours': connected_hours,
        'total_weight': sum(G[purpose][loc]['weight'] for loc in connected_locs)
    }

print("目的分析:")
for purpose, stats in purpose_stats.items():
    print(f"  {purpose}: {stats}")

图检索(GraphRAG)

GraphRAGGenerate类实现了复杂的基于图的检索:

基本检索

from mrra.retriever.graph_rag import GraphRAGGenerate

retriever = GraphRAGGenerate(tb=tb, mobility_graph=mg)

# 为查询检索相关位置
docs = retriever.get_relevant_documents({
    "user_id": "user_123",
    "t": "2024-09-10 12:30:00",    # 可选时间戳
    "purpose": ["dining", "work"],   # 可选目的过滤  
    "k": 8                          # 结果数量
})

for doc in docs:
    print(f"位置: {doc.metadata['node']}, 得分: {doc.metadata['score']:.4f}")

检索配置

# 配置检索权重
retriever.purpose_weight = 0.6   # 目的种子重要性
retriever.hour_weight = 0.5      # 小时上下文重要性  
retriever.dow_weight = 0.3       # 星期几重要性
retriever.recent_weight = 0.2    # 最近位置偏向

# 使用多个种子的高级检索
docs = retriever.get_relevant_documents({
    "user_id": "user_123",
    "purpose": "dining",           # 单个目的
    "hour": 12,                   # 显式小时
    "dow": 1,                     # 周二
    "recent_locations": ["g_1234_5678"],  # 最近上下文
    "k": 10
})

图持久化和缓存

保存和加载图

from mrra.persist.cache import CacheManager

cm = CacheManager()
tb_hash = compute_tb_hash(tb)

# 保存图
cm.save_graph(tb_hash, "mobility_default", mg.G)

# 加载图
cached_graph = cm.load_graph(tb_hash, "mobility_default")
if cached_graph:
    mg.G = cached_graph
    print("加载了缓存图")

图版本管理

# 使用配置特定键保存
config_key = f"mobility_grid{cfg.grid_size_m}_dwell{cfg.min_dwell_minutes}"
cm.save_graph(tb_hash, config_key, mg.G)

# 加载特定版本
specific_graph = cm.load_graph(tb_hash, config_key)

GraphRAG原理:使用user/hour/day/purpose节点作为"种子",通过边权重和种子类型权重传播到位置节点。这提供了更语义化的位置排名,同时结合来自最近位置的新近性偏向。

性能考虑

大图优化

# 对于大数据集,考虑子图提取
def extract_user_subgraph(G, user_id, hops=2):
    """提取特定用户周围的子图"""
    import networkx as nx
    
    user_node = f"u_{user_id}"
    if user_node not in G:
        return nx.MultiDiGraph()
    
    # 获取n跳距离内的节点
    subgraph_nodes = set([user_node])
    current_nodes = {user_node}
    
    for _ in range(hops):
        next_nodes = set()
        for node in current_nodes:
            next_nodes.update(G.neighbors(node))
        subgraph_nodes.update(next_nodes)
        current_nodes = next_nodes
    
    return G.subgraph(subgraph_nodes).copy()

# 使用子图进行用户特定分析
user_subgraph = extract_user_subgraph(mg.G, "user_123")

内存管理

# 对于内存受限的环境
def compress_graph_weights(G, precision=3):
    """压缩边权重以减少内存使用"""
    for src, dst, key, data in G.edges(data=True, keys=True):
        if 'weight' in data:
            G[src][dst][key]['weight'] = round(data['weight'], precision)
    return G

# 应用压缩
mg.G = compress_graph_weights(mg.G)

下一步