p1: fig
This commit is contained in:
@@ -585,6 +585,96 @@ def plot_sensitivity_analysis(
|
||||
return fig
|
||||
|
||||
|
||||
def plot_tornado_chart_standalone(
|
||||
payload_df: pd.DataFrame,
|
||||
elevator_df: pd.DataFrame,
|
||||
frequency_df: pd.DataFrame,
|
||||
alpha_df: pd.DataFrame,
|
||||
save_path: str = '/Volumes/Files/code/mm/20260130_b/p1/tornado_chart.png'
|
||||
):
|
||||
"""
|
||||
单独绘制 Tornado Chart(敏感性汇总图)
|
||||
- XY轴反转(垂直柱状图)
|
||||
- 长宽比 0.618
|
||||
- 无标题
|
||||
- 有图例
|
||||
- 低饱和度色系
|
||||
"""
|
||||
# 使用0.618黄金比例
|
||||
fig_width = 8
|
||||
fig_height = fig_width * 0.618
|
||||
fig, ax = plt.subplots(figsize=(fig_width, fig_height))
|
||||
|
||||
# 计算敏感性指标
|
||||
baseline_T = 139
|
||||
|
||||
sensitivities = [
|
||||
('Payload\n(100→150t)',
|
||||
(payload_df['optimal_T_lambda504'].iloc[0] - baseline_T) / baseline_T * 100,
|
||||
(payload_df['optimal_T_lambda504'].iloc[-1] - baseline_T) / baseline_T * 100),
|
||||
('Elevator Cap.\n(80%→120%)',
|
||||
(elevator_df['optimal_T_lambda504'].iloc[0] - baseline_T) / baseline_T * 100,
|
||||
(elevator_df['optimal_T_lambda504'].iloc[-1] - baseline_T) / baseline_T * 100),
|
||||
('Launch Freq.\n(0.5→2/day)',
|
||||
(frequency_df['optimal_T_lambda504'].iloc[0] - baseline_T) / baseline_T * 100,
|
||||
(frequency_df['optimal_T_lambda504'].iloc[-1] - baseline_T) / baseline_T * 100),
|
||||
('Struct. Coef.\n(0.06→0.14)',
|
||||
(alpha_df['optimal_T_lambda504'].iloc[0] - baseline_T) / baseline_T * 100,
|
||||
(alpha_df['optimal_T_lambda504'].iloc[-1] - baseline_T) / baseline_T * 100),
|
||||
]
|
||||
|
||||
# 低饱和度配色
|
||||
color_positive = '#7A9CBF' # 灰蓝色(参数增加导致T*增加)
|
||||
color_negative = '#C4816B' # 灰橙红色(参数增加导致T*减少)
|
||||
|
||||
x_pos = np.arange(len(sensitivities))
|
||||
bar_width = 0.6
|
||||
|
||||
# 绘制柱状图(XY反转,使用垂直柱状图)
|
||||
for i, (name, low, high) in enumerate(sensitivities):
|
||||
bar_color = color_positive if high > low else color_negative
|
||||
bar_height = high - low
|
||||
bar_bottom = min(low, high)
|
||||
|
||||
ax.bar(i, bar_height, bottom=bar_bottom, width=bar_width,
|
||||
color=bar_color, alpha=0.8, edgecolor='#555555', linewidth=0.8)
|
||||
|
||||
# 标记端点
|
||||
ax.plot(i, low, 'o', color='#444444', markersize=7, zorder=5)
|
||||
ax.plot(i, high, '^', color='#444444', markersize=7, zorder=5)
|
||||
ax.plot([i, i], [low, high], '-', color='#444444', linewidth=1.5, zorder=4)
|
||||
|
||||
# 基准线
|
||||
ax.axhline(y=0, color='#333333', linestyle='-', linewidth=1.2)
|
||||
|
||||
# 设置轴
|
||||
ax.set_xticks(x_pos)
|
||||
ax.set_xticklabels([s[0] for s in sensitivities], fontsize=10)
|
||||
ax.set_ylabel('Change in Optimal Timeline T* (%)', fontsize=11)
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
# 添加图例
|
||||
from matplotlib.patches import Patch
|
||||
from matplotlib.lines import Line2D
|
||||
legend_elements = [
|
||||
Patch(facecolor=color_negative, edgecolor='#555555', alpha=0.8,
|
||||
label='Parameter ↑ → T* ↓'),
|
||||
Patch(facecolor=color_positive, edgecolor='#555555', alpha=0.8,
|
||||
label='Parameter ↑ → T* ↑'),
|
||||
Line2D([0], [0], marker='o', color='#444444', linestyle='None',
|
||||
markersize=6, label='Low value'),
|
||||
Line2D([0], [0], marker='^', color='#444444', linestyle='None',
|
||||
markersize=6, label='High value'),
|
||||
]
|
||||
ax.legend(handles=legend_elements, loc='upper right', fontsize=9, framealpha=0.9)
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(save_path, dpi=150, bbox_inches='tight')
|
||||
print(f"Tornado chart saved to: {save_path}")
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
def plot_tradeoff_comparison(
|
||||
save_path: str = '/Volumes/Files/code/mm/20260130_b/p1/sensitivity_tradeoff.png'
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user