3 min read

Moneyline Odds to Percentages

If I told you there was a -200 chance it would rain, would you bring an umbrella? I’ve always struggled with translating these odds to percentages, and this chart does just that. It’s built using reactable and adjusting a demo from a here.

Code is below . . .

library(reactable)
library(htmltools)
data <- read.csv("odds.csv", stringsAsFactors = FALSE)
tbl <- reactable(
  data,
  pagination = FALSE,
  defaultSorted = "short_odds",
  defaultColDef = colDef(headerClass = "header-p", align = "left"),
  columns = list(
    short_odds = colDef(
      name = "➖ Short Odds (i.e. -200)",
      cell = JS("function(cellInfo) {
        // Format as percentage
        const pct = (cellInfo.value * 100).toFixed(1) + '%'
        // Pad single-digit numbers
        let value = pct.padStart(5)
        // Show % on first row only
        if (cellInfo.viewIndex > 0) {
          value = value.replace('%', ' ')
        }
        // Render bar chart
        return (
          '<div class=\"bar-cell\">' +
            '<span class=\"number\">' + value + '</span>' +
            '<div class=\"bar-chart\" style=\"background-color: #e1e1e1\">' +
              '<div class=\"bar\" style=\"width: ' + pct + '; background-color: #00FF00\"></div>' +
            '</div>' +
          '</div>'
        )
      }"),
      html = TRUE
    ),
    odds = colDef(
      name = "➖ ML Odds ➕",
      width = 150, align = "center"
    ),
    long_odds = colDef(
      name = "➕ Long Odds (i.e. +200)",
      defaultSortOrder = "desc",
      cell = JS("function(cellInfo) {
        // Format as percentage
        const pct = (cellInfo.value * 100).toFixed(1) + '%'
        // Pad single-digit numbers
        let value = pct.padStart(5)
        // Show % on first row only
        if (cellInfo.viewIndex > 0) {
          value = value.replace('%', ' ')
        }
        // Render bar chart
        return (
          '<div class=\"bar-cell\">' +
            '<span class=\"number\">' + value + '</span>' +
            '<div class=\"bar-chart\" style=\"background-color: #e1e1e1\">' +
              '<div class=\"bar\" style=\"width: ' + pct + '; background-color: #F08080\"></div>' +
            '</div>' +
          '</div>'
        )
      }"),
      html = TRUE
    )
  ),
  compact = TRUE,
  class = "odds-tbl"
)

div(class = "odds-style",
  div(class = "odds-header-p",
  ),
  tbl
)
tags$link(href = "https://fonts.googleapis.com/css?family=Karla:400,700|Fira+Mono&display=fallback",
          rel = "stylesheet")
.odds-style {
  margin: 0 auto;
  width: 575px;
  font-family: Karla, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.title {
  margin: 18px 0;
}
.title h2 {
  font-weight: 600;
}
.odds-tbl {
  font-size: 13px;
  line-height: 18px;
}
.odds-tbl a {
  color: inherit;
}
.header-p {
  border-bottom: 2px solid #555;
  font-weight: 400;
  text-transform: uppercase;
}
.header-p:hover {
  background-color: #eee;
}
.bar-cell {
  display: flex;
  align-items: center;
}
.number {
  font-family: "Fira Mono", Consolas, Monaco, monospace;
  font-size: 13.5px;
  white-space: pre;
}
.bar-chart {
  flex-grow: 1;
  margin-left: 6px;
  height: 14px;
}
.bar {
  height: 100%;
}