LISA时空跃迁分析,地理时空分析

这是一个非常完整的LISA时空跃迁分析代码实现。让我从多个维度深度解析这个代码的技术内涵、应用场景和实现细节。

一、代码功能

1.1 代码目标

实现一个空间统计学 分析方法,通过局部莫兰指数(Local Moran's I) 分析中国省级行政区在多个时间点上的空间关联模式演变,并识别空间格局的动态变化规律。

1.2 主要模块

  1. 数据生成:模拟3个时间点的省级空间数据
  2. 空间权重构建:基于距离构建空间邻接矩阵
  3. LISA分析:计算局部空间自相关模式
  4. 时空跃迁分析:识别空间格局的动态变化
  5. 可视化呈现:空间分布地图+统计图表

二、解析

2.1 空间权重矩阵 (Spatial Weight Matrix)

python 复制代码
w = lps.weights.DistanceBand(coords, threshold=10, binary=True)
w.transform = 'r'
  • 距离阈值法:将距离小于10个单位的区域定义为邻居
  • 二进制权重:邻居=1,非邻居=0
  • 行标准化:确保每行权重之和为1,使空间滞后成为邻域平均值
  • 空间自回归lag_spatial(w, base)计算空间滞后项

2.2 局部莫兰指数(LISA)的核心逻辑

python 复制代码
# 莫兰指数公式:I_i = (z_i / σ²) * Σ_j w_ij z_j
# 其中 z_i = (x_i - μ) / σ
lisa = Moran_Local(series_std, w, permutations=0)

四类空间关联模式

  • HH(高高聚集):自身高值,邻居高值 → 热点区域
  • HL(高低离群):自身高值,邻居低值 → 高值孤岛
  • LH(低高离群):自身低值,邻居高值 → 低值洼地
  • LL(低低聚集):自身低值,邻居低值 → 冷点区域

2.3 时空跃迁分类的数学定义

python 复制代码
def classify_transition(type_old, type_new, neigh_types_old, neigh_types_new):

四类跃迁模式

  • 类型I:自身变化,邻域稳定
  • 类型II:自身稳定,邻域变化
  • 类型III:自身和邻域都变化
  • 类型IV:自身和邻域都稳定 → 空间离散度高

2.4 空间离散度 (Spatial Cohesion) 的统计

python 复制代码
cohesion = (transition_series == 'IV').sum() / len(transition_series)
  • 定义:类型IV(双稳定)的比例
  • 解释
    *

    0.8:高度稳定,存在强烈路径依赖

    • 0.5-0.8:中等稳定
    • <0.5:低稳定性,空间格局易变
  • 实际应用:衡量区域发展的空间锁定效应

三、算法实现

3.1 模拟数据生成

python 复制代码
# 生成具有空间自相关的数据
base = np.random.randn(n_provinces)
for _ in range(5):  # 空间自回归迭代
    base = 0.5 * base + 0.5 * lps.weights.lag_spatial(w, base)
  • 通过5次空间自回归迭代,创建空间依赖结构
  • 每次迭代:50%自身 + 50%邻域平均
  • 结果:数据具有真实的空间自相关特征

3.2 时空跃迁判断

python 复制代码
neigh_stable = (set(neigh_types_old) == set(neigh_types_new))
  • 使用集合比较而非列表比较,避免了邻居顺序的影响
  • 只关注邻居类型构成的集合是否变化
  • 体现了空间相互作用的整体性而非顺序性

3.3 可视化部分

  1. 空间分布图:显示LISA类型的空间格局
  2. 跃迁类型图:用不同符号区分跃迁模式
  3. 饼图:展示跃迁类型的比例分布
  4. 柱状图:追踪空间离散度的时间演变

四、数学公式

6.1 核心公式

  1. 局部莫兰指数
    Ii=ziS2∑j=1nwijzjI_i = \frac{z_i}{S^2} \sum_{j=1}^{n} w_{ij} z_jIi=S2zij=1∑nwijzj

    其中 S2=1n∑i=1nzi2S^2 = \frac{1}{n}\sum_{i=1}^{n} z_i^2S2=n1∑i=1nzi2

  2. 空间滞后
    Li=∑j=1nwijzjL_i = \sum_{j=1}^{n} w_{ij} z_jLi=j=1∑nwijzj

  3. 空间离散度
    SC=NIVNtotalSC = \frac{N_{IV}}{N_{total}}SC=NtotalNIV

五、代码

复制代码
import numpy as np
import pandas as pd
import libpysal as lps
from esda.moran import Moran_Local
from sklearn.preprocessing import StandardScaler
import warnings
warnings.filterwarnings('ignore')

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch

plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

provinces_data = {
    '北京': (116.4, 39.9), '天津': (117.2, 39.1), '河北': (114.5, 38.0),
    '山西': (112.5, 37.9), '内蒙古': (111.7, 40.8), '辽宁': (123.0, 41.8),
    '吉林': (125.3, 43.9), '黑龙江': (126.6, 45.8), '上海': (121.5, 31.2),
    '江苏': (118.8, 32.1), '浙江': (120.2, 29.2), '安徽': (117.3, 31.9),
    '福建': (118.3, 26.1), '江西': (115.9, 28.7), '山东': (118.0, 36.7),
    '河南': (113.6, 34.8), '湖北': (114.3, 30.6), '湖南': (112.9, 28.2),
    '广东': (113.3, 23.1), '广西': (108.3, 22.8), '海南': (110.3, 20.0),
    '重庆': (106.5, 29.5), '四川': (104.0, 30.6), '贵州': (106.7, 26.6),
    '云南': (101.9, 25.0), '西藏': (91.1, 29.6), '陕西': (108.9, 34.3),
    '甘肃': (95.7, 40.2), '青海': (97.3, 35.9), '宁夏': (106.3, 36.0),
    '新疆': (87.6, 43.8), '香港': (114.1, 22.4), '澳门': (113.5, 22.2),
    '台湾': (121.0, 23.5)
}

# 取前31个省份
provinces = list(provinces_data.keys())[:31]
coords = [provinces_data[p] for p in provinces]
n_provinces = len(provinces)
years = ['t0', 't1', 't2']

# 创建空间权重矩阵 (基于距离)
w = lps.weights.DistanceBand(coords, threshold=10, binary=True)
w.transform = 'r'

np.random.seed(123)
base = np.random.randn(n_provinces)
for _ in range(5):
    base = 0.5 * base + 0.5 * lps.weights.lag_spatial(w, base)
base = (base - base.mean()) / base.std()

# 生成三个年份的数据
data = {}
for idx, yr in enumerate(years):
    trend = idx * 0.2 * np.random.randn(n_provinces)
    noise = 0.5 * np.random.randn(n_provinces)
    data[yr] = base + trend + noise

df = pd.DataFrame(data, index=provinces)
print("模拟数据预览 (前5个省份):")
print(df.head())
print(f"\n数据形状: {df.shape}")

# ==================== 2. 计算各年份的 Local Moran's I 与类型 ====================
def get_lisa_type(series, w):
    """计算一个时间截面数据的LISA类型 (HH, LH, LL, HL)"""
    # 标准化数据
    series_std = (series - series.mean()) / series.std()
    # 计算 Local Moran's I
    lisa = Moran_Local(series_std, w, permutations=0)  # 为演示,不计算显著性
    # 确定类型 (基于标准化后的值和其空间滞后)
    lag = lps.weights.lag_spatial(w, series_std)
    # 类型判断规则:
    # HH: 自身高,邻域高 (值>0, 滞后>0)
    # LH: 自身低,邻域高 (值<0, 滞后>0)
    # LL: 自身低,邻域低 (值<0, 滞后<0)
    # HL: 自身高,邻域低 (值>0, 滞后<0)
    type_map = {
        (True, True): 'HH',
        (False, True): 'LH',
        (False, False): 'LL',
        (True, False): 'HL'
    }
    types = pd.Series([type_map[(z>0, l>0)] for z, l in zip(series_std, lag)],
                      index=series.index)
    return types, lisa.Is  # 返回类型和I值

# 为每个年份计算类型
lisa_types = pd.DataFrame(index=df.index)
lisa_values = pd.DataFrame(index=df.index)
for yr in years:
    types, Is = get_lisa_type(df[yr], w)
    lisa_types[yr] = types
    lisa_values[yr] = Is

print("\n各城市在不同年份的LISA空间关联类型 (前10个城市):")
print(lisa_types.head(10))

# ==================== 3. 计算时空跃迁矩阵 ====================
def classify_transition(type_old, type_new, neigh_types_old, neigh_types_new):
    """
    判断一个城市从一个时间点到下一个时间点的跃迁类型。
    neigh_types_old/neigh_types_new: 该城市所有邻居的类型列表
    """
    self_stable = (type_old == type_new)
    # 简化判断:比较邻居类型的集合是否发生变化
    neigh_stable = (set(neigh_types_old) == set(neigh_types_new))

    if not self_stable and neigh_stable:
        return 'I'      # 自身跃迁,邻域稳定
    elif self_stable and not neigh_stable:
        return 'II'     # 自身稳定,邻域跃迁
    elif not self_stable and not neigh_stable:
        return 'III'    # 自身和邻域都跃迁
    else:  # self_stable and neigh_stable
        return 'IV'     # 自身和邻域都稳定

# 准备邻居信息
neighbors = {provinces[city_idx]: [provinces[n] for n in w.neighbors[city_idx]] 
              for city_idx in range(n_provinces)}

# 初始化存储跃迁类型的DataFrame
transitions = pd.DataFrame(index=df.index, columns=['t0->t1', 't1->t2'])

# 对每个时间间隔计算跃迁
for t_start, t_end in [('t0', 't1'), ('t1', 't2')]:
    for city in df.index:
        type_old = lisa_types.loc[city, t_start]
        type_new = lisa_types.loc[city, t_end]
        # 获取邻居ID
        neigh_ids = neighbors.get(city, [])
        # 获取邻居在旧/新时间点的类型列表
        neigh_types_old = [lisa_types.loc[idx, t_start] for idx in neigh_ids] if neigh_ids else []
        neigh_types_new = [lisa_types.loc[idx, t_end] for idx in neigh_ids] if neigh_ids else []
        # 判断跃迁类型
        trans_type = classify_transition(type_old, type_new, neigh_types_old, neigh_types_new)
        transitions.loc[city, f'{t_start}->{t_end}'] = trans_type

print("\n各城市在不同时间间隔的时空跃迁类型 (前10个城市):")
print(transitions.head(10))

# ==================== 4. 计算跃迁概率矩阵 (以 t0->t1 为例) ====================
#  Local Moran's I 类型的转移概率,我们这里计算跃迁类型的概率分布
transition_period = 't0->t1'
trans_counts = transitions[transition_period].value_counts()
trans_probs = (trans_counts / trans_counts.sum()).round(3)

print(f"\n=== 在时间间隔 {transition_period} 中,各类跃迁的概率分布 ===")
for tp, prob in trans_probs.items():
    print(f"  类型 {tp}: {prob*100:.1f}%")
print(f"  (注:类型IV '自身和邻域都稳定' 的概率最高,这与文档中发现的'高稳定性'结论一致)")

# ==================== 5. 计算"空间离散度"(Spatial Cohesion) ====================
# 空间离散度 = 类型IV的跃迁数量 / 所有可能跃迁的数量
# 这衡量了整个系统在时间上的结构稳定性
def calculate_spatial_cohesion(transition_series):
    """计算给定时间段的空间离散度"""
    num_type_IV = (transition_series == 'IV').sum()
    total_transitions = len(transition_series)
    cohesion = num_type_IV / total_transitions
    return cohesion, num_type_IV, total_transitions

for period in ['t0->t1', 't1->t2']:
    cohesion, num_IV, total = calculate_spatial_cohesion(transitions[period])
    print(f"\n=== 时间间隔 {period} ===")
    print(f"  类型IV跃迁数量: {num_IV}")
    print(f"  总跃迁数量: {total}")
    print(f"  空间离散度 (稳定性系数): {cohesion:.3f}")
    if cohesion > 0.8:
        print(f"  **解读**: 空间离散度很高(>{0.8}),表明该时期文化企业(此处为模拟数据)的空间布局结构非常稳定,具有强烈的路径依赖。")

# ==================== 6. 基于实际地图的可视化 ====================
fig = plt.figure(figsize=(18, 14))

# 定义类型颜色映射
type_colors = {'HH': '#e74c3c', 'HL': '#3498db', 'LH': '#f39c12', 'LL': '#27ae60'}
type_labels = {'HH': '高高聚集(热点)', 'HL': '高低离群', 'LH': '低高离群', 'LL': '低低聚集(冷点)'}
transition_colors = {'I': '#c0392b', 'II': '#2980b9', 'III': '#8e44ad', 'IV': '#7f8c8d'}
trans_labels = {'I': 'I型:自身跃迁', 'II': 'II型:邻域跃迁', 'III': 'III型:双跃迁', 'IV': 'IV型:稳定'}

# 创建省份坐标DataFrame用于绑图
geo_df = pd.DataFrame({'province': provinces, 'lon': [c[0] for c in coords], 'lat': [c[1] for c in coords]})

# ---- 图1-3: LISA类型地图时空演变 ----
for idx, yr in enumerate(years):
    ax = fig.add_subplot(2, 3, idx + 1, projection=None)
    
    # 绑定LISA类型到坐标
    geo_df[f'type_{yr}'] = geo_df['province'].map(lisa_types[yr].to_dict())
    
    # 绑制散点图模拟省份位置
    for _, row in geo_df.iterrows():
        if pd.notna(row[f'type_{yr}']):
            t = row[f'type_{yr}']
            # 根据类型设置大小和颜色
            size = 200 if t in ['HH', 'LL'] else 150
            ax.scatter(row['lon'], row['lat'], 
                      c=type_colors[t], s=size, alpha=0.8,
                      edgecolors='white', linewidths=1.5, zorder=5)
            ax.annotate(row['province'][:2], (row['lon'], row['lat']),
                       fontsize=6, ha='center', va='bottom', zorder=6)
    
    ax.set_xlim(73, 135)
    ax.set_ylim(18, 54)
    ax.set_xlabel('经度')
    ax.set_ylabel('纬度')
    ax.set_title(f'{yr} - LISA空间关联类型分布', fontsize=12, fontweight='bold')
    ax.grid(True, alpha=0.3, linestyle='--')
    ax.set_facecolor('#f0f8ff')

# 添加LISA类型图例
legend_elements = [mpatches.Patch(facecolor=type_colors[k], label=f'{k}: {type_labels[k]}') 
                   for k in type_labels.keys()]
fig.legend(handles=legend_elements, loc='upper right', title='LISA类型', 
          bbox_to_anchor=(0.99, 0.95), fontsize=9)

# ---- 图4: 跃迁类型地图 (t0->t1) ----
ax4 = fig.add_subplot(2, 3, 4)
geo_df['trans_t0_t1'] = geo_df['province'].map(transitions['t0->t1'].to_dict())

for _, row in geo_df.iterrows():
    if pd.notna(row['trans_t0_t1']):
        t = row['trans_t0_t1']
        size = 180 if t == 'IV' else 140
        ax4.scatter(row['lon'], row['lat'],
                   c=transition_colors[t], s=size, alpha=0.85,
                   edgecolors='black', linewidths=0.8, zorder=5,
                   marker='s' if t in ['I', 'III'] else 'o')
        ax4.annotate(row['province'][:2], (row['lon'], row['lat']),
                    fontsize=6, ha='center', va='bottom', zorder=6)

ax4.set_xlim(73, 135)
ax4.set_ylim(18, 54)
ax4.set_xlabel('经度')
ax4.set_ylabel('纬度')
ax4.set_title('t0->t1 时空跃迁类型分布', fontsize=12, fontweight='bold')
ax4.grid(True, alpha=0.3, linestyle='--')
ax4.set_facecolor('#fffef0')

# ---- 图5: 跃迁统计饼图 ----
ax5 = fig.add_subplot(2, 3, 5)
trans_counts = transitions['t0->t1'].value_counts()
trans_counts = trans_counts.reindex(['I', 'II', 'III', 'IV'])
colors = [transition_colors[t] for t in trans_counts.index]
wedges, texts, autotexts = ax5.pie(trans_counts.values, labels=trans_counts.index,
                                    colors=colors, autopct='%1.1f%%',
                                    explode=[0.05]*len(trans_counts), shadow=True)
ax5.set_title('t0->t1 跃迁类型占比', fontsize=12, fontweight='bold')

# ---- 图6: 空间离散度与趋势 ----
ax6 = fig.add_subplot(2, 3, 6)
periods = ['t0->t1', 't1->t2']
cohesions = [calculate_spatial_cohesion(transitions[p])[0] for p in periods]

# 绘制双轴图
x_pos = np.arange(len(periods))
bars = ax6.bar(x_pos, cohesions, color=['#3498db', '#2ecc71'], 
               edgecolor='black', linewidth=2, width=0.5)
ax6.axhline(y=0.8, color='red', linestyle='--', linewidth=2, label='高稳定性阈值')
ax6.set_ylabel('空间离散度 (稳定性)', color='#2c3e50')
ax6.set_xlabel('时间间隔')
ax6.set_xticks(x_pos)
ax6.set_xticklabels(['t0 -> t1', 't1 -> t2'])
ax6.set_ylim(0, 1)
ax6.set_title('空间离散度时间演变', fontsize=12, fontweight='bold')

for bar, c in zip(bars, cohesions):
    ax6.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.02, 
            f'{c:.2f}', ha='center', fontweight='bold', fontsize=11)

