Skip to content
All stat categories, model concepts, and definitions used across Football pages.
Panna Model
The Panna rating system is a predictive player value model for association football, analogous to TORP for AFL.
| Panna |
Overall player rating: Offense − Defense. Higher = more valuable. |
| Offense |
Expected goals (xG) created per 90 minutes, adjusted for teammate and opponent quality. |
| Defense |
Expected goals prevented per 90 minutes. Negative values are better (more xG prevented). |
| SPM |
Statistical Performance Metric — box-score regularized plus-minus, complementary to xG-based ratings. |
| Percentile |
Player’s rank as a percentile among all rated players (100 = top). |
Future Metrics (Coming Soon)
These metrics will be available once the Panna data pipeline generates per-match value breakdowns:
| EPV |
Expected Possession Value — the expected goal value of a possession given the current game state. |
| EPR |
Expected Points Rating — predictive rating derived from historical EPV contributions. |
| SPV |
Statistical Performance Value — per-match box-score value contribution. |
| SPR |
Statistical Performance Rating — predictive career rating from SPV history. |
| Equity |
Play-by-play credit assigned to each action based on EPV change. Positive = value added, negative = value lost. |
Box-Score Stats
Show code
defs = window.footballStatDefs
{
const container = html`<div></div>`
for (const [key, cat] of Object.entries(defs)) {
if (cat.columns.length === 0) continue
// Skip value/ratings — documented in markdown above
if (key === "value" || key === "ratings") continue
const section = document.createElement("div")
section.style.marginBottom = "2rem"
const h2 = document.createElement("h2")
h2.textContent = cat.label
h2.style.marginBottom = "0.75rem"
section.appendChild(h2)
const table = document.createElement("table")
table.className = "table table-sm"
const thead = document.createElement("thead")
const headRow = document.createElement("tr")
for (const label of ["Stat", "Description"]) {
const th = document.createElement("th")
th.textContent = label
headRow.appendChild(th)
}
thead.appendChild(headRow)
table.appendChild(thead)
const tbody = document.createElement("tbody")
for (const col of cat.columns) {
const tr = document.createElement("tr")
const tdName = document.createElement("td")
const strong = document.createElement("strong")
strong.textContent = cat.header[col] || col
tdName.appendChild(strong)
tdName.style.whiteSpace = "nowrap"
const tdDesc = document.createElement("td")
tdDesc.textContent = cat.tooltip?.[col] || "\u2014"
tr.appendChild(tdName)
tr.appendChild(tdDesc)
tbody.appendChild(tr)
}
table.appendChild(tbody)
section.appendChild(table)
container.appendChild(section)
}
return container
}