Classification Model Confusion Matrix Markdown Table Template
A confusion matrix is the raw evidence behind every classification metric. Precision, recall, specificity, F1 and accuracy are all ratios of its four cells, so publishing the counts lets a reader recompute any metric they care about instead of trusting the one you chose. It belongs in model evaluation reports, data science notebooks and diagnostic test write-ups, where reviewers, product owners and clinicians need to see not just how often the model is right, but which way it is wrong.
Edit This Template
Edit cells, add rows, or sort — the Markdown updates livePaste cells from Excel or Google Sheets into the first header to replace the table · Tab / Enter / arrows move · Shift+F10 opens row & column actions
When to Use This Table
The two kinds of error rarely cost the same. In fraud screening a false negative is lost money while a false positive is an annoyed customer; in medical screening a missed case can be far worse than a follow-up test. The matrix makes that trade visible and anchors the conversation about where to set the decision threshold, because every threshold produces a different matrix.
Using the template’s counts: precision is 850 / 880 = 96.6%, recall is 850 / 900 = 94.4%, and accuracy is 9,870 / 9,950 = 99.2%. That accuracy is less impressive than it looks, since only 9% of examples are positive and a model that always answers “negative” would already score 91.0%. A good matrix therefore states the positive class, the threshold, the dataset split, and shows totals so the class balance is obvious at a glance.
Formatting This Table in Markdown
A classification model confusion matrix table puts a few things in cells that Markdown treats as syntax. These are the ones to watch so it renders correctly on GitHub, GitLab, Obsidian and docs sites.
No merged header: put both axis names in the corner cell
With no merged cells, “Predicted” cannot sit above two columns, so the corner cell carries both axes (
Actual \ Predicted) and each column is labelled in full. The backslash renders literally because it is followed by a space, not ASCII punctuation.Spend bold on one thing
Bold is the emphasis that reliably survives, since GitHub strips
styleattributes and<font>, so colour shading is not available. If you bold the diagonal (TP and TN), avoid also bolding row labels and totals, or the diagonal stops standing out.Write conditional probabilities with an escaped bar
Recall is often written
P(ŷ=1 \| y=1), and the bar splits the cell unless it is escaped. Below the table, in a normal paragraph, no escaping is needed, so put derived formulas such as precision and recall there.Past about six classes, switch from table to image
A multi-class matrix grows with the square of the class count, and GFM cannot shade cells to show where errors cluster. Beyond roughly six classes, publish a heatmap image with alt text (
<img width>sizes it on GitHub) and keep the counts in a linked CSV.
What Goes in Each Column
| Column | Alignment | What to put here |
|---|---|---|
| Actual \ Predicted | left :--- | The corner cell names the orientation: rows are the true class, columns are the model’s prediction. Below it, bold row labels for each actual class. State what “positive” means right under the table (“Positive = transaction later confirmed fraudulent”), since nothing in the counts reveals it. |
| Predicted Positive | right ---: | True positives in the actual-positive row and false positives in the actual-negative row. Right-aligned counts with thousands separators. The column total is everything the model flagged and is the denominator of precision: TP / (TP + FP). |
| Predicted Negative | right ---: | False negatives (missed positives) and true negatives. Right-aligned like its neighbour. Its total is everything the model cleared; TN divided by that total is the negative predictive value, 9,020 / 9,070 ≈ 99.4% for the template, which matters when a negative result means “no further checks”. |
| Total Actual | right ---: | Row sums: the true number of examples in each class, also called support. Right-aligned. Actual positives are the recall denominator, TP / (TP + FN); actual negatives are the specificity denominator. The bottom-right cell is the test-set size and must match your dataset. |
How to Fill It In
- 1Decide and write down the positive class, the decision threshold, and the exact evaluation split. The matrix must come from held-out data that was not used for training or for picking that threshold.
- 2Compute the counts with a library rather than by hand. In scikit-learn,
confusion_matrix(y_true, y_pred, labels=[1, 0])returns rows as actual and columns as predicted with the positive class first, matching this template; withoutlabels, classes sort ascending and the negative class comes first. - 3Verify the marginals: each row total should equal the class count in your test labels, each column total the number of predictions of that class, and the grand total the test-set size. A mismatch usually means dropped rows or a label-encoding error.
- 4Derive the metrics directly beneath the table, with formulas, so readers can check them. GitHub renders inline math such as
$\frac{TP}{TP+FP}$if you prefer typeset notation. - 5Put the result in context: the score of a trivial majority-class predictor, a baseline model, and if possible the cost of errors — FP count times the cost of a false alarm plus FN count times the cost of a miss.
Ready-to-Paste Variations
Derived metrics for the template counts
Publish this under the matrix. Every value is computed from TP = 850, FN = 50, FP = 30, TN = 9,020, so readers can verify each line.
| Metric | Formula | Value |
| :--- | :--- | ---: |
| Precision (PPV) | TP / (TP + FP) | 850 / 880 = 0.966 |
| Recall (sensitivity) | TP / (TP + FN) | 850 / 900 = 0.944 |
| Specificity | TN / (TN + FP) | 9,020 / 9,050 = 0.997 |
| F1 score | 2TP / (2TP + FP + FN) | 1,700 / 1,780 = 0.955 |
| Accuracy | (TP + TN) / total | 9,870 / 9,950 = 0.992 |
| Balanced accuracy | (recall + specificity) / 2 | 0.971 |Multi-class matrix with per-class precision and recall
For three or more classes. The diagonal holds correct predictions; recall is read along each row and precision down each column. Overall accuracy here is 210 / 250 = 84%.
| Actual \ Predicted | Cat | Dog | Fox | Total | Recall |
| :--- | ---: | ---: | ---: | ---: | ---: |
| **Cat** | **90** | 8 | 2 | 100 | 0.900 |
| **Dog** | 5 | **80** | 15 | 100 | 0.800 |
| **Fox** | 1 | 9 | **40** | 50 | 0.800 |
| **Total** | 96 | 97 | 57 | 250 | |
| **Precision** | 0.938 | 0.825 | 0.702 | | |Threshold sweep
One matrix per candidate threshold, flattened into rows, for choosing an operating point. Row totals stay at 900 positives and 9,050 negatives.
| Threshold | TP | FN | FP | TN | Precision | Recall |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| 0.30 | 880 | 20 | 120 | 8,930 | 0.880 | 0.978 |
| **0.50** | 850 | 50 | 30 | 9,020 | 0.966 | 0.944 |
| 0.70 | 790 | 110 | 9 | 9,041 | 0.989 | 0.878 |Common Mistakes
Silently using the opposite orientation, for example pasting scikit-learn’s default output, which puts the negative class in the first row and first column, into a positive-first template.
Label the corner cell
Actual \ Predicted, passlabels=[1, 0], and check that the cell you call TP really is actual-positive and predicted-positive.Headlining accuracy on an imbalanced dataset, where most of the score comes from the easy majority class.
Report precision and recall for the minority class, balanced accuracy, or the Matthews correlation coefficient, and show the majority-class baseline beside them.
Showing only normalised percentages, which hides how many examples each cell represents.
Keep the counts and add percentages in parentheses, stating the normalisation axis: by row gives recall per class on the diagonal, by column gives precision.
Publishing a matrix without the threshold that produced it.
Put the threshold in the caption, or use the sweep variation so readers can see how the counts move as it changes.
Related Markdown Table Templates
Browse all 50 templatesMachine Learning Model Benchmark Matrix
Compare machine learning and deep learning models across accuracy, precision, recall, F1 score, model parameter count, and inference latency.
Hypothesis Testing & Statistical Analysis
Summarize inferential statistical hypotheses, sample sizes, degrees of freedom, test statistics, p-values, and empirical conclusions.
Big-O Algorithmic Time & Space Complexity
Comprehensive cheat sheet for common data structures and sorting algorithms, comparing best, average, worst time, and space complexity.
Classification Model Confusion Matrix: Frequently Asked Questions
What is the difference between precision and recall?
Precision asks “of everything the model flagged, how much was really positive?” and uses the predicted-positive column. Recall asks “of everything that really was positive, how much did the model catch?” and uses the actual-positive row. Raising the threshold usually trades recall for precision.
Why does scikit-learn’s confusion matrix look flipped compared with textbooks?
scikit-learn puts actual classes on rows and predictions on columns, ordered by sorted label, so for 0/1 labels the negative class is first and the top-left cell is TN. Many textbooks put the positive class first. Neither is wrong; just label the axes.
Are sensitivity and specificity the same as recall?
Sensitivity is exactly recall, the true-positive rate. Specificity is the true-negative rate, TN / (TN + FP), the equivalent of recall for the negative class. Medical literature also uses positive predictive value for precision.
What counts as a good F1 score?
There is no universal cut-off. F1 ignores true negatives and weights precision and recall equally, so judge it against a baseline on the same data and against what each type of error costs. If misses are much worse than false alarms, an F-beta score with beta above 1 matches priorities better.