Workspace Price Tiers the competition, step by step

Lesson 9 of 10

Error analysis

Confusion matrix, per-tier and per-metro scores: where the model still struggles.

Download the final notebook

Outputs will appear here after the full run. The code is final.

Left: what & whyRight: the code and its real output

9. Error analysis

Where does the chosen model go wrong? The predictions below are the cross-fitted out-of-fold ones (each fold decoded with cut-points/weights tuned on the other folds).

from sklearn.metrics import confusion_matrix, f1_score as sk_f1
best_r = ranked[0]
o_, _ = best_r["out"]
cf_pred = np.empty(n_tr, int)
for f in range(FOLDS):
    m_ = fold_id == f
    prm = best_r["fit"](o_[~m_], y[~m_], W_tr[~m_], MT_tr[~m_])
    cf_pred[m_] = best_r["apply"](o_[m_], prm, MT_tr[m_])
print("chosen:", best_r["scenario"], "|", best_r["decoder"])
print("per-tier F1:", dict(zip(["tier 1", "tier 2", "tier 3", "tier 4"], sk_f1(y, cf_pred, average=None).round(3))))
cm = confusion_matrix(y, cf_pred, normalize="true")
fig, ax = plt.subplots(figsize=(5, 4))
ax.imshow(cm, cmap="Blues")
for i in range(K):
    for j in range(K):
        ax.text(j, i, f"{cm[i, j]:.2f}", ha="center", va="center", color="white" if cm[i, j] > 0.5 else "black")
ax.set_xticks(range(K), [f"pred {k + 1}" for k in range(K)]); ax.set_yticks(range(K), [f"true {k + 1}" for k in range(K)])
ax.set_title("Confusion matrix (row-normalised)"); plt.tight_layout(); plt.show()
err = cf_pred != y
print(f"wrong: {err.mean():.1%} of listings | of the mistakes, {(np.abs(cf_pred - y)[err] == 1).mean():.1%} are off by just one tier")
per_metro = pd.DataFrame({"metro": train.metro.astype(str), "y": y, "p": cf_pred}).groupby("metro")[["y", "p"]].apply(
    lambda d: pd.Series({"listings": len(d), "macro F1": round(sk_f1(d.y, d.p, average="macro"), 3)}))
display(per_metro.sort_values("macro F1"))

Reading this. Look at where the mistakes sit in the confusion matrix: mostly next to the diagonal means the model ranks listings well and only the borders between tiers are blurry. Middle tiers usually score lowest because they border two tiers each. The per-metro table shows which markets are hardest to predict.