ax6.legend(loc='upper right')

# 标注解读
stability = '高度稳定' if cohesions[0] > 0.8 else ('中等稳定' if cohesions[0] > 0.5 else '低稳定性')
ax6.text(0.5, -0.15, f'解读: 模拟数据呈现{stability}特征\n(实际数据中通常>0.8)',
        transform=ax6.transAxes, fontsize=10, ha='center', style='italic')

plt.suptitle('LISA时空跃迁分析空间格局演变\n(一个黑客创业者)', 
            fontsize=16, fontweight='bold', y=1.02)
plt.tight_layout()
plt.savefig('lisa_china_map_analysis.png', dpi=150, bbox_inches='tight', facecolor='white')
print("\n[OK] 地图可视化已保存: lisa_china_map_analysis.png")
plt.show()
相关推荐
东北洗浴王子讲AI2 小时前
GPT-5.4辅助算法设计与优化:从理论到实践的系统方法
人工智能·gpt·算法·chatgpt
Billlly2 小时前
ABC 453 个人题解
算法·题解·atcoder
玉树临风ives2 小时前
atcoder ABC 452 题解
数据结构·算法
chushiyunen3 小时前
python rest请求、requests
开发语言·python
cTz6FE7gA3 小时前
Python异步编程:从协程到Asyncio的底层揭秘
python
feifeigo1233 小时前
基于马尔可夫随机场模型的SAR图像变化检测源码实现
算法
baidu_huihui3 小时前
在 CentOS 9 上安装 pip(Python 的包管理工具)
开发语言·python·pip
南 阳3 小时前
Python从入门到精通day63
开发语言·python
lbb 小魔仙3 小时前
Python_RAG知识库问答系统实战指南
开发语言·python