用 AI 辅助分析历史经济周期:按朝代切片的数据科学实验

📅 2026/7/23 10:31:52
用 AI 辅助分析历史经济周期:按朝代切片的数据科学实验
用 AI 辅助分析历史经济周期按朝代切片的数据科学实验一、个性化深度引言经济学教材上写着“经济危机大约每 10 年一次”但这基于 20 世纪的工业经济数据。有没有可能从更长的历史维度看经济周期我们做了一次实验将中国从秦汉到明清 2000 多年的经济史数据整理成结构化数据用统计和机器学习方法分析周期性规律。这不是一个严谨的经济学研究——历史数据的质量远不能和现代统计数据相比。但方法论上有趣的发现是即使数据充满噪声一些跨朝代的统计规律仍然浮现出来。见证奇迹的时刻在于当把朝代更替作为断点做结构性变化检验时数据自己讲述了比教科书更丰富的故事。二、个性化原理剖析历史经济周期分析框架数据处理中的关键挑战1. 数据稀疏性古代经济数据的覆盖度极不均匀。宋以后有相对详细的粮价记录之前的只能靠《史记》《汉书》中的碎片化信息。处理方法对稀疏时期使用线性插值但必须标注插值区间以降低对分析结果的影响权重。2. 计量单位不一致唐代用“石”宋代用“斗”明代用“两”白银。需要在统一单位之前追溯每个朝代的度量衡换算关系。3. 记录偏差史书记录偏向“大事件”——大丰收、大饥荒、大规模战争。日常年份的数据反而是缺失的。这种“极端值偏向”会影响均值和方差的估计。分析方法的选择频谱分析将时间序列转换到频域寻找周期性成分。傅里叶变换 小波变换可用于非平稳序列。结构断点检验检验时间序列在朝代更替时是否存在结构性变化。Chow Test 或 Bai-Perron 检验。事件研究法以重大事件如变法、战争为中心观察前后 N 年的经济指标变化模式。三、个性化代码实践import numpy as np import pandas as pd from typing import List, Dict, Tuple, Optional from dataclasses import dataclass from scipy import signal, stats from collections import defaultdict dataclass class Dynasty: 朝代数据结构 name: str start_year: int end_year: int capital: str notes: str class HistoricalEconomicAnalyzer: 历史经济周期分析器 重要说明历史数据的准确性有限 本分析目的是方法探索结论需谨慎解释 # 设计原因中国主要朝代时间线 # 数据来源《中国大百科全书》历史卷 # 朝代起止年份在学界有不同观点此处采用通用分类 DYNASTIES [ Dynasty(秦, -221, -206, 咸阳), Dynasty(西汉, -206, 8, 长安), Dynasty(新, 9, 23, 长安), Dynasty(东汉, 25, 220, 洛阳), Dynasty(三国, 220, 280, 多都), Dynasty(西晋, 265, 316, 洛阳), Dynasty(东晋, 317, 420, 建康), Dynasty(南北朝, 420, 589, 多都), Dynasty(隋, 581, 618, 大兴), Dynasty(唐, 618, 907, 长安), Dynasty(五代十国, 907, 960, 多都), Dynasty(北宋, 960, 1127, 开封), Dynasty(南宋, 1127, 1279, 临安), Dynasty(元, 1271, 1368, 大都), Dynasty(明, 1368, 1644, 北京), Dynasty(清, 1644, 1911, 北京), ] def __init__(self): self.data: pd.DataFrame None self.results: Dict {} def generate_synthetic_data(self) - pd.DataFrame: 生成模拟历史经济数据 设计原因真实古代经济数据的获取极为困难 此处用模拟数据展示分析方法框架 np.random.seed(42) # 时间范围公元前 200 年到公元 1911 年 years np.arange(-200, 1912) n len(years) # 基础趋势长期增长 周期成分 trend 0.00001 * years # 极其缓慢的增长 # 设计原因40-60 年周期Kondratiev 波浪的假设 cycle_60 0.3 * np.sin(2 * np.pi * years / 60) cycle_120 0.2 * np.sin(2 * np.pi * years / 120) # 朝代效应每个朝代的基线水平略有不同 dynasty_effect np.zeros(n) for dyn in self.DYNASTIES: mask (years dyn.start_year) (years dyn.end_year) if mask.any(): # 设计原因每个朝代的经济基线不同 dynasty_effect[mask] np.random.normal(0, 0.15) # 随机波动 noise np.random.normal(0, 0.2, n) # 合成经济指标如人均粮食产量指数 economic_index trend cycle_60 cycle_120 dynasty_effect noise # 添加一些重大事件的冲击 events { -200: -0.5, # 秦末战乱 8: -0.3, # 王莽代汉 184: -0.4, # 黄巾起义 755: -0.6, # 安史之乱 1127: -0.5, # 靖康之变 1644: -0.4, # 明末农民起义 } for year, shock in events.items(): if year in years: idx list(years).index(year) # 冲击影响持续 5 年 for j in range(5): if idx j n: economic_index[idx j] shock * (0.8 ** j) df pd.DataFrame({ year: years, economic_index: economic_index, }) # 添加朝代标签 df[dynasty] 未知 for dyn in self.DYNASTIES: mask (df[year] dyn.start_year) (df[year] dyn.end_year) df.loc[mask, dynasty] dyn.name return df def spectral_analysis( self, df: pd.DataFrame, indicator: str economic_index ) - Dict: 频谱分析寻找周期成分 设计原因傅里叶变换将时域信号转为频域 峰值频率对应周期成分 values df[indicator].dropna().values # 去趋势避免趋势信号干扰周期检测 from scipy.signal import detrend detrended detrend(values) # 傅里叶变换 fft np.fft.fft(detrended) freqs np.fft.fftfreq(len(detrended)) # 只考虑正频率 pos_mask freqs 0 freqs_pos freqs[pos_mask] power np.abs(fft[pos_mask]) ** 2 # 找到峰值 from scipy.signal import find_peaks peaks, properties find_peaks( power, heightnp.percentile(power, 95), # 只取最高 5% distance5, ) # 设计原因频率 → 周期年 peak_periods [] for peak_idx in peaks: if freqs_pos[peak_idx] 0: period 1 / freqs_pos[peak_idx] if 10 period 300: # 只关注 10-300 年的周期 peak_periods.append({ period_years: round(period, 1), frequency: freqs_pos[peak_idx], power: power[peak_idx], }) return { peak_periods: sorted( peak_periods, keylambda x: x[power], reverseTrue )[:5], # Top 5 dominant_period: peak_periods[0][period_years] if peak_periods else None, } def structural_break_test( self, df: pd.DataFrame, indicator: str economic_index, ) - pd.DataFrame: 结构性变化检验朝代更替是否为断点 设计原因如果朝代更替是统计意义上的断点 说明政权变化对经济有实质性影响 values df[indicator].values years df[year].values n len(values) break_results [] # 对每个朝代边界做检验 for i, dyn in enumerate(self.DYNASTIES): # 找到朝代开始年份在数据中的位置 start_idx np.searchsorted(years, dyn.start_year) end_idx np.searchsorted(years, dyn.end_year) # 设计原因断点前后至少需要 30 个数据点 if start_idx 30 or (n - start_idx) 30: continue # 简单版本比较朝代前后 30 年的均值差异 before values[max(0, start_idx-30):start_idx] after values[start_idx:min(n, start_idx30)] if len(before) 0 and len(after) 0: t_stat, p_value stats.ttest_ind(before, after) break_results.append({ dynasty: dyn.name, start_year: dyn.start_year, mean_before: np.mean(before), mean_after: np.mean(after), t_statistic: t_stat, p_value: p_value, significant_0.05: p_value 0.05, }) return pd.DataFrame(break_results) def event_study( self, df: pd.DataFrame, events: Dict[int, str], window: int 10, ) - pd.DataFrame: 事件研究法 设计原因观测重大事件前后经济指标的变化模式 典型应用变法、战争、自然灾害的影响评估 all_windows [] for year, event_name in events.items(): year_idx df.index[df[year] year] if len(year_idx) 0: continue idx year_idx[0] start max(0, idx - window) end min(len(df), idx window 1) event_data df.iloc[start:end].copy() event_data[relative_year] range( -min(idx - start, window), min(end - idx, window 1), ) event_data[event] event_name all_windows.append(event_data) if not all_windows: return pd.DataFrame() return pd.concat(all_windows, ignore_indexTrue) def run_full_analysis(self) - Dict: 运行完整分析 print( * 60) print(历史经济周期分析报告) print(警告本分析基于模拟数据结论不能作为历史研究依据) print( * 60) # 生成数据 df self.generate_synthetic_data() # 1. 频谱分析 print(\n1. 频谱分析周期检测) spectrum self.spectral_analysis(df) print(f 主导周期{spectrum[dominant_period]} 年) for p in spectrum[peak_periods][:3]: print(f - {p[period_years]} 年周期能量: {p[power]:.2f}) # 2. 结构性断点检验 print(\n2. 结构性断点检验朝代更替) breaks self.structural_break_test(df) significant breaks[breaks[significant_0.05]] print(f 显著断点{len(significant)} / {len(breaks)} 个朝代边界) for _, row in significant.iterrows(): print(f - {row[dynasty]}: f均值 {row[mean_before]:.3f} → {row[mean_after]:.3f} f (p{row[p_value]:.4f})) # 3. 事件研究 print(\n3. 事件研究重大事件前后 10 年) events { -200: 秦统一六国, 755: 安史之乱, 1644: 明清更替, } event_data self.event_study(df, events, window10) for event_name in events.values(): e_data event_data[event_data[event] event_name] if e_data.empty: continue before e_data[e_data[relative_year] 0][economic_index].mean() after e_data[e_data[relative_year] 0][economic_index].mean() change_pct (after - before) / max(abs(before), 1e-8) * 100 print(f - {event_name}: f事件前后变化 {change_pct:.1f}%) return { spectrum: spectrum, breaks: breaks, event_data: event_data, } # 运行分析 analyzer HistoricalEconomicAnalyzer() results analyzer.run_full_analysis() print(f\n方法论的局限性) print( 1. 古代经济数据来源有限且存在记录偏差) print( 2. 插值填补的数据降低了统计检验效力) print( 3. 相关关系不等于因果关系) print( 4. 本分析为方法演示真实研究需要与历史学家合作)四、个性化边界权衡分析方法发现潜力数据要求可靠性适用性频谱分析周期规律长序列 200中受插值影响寻找周期结构断点检验朝代更替影响断点前后 30中依赖断点定义政策/制度评估事件研究法事件冲击事件前后 5-10 年低混杂因素多短期影响评估面板数据分析地区差异多地数据 30中区域比较机器学习预测模式识别大量标注低因果混淆辅助假设生成关键权衡数据的质 vs 量的利用古代经济数据的精度远不如现代数据。需要极其审慎地解释分析结果——统计显著不等于历史真实。分析结论应该作为假设提出而非事实陈述。相关性 vs 因果性任何时间序列分析都面临相关不等于因果的挑战。事件研究法中发现的事件前后变化可能只是长期趋势的延续。方法严谨 vs 结论务实在数据质量有限的情况下过度追求统计方法的复杂性可能产生伪精确性。优选简单、可解释的方法。五、总结用 AI 和数据科学方法分析历史经济周期核心价值不在结论而在方法。频谱分析可检测时间序列中的周期成分40-60 年周期的假设在此框架下可检验结构断点检验可判断朝代更替是否构成统计意义上的经济断点事件研究法可观测重大事件的短期冲击模式。数据层面的三大挑战是稀疏性需要插值但必须标注不确定性、单位不一致需要跨朝代度量衡换算、记录偏差史书记录偏好极端事件。工程上建议采用多方法交叉验证不同方法得出相似结论时才认真对待并与历史学专业知识结合统计分析不应脱离历史脉络的定性理解。最终这类分析的价值是生成可检验的假设而非得出确定性的历史结论。