classes = ['Poor', 'Standard', 'Good']
results = {}
for name, pred in [('Logistic Reg.', y_pred_lr),
('Decision Tree', y_pred_dt),
('Random Forest', y_pred_rf)]:
p, r, f, _ = precision_recall_fscore_support(
y_val, pred, labels=[0, 1, 2])
results[name] = pd.DataFrame(
{'Class': classes, 'Precision': p, 'Recall': r, 'F1': f})
combined = (
pd.concat(results, names=['Model'])
.reset_index(level=0)
.rename(columns={'level_0': 'Model'})
)
melted = combined.melt(
id_vars=['Model', 'Class'],
value_vars=['Precision', 'Recall', 'F1'],
var_name='Metric', value_name='Score',
)
fig, axes = plt.subplots(1, 3, figsize=(13, 4.5), sharey=True)
palette = ['#fc8d62', 'steelblue', '#66c2a5']
for ax, metric in zip(axes, ['Precision', 'Recall', 'F1']):
sub = melted[melted['Metric'] == metric]
sns.barplot(data=sub, x='Class', y='Score', hue='Model', ax=ax,
palette=palette)
ax.set_title(metric, fontsize=11)
ax.set_ylim(0, 1.05)
ax.set_xlabel('')
ax.set_ylabel('Score' if ax is axes[0] else '')
ax.legend(fontsize=7)
plt.suptitle('Logistic Regression vs. Decision Tree vs. Random Forest — Per-Class Metrics',
fontsize=11, y=1.02)
plt.tight_layout()
plt.show()