|
import plotly.graph_objects as go |
|
import pandas as pd |
|
import numpy as np |
|
|
|
|
|
np.random.seed(42) |
|
methods = ['RSM', 'PSRN', 'NGGP', 'PySR', 'BMS', 'uDSR', 'AIF', |
|
'DGSR', 'E2E', 'SymINDy', 'physo', 'TPSR', 'SPL', |
|
'DEAP', 'SINDy', 'NSRS', 'gplearn', 'SNIP', 'KAN', 'EQL'] |
|
|
|
|
|
rmse_values = np.clip(np.abs(np.random.normal(0.3, 0.15, len(methods))), 0.05, 0.95) |
|
uncertainty = np.random.uniform(0.02, 0.08, len(methods)) |
|
param_sizes = np.array([1e3, 1e4, 5e4, 1e5, 5e5, 1e6, 2e6]) |
|
|
|
|
|
method_categories = { |
|
'Symbolic': ['RSM', 'PySR', 'SymINDy', 'gplearn', 'EQL'], |
|
'Neural': ['NGGP', 'DGSR', 'E2E', 'KAN'], |
|
'Evolutionary': ['PSRN', 'DEAP', 'NSRS'], |
|
'Physics-based': ['physo', 'TPSR', 'SPL'], |
|
'Hybrid': ['BMS', 'uDSR', 'AIF', 'SINDy', 'SNIP'] |
|
} |
|
|
|
|
|
df = pd.DataFrame({ |
|
'Method': methods, |
|
'RMSE': rmse_values, |
|
'Uncertainty': uncertainty, |
|
'ParamSize': np.random.choice(param_sizes, len(methods)) |
|
}).sort_values('RMSE', ascending=True) |
|
|
|
|
|
df['Category'] = df['Method'].apply(lambda x: next((k for k,v in method_categories.items() if x in v), 'Other')) |
|
|
|
|
|
category_colors = { |
|
'Symbolic': '#1f77b4', |
|
'Neural': '#ff7f0e', |
|
'Evolutionary': '#2ca02c', |
|
'Physics-based': '#d62728', |
|
'Hybrid': '#9467bd' |
|
} |
|
|
|
|
|
fig = go.Figure() |
|
|
|
|
|
size_min = np.log(df['ParamSize'].min()) |
|
size_max = np.log(df['ParamSize'].max()) |
|
sizes = 15 + 25 * (np.log(df['ParamSize']) - size_min) / (size_max - size_min) |
|
|
|
|
|
for category in df['Category'].unique(): |
|
df_sub = df[df['Category'] == category] |
|
fig.add_trace(go.Scatter( |
|
x=df_sub['RMSE'], |
|
y=df_sub['Method'], |
|
mode='markers', |
|
name=category, |
|
marker=dict( |
|
size=sizes[df_sub.index], |
|
color=category_colors[category], |
|
opacity=0.9, |
|
line=dict(width=1, color='black') |
|
), |
|
error_x=dict( |
|
type='data', |
|
array=df_sub['Uncertainty'], |
|
color='rgba(40,40,40,0.6)', |
|
thickness=1.2, |
|
width=10 |
|
), |
|
hoverinfo='text', |
|
hovertext=df_sub.apply(lambda r: f"{r['Method']}<br>RMSE: {r['RMSE']:.3f} ± {r['Uncertainty']:.3f}<br>Params: {r['ParamSize']:,.0f}", axis=1) |
|
)) |
|
|
|
|
|
data_min = (df['RMSE'] - df['Uncertainty']).min() |
|
x_min = max(data_min - 0.05, 0) |
|
x_max = min(df['RMSE'].max() + df['Uncertainty'].max(), 1) |
|
|
|
|
|
fig.update_layout( |
|
title='Methods RMSE Comparison with Parameter Scale', |
|
xaxis=dict( |
|
title='Root Mean Square Error (RMSE) → Lower is better', |
|
range=[x_min, x_max], |
|
tickvals=np.arange(0, 1.1, 0.1), |
|
gridcolor='#F0F0F0', |
|
zeroline=False, |
|
showspikes=True |
|
), |
|
yaxis=dict( |
|
categoryorder='array', |
|
categoryarray=df['Method'].tolist(), |
|
tickfont=dict(size=12), |
|
showticklabels=False |
|
), |
|
plot_bgcolor='white', |
|
width=1100, |
|
height=700, |
|
margin=dict(l=180, r=50, t=80, b=40), |
|
legend=dict( |
|
title='Method Categories', |
|
orientation='v', |
|
yanchor="top", |
|
y=0.98, |
|
xanchor="left", |
|
x=1.02 |
|
) |
|
) |
|
|
|
|
|
y_positions = np.linspace(0.03, 0.97, len(methods)) |
|
for idx, method in enumerate(df['Method']): |
|
category = df[df['Method'] == method]['Category'].values[0] |
|
fig.add_annotation( |
|
x=0.01, |
|
y=y_positions[idx], |
|
xref='paper', |
|
yref='paper', |
|
text=method, |
|
showarrow=False, |
|
font=dict( |
|
size=12, |
|
color=category_colors[category] |
|
), |
|
xanchor='right' |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fig.add_annotation( |
|
x=0.98, y=0.02, |
|
xref='paper', yref='paper', |
|
text='叶璨铭绘制', |
|
showarrow=False, |
|
font=dict(size=10, color='#666666'), |
|
bgcolor='white' |
|
) |
|
|
|
fig.show() |