p3: figfix
This commit is contained in:
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 110 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 58 KiB |
@@ -806,7 +806,7 @@ def sensitivity_buffer_days(baseline: BaselineParameters, save_dir: str):
|
||||
|
||||
def tornado_analysis(baseline: BaselineParameters, save_dir: str):
|
||||
"""
|
||||
Tornado图 - 多参数灵敏度排序分析
|
||||
Tornado图 - 多参数灵敏度排序分析(马卡龙色系简约版)
|
||||
"""
|
||||
print("生成Tornado图...")
|
||||
|
||||
@@ -825,7 +825,7 @@ def tornado_analysis(baseline: BaselineParameters, save_dir: str):
|
||||
baseline_annual = baseline_demand['annual_supply_tons']
|
||||
baseline_initial = baseline_demand['initial_transport_tons']
|
||||
|
||||
# 定义参数范围
|
||||
# 定义参数范围 - 根据论述调整(删除Confidence Level)
|
||||
params = [
|
||||
('Recycle Rate (η)', 'recycle_rate', 0.80, 0.95, baseline.recycle_rate),
|
||||
('Comfort Factor (α)', 'comfort_factor', 25, 100, baseline.comfort_factor),
|
||||
@@ -833,7 +833,6 @@ def tornado_analysis(baseline: BaselineParameters, save_dir: str):
|
||||
('Sickness Rate', 'sickness_rate', 0.01, 0.04, baseline.sickness_rate),
|
||||
('Medical Water/Event', 'medical_water', 3, 10, baseline.medical_water_per_event),
|
||||
('Buffer Days', 'buffer_days', 15, 45, baseline.safety_buffer_days),
|
||||
('Confidence Level', 'confidence', 0.95, 0.999, baseline.confidence_level),
|
||||
]
|
||||
|
||||
results_annual = []
|
||||
@@ -883,13 +882,6 @@ def tornado_analysis(baseline: BaselineParameters, save_dir: str):
|
||||
demand_high = calculate_water_demand(baseline.population, baseline.survival_water, baseline.hygiene_water_base,
|
||||
baseline.comfort_factor, baseline.medical_water_per_event, baseline.sickness_rate,
|
||||
baseline.confidence_level, baseline.recycle_rate, int(high))
|
||||
elif param_type == 'confidence':
|
||||
demand_low = calculate_water_demand(baseline.population, baseline.survival_water, baseline.hygiene_water_base,
|
||||
baseline.comfort_factor, baseline.medical_water_per_event, baseline.sickness_rate,
|
||||
low, baseline.recycle_rate, baseline.safety_buffer_days)
|
||||
demand_high = calculate_water_demand(baseline.population, baseline.survival_water, baseline.hygiene_water_base,
|
||||
baseline.comfort_factor, baseline.medical_water_per_event, baseline.sickness_rate,
|
||||
high, baseline.recycle_rate, baseline.safety_buffer_days)
|
||||
|
||||
# 计算变化百分比
|
||||
change_low_annual = (demand_low['annual_supply_tons'] - baseline_annual) / baseline_annual * 100
|
||||
@@ -914,76 +906,109 @@ def tornado_analysis(baseline: BaselineParameters, save_dir: str):
|
||||
'swing': swing_initial
|
||||
})
|
||||
|
||||
# 按swing排序
|
||||
# 按年度需求的swing排序(统一排序顺序)
|
||||
results_annual = sorted(results_annual, key=lambda x: x['swing'], reverse=True)
|
||||
results_initial = sorted(results_initial, key=lambda x: x['swing'], reverse=True)
|
||||
|
||||
# 绘图
|
||||
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
|
||||
# 按相同顺序重排initial结果
|
||||
param_order = [r['param'] for r in results_annual]
|
||||
results_initial_dict = {r['param']: r for r in results_initial}
|
||||
results_initial = [results_initial_dict[p] for p in param_order]
|
||||
|
||||
# 图1: 年补充量Tornado
|
||||
ax = axes[0]
|
||||
y_pos = np.arange(len(results_annual))
|
||||
# 马卡龙色系
|
||||
color_decrease = '#A8D8B9' # 薄荷绿 - 减少
|
||||
color_increase = '#F7C5A8' # 杏橙 - 增加
|
||||
color_decrease_dark = '#7BC08F'
|
||||
color_increase_dark = '#E8A07A'
|
||||
|
||||
# 绘图 - 简约风格(缩小尺寸)
|
||||
fig, axes = plt.subplots(1, 2, figsize=(9, 3.5))
|
||||
fig.patch.set_facecolor('#FAFAFA')
|
||||
|
||||
# 统一的参数标签
|
||||
params_sorted = [r['param'] for r in results_annual]
|
||||
y_pos = np.arange(len(params_sorted))
|
||||
|
||||
# ========== 图1: 年补充量Tornado ==========
|
||||
ax = axes[0]
|
||||
ax.set_facecolor('#FAFAFA')
|
||||
lows = [r['low'] for r in results_annual]
|
||||
highs = [r['high'] for r in results_annual]
|
||||
|
||||
# 绘制条形图
|
||||
# 绘制条形图 - 分开绘制正负部分
|
||||
for i, (l, h) in enumerate(zip(lows, highs)):
|
||||
# 负值部分(减少)
|
||||
if l < 0:
|
||||
ax.barh(i, l, color=color_decrease, edgecolor=color_decrease_dark,
|
||||
linewidth=1, height=0.65)
|
||||
# 正值部分(增加)
|
||||
if h > 0:
|
||||
ax.barh(i, h, color=color_increase, edgecolor=color_increase_dark,
|
||||
linewidth=1, height=0.65)
|
||||
|
||||
# 添加数值标注
|
||||
for i, (l, h) in enumerate(zip(lows, highs)):
|
||||
if l < 0:
|
||||
ax.barh(i, l, color='green', alpha=0.7, height=0.6)
|
||||
ax.text(l - 2, i, f'{l:.0f}%', ha='right', va='center', fontsize=8, color='#555555')
|
||||
if h > 0:
|
||||
ax.barh(i, h, color='red', alpha=0.7, height=0.6)
|
||||
if l >= 0:
|
||||
ax.barh(i, l, left=0, color='green', alpha=0.7, height=0.6)
|
||||
if h <= 0:
|
||||
ax.barh(i, h, left=0, color='red', alpha=0.7, height=0.6)
|
||||
# 如果跨越0,绘制完整范围
|
||||
if l < 0 < h:
|
||||
pass # 已经绘制
|
||||
elif l >= 0:
|
||||
ax.barh(i, h - l, left=l, color='orange', alpha=0.7, height=0.6)
|
||||
elif h <= 0:
|
||||
ax.barh(i, h - l, left=l, color='orange', alpha=0.7, height=0.6)
|
||||
ax.text(h + 2, i, f'+{h:.0f}%', ha='left', va='center', fontsize=8, color='#555555')
|
||||
|
||||
ax.axvline(x=0, color='black', linewidth=1.5)
|
||||
ax.axvline(x=0, color='#333333', linewidth=1.2)
|
||||
ax.set_yticks(y_pos)
|
||||
ax.set_yticklabels(params_sorted)
|
||||
ax.set_xlabel('Change in Annual Supply (%)', fontsize=12)
|
||||
ax.set_title('Tornado Diagram: Annual Water Supply', fontsize=14)
|
||||
ax.grid(True, alpha=0.3, axis='x')
|
||||
ax.set_yticklabels(params_sorted, fontsize=10)
|
||||
ax.set_xlabel('Change in Annual Supply (%)', fontsize=11, color='#444444')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines['left'].set_color('#CCCCCC')
|
||||
ax.spines['bottom'].set_color('#CCCCCC')
|
||||
ax.grid(True, alpha=0.15, axis='x', linestyle='--')
|
||||
ax.set_xlim(-65, 115)
|
||||
|
||||
# 图2: 首批运输量Tornado
|
||||
# ========== 图2: 首批运输量Tornado ==========
|
||||
ax = axes[1]
|
||||
params_sorted = [r['param'] for r in results_initial]
|
||||
ax.set_facecolor('#FAFAFA')
|
||||
lows = [r['low'] for r in results_initial]
|
||||
highs = [r['high'] for r in results_initial]
|
||||
|
||||
for i, (l, h) in enumerate(zip(lows, highs)):
|
||||
width = h - l
|
||||
left = l
|
||||
color = 'steelblue' if width > 0 else 'gray'
|
||||
ax.barh(i, width, left=left, color=color, alpha=0.7, height=0.6)
|
||||
# 负值部分(减少)
|
||||
if l < 0:
|
||||
ax.barh(i, l, color=color_decrease, edgecolor=color_decrease_dark,
|
||||
linewidth=1, height=0.65)
|
||||
# 正值部分(增加)
|
||||
if h > 0:
|
||||
ax.barh(i, h, color=color_increase, edgecolor=color_increase_dark,
|
||||
linewidth=1, height=0.65)
|
||||
|
||||
ax.axvline(x=0, color='black', linewidth=1.5)
|
||||
# 添加数值标注
|
||||
for i, (l, h) in enumerate(zip(lows, highs)):
|
||||
if l < 0:
|
||||
ax.text(l - 2, i, f'{l:.0f}%', ha='right', va='center', fontsize=8, color='#555555')
|
||||
if h > 0:
|
||||
ax.text(h + 2, i, f'+{h:.0f}%', ha='left', va='center', fontsize=8, color='#555555')
|
||||
|
||||
ax.axvline(x=0, color='#333333', linewidth=1.2)
|
||||
ax.set_yticks(y_pos)
|
||||
ax.set_yticklabels(params_sorted)
|
||||
ax.set_xlabel('Change in Initial Transport (%)', fontsize=12)
|
||||
ax.set_title('Tornado Diagram: Initial Transport Volume', fontsize=14)
|
||||
ax.grid(True, alpha=0.3, axis='x')
|
||||
ax.set_yticklabels([]) # 删除右图纵坐标标签
|
||||
ax.set_xlabel('Change in Initial Transport (%)', fontsize=11, color='#444444')
|
||||
ax.spines['top'].set_visible(False)
|
||||
ax.spines['right'].set_visible(False)
|
||||
ax.spines['left'].set_visible(False) # 隐藏左边框
|
||||
ax.spines['bottom'].set_color('#CCCCCC')
|
||||
ax.grid(True, alpha=0.15, axis='x', linestyle='--')
|
||||
ax.set_xlim(-65, 105)
|
||||
|
||||
# 添加图例说明
|
||||
# 简化图例 - 放在图的下方中间
|
||||
from matplotlib.patches import Patch
|
||||
legend_elements = [
|
||||
Patch(facecolor='green', alpha=0.7, label='Decrease'),
|
||||
Patch(facecolor='red', alpha=0.7, label='Increase'),
|
||||
Patch(facecolor=color_decrease, edgecolor=color_decrease_dark, label='Decrease'),
|
||||
Patch(facecolor=color_increase, edgecolor=color_increase_dark, label='Increase'),
|
||||
]
|
||||
axes[0].legend(handles=legend_elements, loc='lower right')
|
||||
fig.legend(handles=legend_elements, loc='lower center', ncol=2, fontsize=9,
|
||||
framealpha=0.9, edgecolor='#CCCCCC', bbox_to_anchor=(0.55, -0.02))
|
||||
|
||||
plt.suptitle('Multi-Parameter Sensitivity Analysis (Tornado Diagram)\nBaseline: α=50, η=90%, N=100k',
|
||||
fontsize=14, y=1.02)
|
||||
plt.tight_layout()
|
||||
plt.savefig(f'{save_dir}/tornado_analysis.png', dpi=150, bbox_inches='tight')
|
||||
plt.subplots_adjust(bottom=0.15)
|
||||
plt.savefig(f'{save_dir}/tornado_analysis.png', dpi=150, bbox_inches='tight', facecolor='#FAFAFA')
|
||||
print(f" 保存: {save_dir}/tornado_analysis.png")
|
||||
|
||||
return results_annual, results_initial
|
||||
@@ -991,10 +1016,12 @@ def tornado_analysis(baseline: BaselineParameters, save_dir: str):
|
||||
|
||||
def interaction_heatmap(baseline: BaselineParameters, save_dir: str):
|
||||
"""
|
||||
双参数交互热力图 - α vs η
|
||||
双参数交互热力图 - α vs η(马卡龙色系简约版)
|
||||
"""
|
||||
print("生成双参数交互热力图...")
|
||||
|
||||
from matplotlib.colors import LinearSegmentedColormap
|
||||
|
||||
alphas = np.array([1, 25, 50, 100, 150, 200, 250])
|
||||
etas = np.array([0.70, 0.75, 0.80, 0.85, 0.90, 0.95])
|
||||
|
||||
@@ -1019,52 +1046,64 @@ def interaction_heatmap(baseline: BaselineParameters, save_dir: str):
|
||||
annual_matrix[i, j] = demand['annual_supply_tons'] / 1000 # 千吨
|
||||
capacity_matrix[i, j] = demand['annual_supply_tons'] / baseline.total_elevator_capacity * 100
|
||||
|
||||
fig, axes = plt.subplots(1, 2, figsize=(16, 7))
|
||||
# 马卡龙色系(中等饱和度)
|
||||
# 图1: 淡蓝→蓝紫→玫粉(年补充量)
|
||||
colors_annual = ['#C8E6F0', '#A8D0E8', '#98B8D8', '#B898C8', '#D098B0', '#D88898', '#C86878']
|
||||
cmap_annual = LinearSegmentedColormap.from_list('macaron_annual', colors_annual)
|
||||
|
||||
# 图2: 薄荷绿→淡黄→杏橙→珊瑚红(运力占比)
|
||||
colors_capacity = ['#A8D8B8', '#C8E0A0', '#E0D890', '#E8C078', '#E8A070', '#D88070', '#C86868']
|
||||
cmap_capacity = LinearSegmentedColormap.from_list('macaron_capacity', colors_capacity)
|
||||
|
||||
# 缩小图片尺寸
|
||||
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
|
||||
fig.patch.set_facecolor('#FAFAFA')
|
||||
|
||||
# 图1: 年补充量热力图
|
||||
ax = axes[0]
|
||||
im = ax.imshow(annual_matrix, cmap='YlOrRd', aspect='auto')
|
||||
ax.set_facecolor('#FAFAFA')
|
||||
im = ax.imshow(annual_matrix, cmap=cmap_annual, aspect='auto')
|
||||
ax.set_xticks(range(len(alphas)))
|
||||
ax.set_xticklabels([f'{a}' for a in alphas])
|
||||
ax.set_xticklabels([f'{a}' for a in alphas], fontsize=9)
|
||||
ax.set_yticks(range(len(etas)))
|
||||
ax.set_yticklabels([f'{e*100:.0f}%' for e in etas])
|
||||
ax.set_xlabel('Comfort Factor (α)', fontsize=12)
|
||||
ax.set_ylabel('Recycle Rate (η)', fontsize=12)
|
||||
ax.set_title('Annual Water Supply (kt)', fontsize=13)
|
||||
ax.set_yticklabels([f'{e*100:.0f}%' for e in etas], fontsize=9)
|
||||
ax.set_xlabel('Comfort Factor (α)', fontsize=10, color='#444444')
|
||||
ax.set_ylabel('Recycle Rate (η)', fontsize=10, color='#444444')
|
||||
|
||||
# 添加数值标注
|
||||
for i in range(len(etas)):
|
||||
for j in range(len(alphas)):
|
||||
text_color = 'white' if annual_matrix[i, j] > annual_matrix.max() * 0.6 else 'black'
|
||||
ax.text(j, i, f'{annual_matrix[i, j]:.0f}', ha='center', va='center', fontsize=9, color=text_color)
|
||||
ax.text(j, i, f'{annual_matrix[i, j]:.0f}', ha='center', va='center',
|
||||
fontsize=8, color='#333333', fontweight='medium')
|
||||
|
||||
plt.colorbar(im, ax=ax, label='Annual Supply (kt)')
|
||||
cbar1 = plt.colorbar(im, ax=ax, shrink=0.8)
|
||||
cbar1.ax.tick_params(labelsize=8)
|
||||
cbar1.set_label('Annual Supply (kt)', fontsize=9, color='#444444')
|
||||
|
||||
# 图2: 运力占比热力图
|
||||
ax = axes[1]
|
||||
im = ax.imshow(capacity_matrix, cmap='RdYlGn_r', aspect='auto', vmin=0, vmax=150)
|
||||
ax.set_facecolor('#FAFAFA')
|
||||
im = ax.imshow(capacity_matrix, cmap=cmap_capacity, aspect='auto', vmin=0, vmax=150)
|
||||
ax.set_xticks(range(len(alphas)))
|
||||
ax.set_xticklabels([f'{a}' for a in alphas])
|
||||
ax.set_xticklabels([f'{a}' for a in alphas], fontsize=9)
|
||||
ax.set_yticks(range(len(etas)))
|
||||
ax.set_yticklabels([f'{e*100:.0f}%' for e in etas])
|
||||
ax.set_xlabel('Comfort Factor (α)', fontsize=12)
|
||||
ax.set_ylabel('Recycle Rate (η)', fontsize=12)
|
||||
ax.set_title('Elevator Capacity Usage (%)', fontsize=13)
|
||||
ax.set_yticklabels([f'{e*100:.0f}%' for e in etas], fontsize=9)
|
||||
ax.set_xlabel('Comfort Factor (α)', fontsize=10, color='#444444')
|
||||
ax.set_ylabel('Recycle Rate (η)', fontsize=10, color='#444444')
|
||||
|
||||
# 添加数值标注
|
||||
for i in range(len(etas)):
|
||||
for j in range(len(alphas)):
|
||||
val = capacity_matrix[i, j]
|
||||
text_color = 'white' if val > 60 else 'black'
|
||||
ax.text(j, i, f'{val:.0f}%', ha='center', va='center', fontsize=9, color=text_color)
|
||||
ax.text(j, i, f'{val:.0f}%', ha='center', va='center',
|
||||
fontsize=8, color='#333333', fontweight='medium')
|
||||
|
||||
# 添加100%等高线
|
||||
cbar = plt.colorbar(im, ax=ax, label='Capacity Usage (%)')
|
||||
cbar2 = plt.colorbar(im, ax=ax, shrink=0.8)
|
||||
cbar2.ax.tick_params(labelsize=8)
|
||||
cbar2.set_label('Capacity Usage (%)', fontsize=9, color='#444444')
|
||||
|
||||
plt.suptitle('Two-Parameter Interaction Analysis: α vs η\n(N=100,000, Buffer=30 days)',
|
||||
fontsize=14, y=1.02)
|
||||
plt.tight_layout()
|
||||
plt.savefig(f'{save_dir}/interaction_heatmap.png', dpi=150, bbox_inches='tight')
|
||||
plt.savefig(f'{save_dir}/interaction_heatmap.png', dpi=150, bbox_inches='tight', facecolor='#FAFAFA')
|
||||
print(f" 保存: {save_dir}/interaction_heatmap.png")
|
||||
|
||||
return annual_matrix, capacity_matrix
|
||||
|
||||
Reference in New Issue
Block a user