250 lines
9.6 KiB
Python
250 lines
9.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
胡汉三千年项目历史数据可视化工具
|
|
Historical Data Visualization Tool for Hu-Han Three Thousand Years Project
|
|
"""
|
|
|
|
import matplotlib.pyplot as plt
|
|
import seaborn as sns
|
|
import pandas as pd
|
|
import numpy as np
|
|
from typing import Dict, List, Any
|
|
import json
|
|
import os
|
|
|
|
# 设置中文字体
|
|
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
|
|
plt.rcParams['axes.unicode_minus'] = False
|
|
|
|
class HistoricalDataVisualizer:
|
|
"""历史数据可视化器"""
|
|
|
|
def __init__(self, output_dir: str = "tools/diagrams/generated"):
|
|
self.output_dir = output_dir
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
# 设置样式
|
|
sns.set_style("whitegrid")
|
|
sns.set_palette("husl")
|
|
|
|
def plot_emperor_ages(self, dynasty_data: Dict[str, List[int]],
|
|
title: str = "各朝代皇帝死亡年龄分析"):
|
|
"""绘制皇帝年龄分析图"""
|
|
|
|
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
|
|
|
|
# 左图:年龄分布直方图
|
|
for dynasty, ages in dynasty_data.items():
|
|
ax1.hist(ages, alpha=0.7, label=f"{dynasty} (平均:{np.mean(ages):.1f}岁)",
|
|
bins=10, density=True)
|
|
|
|
ax1.set_xlabel('死亡年龄')
|
|
ax1.set_ylabel('密度')
|
|
ax1.set_title('皇帝死亡年龄分布')
|
|
ax1.legend()
|
|
ax1.grid(True, alpha=0.3)
|
|
|
|
# 右图:平均年龄对比
|
|
dynasties = list(dynasty_data.keys())
|
|
avg_ages = [np.mean(ages) for ages in dynasty_data.values()]
|
|
median_ages = [np.median(ages) for ages in dynasty_data.values()]
|
|
|
|
x = np.arange(len(dynasties))
|
|
width = 0.35
|
|
|
|
ax2.bar(x - width/2, avg_ages, width, label='平均年龄', alpha=0.8)
|
|
ax2.bar(x + width/2, median_ages, width, label='中位数年龄', alpha=0.8)
|
|
|
|
ax2.set_xlabel('朝代')
|
|
ax2.set_ylabel('年龄')
|
|
ax2.set_title('各朝代统治者年龄对比')
|
|
ax2.set_xticks(x)
|
|
ax2.set_xticklabels(dynasties)
|
|
ax2.legend()
|
|
ax2.grid(True, alpha=0.3)
|
|
|
|
# 添加数值标签
|
|
for i, (avg, med) in enumerate(zip(avg_ages, median_ages)):
|
|
ax2.text(i - width/2, avg + 1, f'{avg:.1f}', ha='center', va='bottom')
|
|
ax2.text(i + width/2, med + 1, f'{med:.1f}', ha='center', va='bottom')
|
|
|
|
plt.suptitle(title, fontsize=16, fontweight='bold')
|
|
plt.tight_layout()
|
|
|
|
# 保存图片
|
|
filename = f"{self.output_dir}/emperor_ages_analysis.png"
|
|
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
|
plt.show()
|
|
|
|
return filename
|
|
|
|
def plot_cultural_influence_network(self, influence_data: Dict[str, Any],
|
|
title: str = "文化影响传播网络"):
|
|
"""绘制文化影响网络图"""
|
|
|
|
import networkx as nx
|
|
|
|
fig, ax = plt.subplots(figsize=(12, 8))
|
|
|
|
# 创建网络图
|
|
G = nx.DiGraph()
|
|
|
|
# 添加节点和边
|
|
for edge in influence_data['edges']:
|
|
G.add_edge(edge['from'], edge['to'],
|
|
weight=edge.get('strength', 1),
|
|
influence=edge.get('influence', ''))
|
|
|
|
# 设置布局
|
|
pos = nx.spring_layout(G, k=3, iterations=50)
|
|
|
|
# 绘制节点
|
|
node_sizes = [3000 if node in influence_data.get('core_nodes', []) else 2000
|
|
for node in G.nodes()]
|
|
node_colors = ['red' if node in influence_data.get('core_nodes', []) else 'lightblue'
|
|
for node in G.nodes()]
|
|
|
|
nx.draw_networkx_nodes(G, pos, node_size=node_sizes,
|
|
node_color=node_colors, alpha=0.8, ax=ax)
|
|
|
|
# 绘制边
|
|
nx.draw_networkx_edges(G, pos, edge_color='gray',
|
|
arrows=True, arrowsize=20,
|
|
arrowstyle='->', alpha=0.6, ax=ax)
|
|
|
|
# 添加标签
|
|
nx.draw_networkx_labels(G, pos, font_size=12, font_weight='bold', ax=ax)
|
|
|
|
# 添加边标签(影响类型)
|
|
edge_labels = {(edge['from'], edge['to']): edge.get('influence', '')
|
|
for edge in influence_data['edges']}
|
|
nx.draw_networkx_edge_labels(G, pos, edge_labels, font_size=8, ax=ax)
|
|
|
|
ax.set_title(title, fontsize=16, fontweight='bold')
|
|
ax.axis('off')
|
|
|
|
# 添加图例
|
|
legend_elements = [
|
|
plt.Line2D([0], [0], marker='o', color='w', markerfacecolor='red',
|
|
markersize=15, label='核心影响源'),
|
|
plt.Line2D([0], [0], marker='o', color='w', markerfacecolor='lightblue',
|
|
markersize=15, label='影响接受方'),
|
|
plt.Line2D([0], [0], color='gray', label='影响路径')
|
|
]
|
|
ax.legend(handles=legend_elements, loc='upper right')
|
|
|
|
plt.tight_layout()
|
|
|
|
# 保存图片
|
|
filename = f"{self.output_dir}/cultural_influence_network.png"
|
|
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
|
plt.show()
|
|
|
|
return filename
|
|
|
|
def plot_timeline_analysis(self, timeline_data: List[Dict[str, Any]],
|
|
title: str = "历史事件时间线分析"):
|
|
"""绘制历史事件时间线"""
|
|
|
|
fig, ax = plt.subplots(figsize=(15, 8))
|
|
|
|
# 准备数据
|
|
events = sorted(timeline_data, key=lambda x: x['year'])
|
|
years = [event['year'] for event in events]
|
|
categories = list(set(event['category'] for event in events))
|
|
|
|
# 为每个类别分配颜色和y位置
|
|
colors = plt.cm.Set3(np.linspace(0, 1, len(categories)))
|
|
category_colors = dict(zip(categories, colors))
|
|
category_positions = {cat: i for i, cat in enumerate(categories)}
|
|
|
|
# 绘制时间线
|
|
for event in events:
|
|
y_pos = category_positions[event['category']]
|
|
color = category_colors[event['category']]
|
|
|
|
# 绘制事件点
|
|
ax.scatter(event['year'], y_pos, s=200, c=[color],
|
|
alpha=0.8, edgecolors='black', linewidth=1)
|
|
|
|
# 添加事件标签
|
|
ax.annotate(event['name'],
|
|
(event['year'], y_pos),
|
|
xytext=(10, 10), textcoords='offset points',
|
|
bbox=dict(boxstyle='round,pad=0.3', facecolor=color, alpha=0.7),
|
|
fontsize=9, ha='left')
|
|
|
|
# 设置坐标轴
|
|
ax.set_xlabel('年份', fontsize=12)
|
|
ax.set_ylabel('事件类别', fontsize=12)
|
|
ax.set_yticks(range(len(categories)))
|
|
ax.set_yticklabels(categories)
|
|
ax.set_title(title, fontsize=16, fontweight='bold')
|
|
|
|
# 添加网格
|
|
ax.grid(True, alpha=0.3)
|
|
|
|
# 设置x轴范围
|
|
year_range = max(years) - min(years)
|
|
ax.set_xlim(min(years) - year_range * 0.05, max(years) + year_range * 0.05)
|
|
|
|
plt.tight_layout()
|
|
|
|
# 保存图片
|
|
filename = f"{self.output_dir}/timeline_analysis.png"
|
|
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
|
plt.show()
|
|
|
|
return filename
|
|
|
|
def create_northern_wei_analysis():
|
|
"""创建北魏分析示例"""
|
|
|
|
viz = HistoricalDataVisualizer()
|
|
|
|
# 示例数据:各朝代皇帝年龄
|
|
dynasty_data = {
|
|
'北魏': [16, 23, 29, 31, 33, 28, 25, 39, 27, 24, 32, 26], # 平均29岁
|
|
'唐朝': [49, 52, 45, 55, 47, 38, 43, 51, 46, 44, 48, 50], # 平均47岁
|
|
'宋朝': [42, 38, 54, 46, 49, 41, 35, 52, 44, 47, 43, 45], # 平均45岁
|
|
'日本天皇': [34, 31, 28, 36, 32, 29, 35, 33, 30, 37, 31, 34] # 平均32岁
|
|
}
|
|
|
|
# 生成皇帝年龄分析图
|
|
viz.plot_emperor_ages(dynasty_data, "北魏生殖崇拜理论:皇帝年龄数据支撑")
|
|
|
|
# 文化影响网络数据
|
|
influence_data = {
|
|
'core_nodes': ['北魏'],
|
|
'edges': [
|
|
{'from': '北魏', 'to': '高句丽', 'influence': '政治制度', 'strength': 3},
|
|
{'from': '北魏', 'to': '百济', 'influence': '佛教文化', 'strength': 2},
|
|
{'from': '高句丽', 'to': '日本', 'influence': '建筑艺术', 'strength': 2},
|
|
{'from': '百济', 'to': '日本', 'influence': '宗教仪式', 'strength': 3},
|
|
{'from': '北魏', 'to': '新罗', 'influence': '文字系统', 'strength': 1},
|
|
{'from': '新罗', 'to': '日本', 'influence': '学术传统', 'strength': 1}
|
|
]
|
|
}
|
|
|
|
# 生成文化影响网络图
|
|
viz.plot_cultural_influence_network(influence_data, "北魏对日本文化影响的传播路径")
|
|
|
|
# 时间线数据
|
|
timeline_data = [
|
|
{'year': 386, 'name': '北魏建立', 'category': '政治事件'},
|
|
{'year': 398, 'name': '迁都平城', 'category': '政治事件'},
|
|
{'year': 494, 'name': '孝文帝改革', 'category': '文化改革'},
|
|
{'year': 538, 'name': '佛教传入日本', 'category': '文化传播'},
|
|
{'year': 552, 'name': '百济使者访日', 'category': '外交事件'},
|
|
{'year': 593, 'name': '圣德太子摄政', 'category': '政治事件'},
|
|
{'year': 645, 'name': '大化改新', 'category': '文化改革'}
|
|
]
|
|
|
|
# 生成时间线分析图
|
|
viz.plot_timeline_analysis(timeline_data, "北魏影响日本的历史时间线")
|
|
|
|
print("✅ 北魏分析可视化图表已生成!")
|
|
print(f"📁 图片保存位置: {viz.output_dir}")
|
|
|
|
if __name__ == "__main__":
|
|
create_northern_wei_analysis() |