[1] 0.8175745
Bradley-Terry Models II
load("wd1hockey_regseason_2024_2025.RData")
no_ties <-
no_ties |>
dplyr::mutate(
neutral = dplyr::case_when(
grepl("IceBreaker", Notes) ~ 1,
grepl("East/West", Notes) & Home != "Minnesota" ~ 1,
grepl("Nutmeg", Notes) & Home != "Sacred Heart" ~ 1,
grepl("Mayor", Notes) & Home == "Rensselaer" ~ 1,
grepl("Smashville", Notes) ~ 1,
grepl("Beanpot", Notes) & Home != "Northeastern" ~ 1,
Date == "3/7/2025" & Home == "St. Lawrence" & Opponent == "Colgate" ~ 1,
Date == "3/7/2025" & Home == "Minnesota" & Opponent == "Ohio State" ~ 1,
Date == "3/8/2025" & Home == "Minnesota" & Opponent == "Wisconsin" ~ 1,
.default = 0))home.team and away.team
unik_teams <- sort(unique(c(no_ties$Home, no_ties$Opponent)))
results <-
no_ties |>
dplyr::rename(home.team = Home, away.team = Opponent) |>
dplyr::group_by(home.team, away.team, neutral) |>
dplyr::summarise(
home.win = sum(Home_Winner),
away.win = sum(Opp_Winner), .groups = 'drop') |>
dplyr::mutate(
home.team = factor(home.team, levels = unik_teams),
away.team = factor(away.team,levels = unik_teams),
home.athome = ifelse(neutral == 1, 0, 1),
away.athome = 0)
Call:
BradleyTerry2::BTm(outcome = cbind(home.win, away.win), player1 = home.team,
player2 = away.team, formula = ~team + at.home, id = "team",
refcat = "New Hampshire", data = tmp_df)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
teamAssumption -4.74585 1.24679 -3.806 0.000141 ***
teamBemidji State -0.75271 0.80275 -0.938 0.348415
teamBoston College 0.88528 0.52440 1.688 0.091375 .
teamBoston University 1.13510 0.52380 2.167 0.030229 *
teamBrown 0.14970 0.65910 0.227 0.820326
teamClarkson 1.22543 0.62242 1.969 0.048975 *
teamColgate 2.07470 0.65875 3.149 0.001636 **
teamConnecticut 1.11779 0.53080 2.106 0.035217 *
teamCornell 2.63595 0.71812 3.671 0.000242 ***
teamDartmouth -0.79392 0.69582 -1.141 0.253875
teamFranklin Pierce -3.94938 1.23673 -3.193 0.001406 **
teamHarvard -2.17071 0.87974 -2.467 0.013608 *
teamHoly Cross -0.50866 0.52815 -0.963 0.335499
teamLindenwood -1.47362 0.71219 -2.069 0.038532 *
teamLIU -3.07687 1.21642 -2.529 0.011425 *
teamMaine -0.38395 0.53066 -0.724 0.469350
teamMercyhurst 0.51099 0.64323 0.794 0.426955
teamMerrimack -0.78126 0.52339 -1.493 0.135519
teamMinnesota 2.76577 0.74846 3.695 0.000220 ***
teamMinnesota Duluth 2.17957 0.76679 2.842 0.004477 **
teamMinnesota State 0.78237 0.78409 0.998 0.318375
teamNortheastern 1.02645 0.51130 2.008 0.044695 *
teamOhio State 3.27350 0.79365 4.125 3.71e-05 ***
teamPenn State 2.02649 0.71347 2.840 0.004507 **
teamPost -4.42233 1.23567 -3.579 0.000345 ***
teamPrinceton 0.90657 0.66577 1.362 0.173298
teamProvidence 0.66011 0.52149 1.266 0.205585
teamQuinnipiac 1.07109 0.59894 1.788 0.073726 .
teamRensselaer -0.29970 0.61486 -0.487 0.625954
teamRIT -0.21963 0.66882 -0.328 0.742616
teamRobert Morris -1.61446 0.71727 -2.251 0.024397 *
teamSacred Heart -3.33864 1.22272 -2.731 0.006324 **
teamSt. Anselm -4.02309 1.20138 -3.349 0.000812 ***
teamSt. Cloud State 1.28838 0.75363 1.710 0.087349 .
teamSt. Lawrence 1.31564 0.62136 2.117 0.034229 *
teamSt. Michael's -5.91267 1.29972 -4.549 5.39e-06 ***
teamSt. Thomas 0.08436 0.80906 0.104 0.916954
teamStonehill -4.17369 1.23819 -3.371 0.000749 ***
teamSyracuse -0.39362 0.65720 -0.599 0.549213
teamUnion -0.05669 0.59147 -0.096 0.923646
teamVermont -0.56482 0.52659 -1.073 0.283454
teamWisconsin 4.77395 0.99956 4.776 1.79e-06 ***
teamYale 0.82238 0.63286 1.299 0.193779
at.home 0.31079 0.09542 3.257 0.001126 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 817.92 on 476 degrees of freedom
Residual deviance: 455.43 on 432 degrees of freedom
AIC: 661.38
Number of Fisher Scoring iterations: 6
diff: quantifies different in team strengths (\(\textrm{WIS} - \textrm{OSU}\))diff: difference in team strengths (log-odds scale)prob: \(\mathbb{P}(\textrm{higher seed beats lower seed})\)seeds <- data.frame(
Team = c("Wisconsin", "Ohio State", "Cornell", "Minnesota"),
Seed = 1:4)
possible_matchups <-
expand.grid(Hi = seeds$Team, Lo = seeds$Team) |>
as.data.frame() |>
dplyr::inner_join(y = seeds |> dplyr::rename(Hi = Team, Hi.Seed=Seed), by = "Hi") |>
dplyr::inner_join(y = seeds |> dplyr::rename(Lo = Team, Lo.Seed=Seed), by = "Lo") |>
dplyr::filter(Hi.Seed < Lo.Seed) |>
dplyr::mutate(neutral = ifelse(Hi == "Minnesota" | Lo == "Minnesota", 0, 1)) |>
dplyr::mutate(
diff = lambda_hat[Hi, "ability"] - lambda_hat[Lo, "ability"],
prob = dplyr::case_when(
neutral == 1 ~ 1/(1 + exp(-1 * diff)),
neutral == 0 & Hi == "Minnesota" ~ 1/(1 + exp(-1 * (diff + lambda0_hat))),
neutral == 0 & Lo == "Minnesota" ~ 1/(1 + exp(-1 * (diff - lambda0_hat))))) Hi Lo Hi.Seed Lo.Seed neutral diff prob
1 Wisconsin Ohio State 1 2 1 1.5004449 0.8176408
2 Wisconsin Cornell 1 3 1 2.1379945 0.8945416
3 Ohio State Cornell 2 3 1 0.6375496 0.6541993
4 Wisconsin Minnesota 1 4 0 2.0081754 0.8451936
5 Ohio State Minnesota 2 4 0 0.5077305 0.5490778
6 Cornell Minnesota 3 4 0 -0.1298191 0.3915970
semis <-
data.frame(Hi = c("Wisconsin", "Ohio State"), Lo = c("Minnesota", "Cornell")) |>
dplyr::left_join(possible_matchups |> dplyr::select(Hi, Lo, prob), by = c("Hi", "Lo"))
set.seed(479)
sf_winners <- c(NA, NA)
sf_outcomes <- rbinom(n = nrow(semis), size = 1, prob = semis$prob)
for(i in 1:nrow(semis)){
if(sf_outcomes[i] == 1) sf_winners[i] <- semis$Hi[i]
else sf_winners[i] <- semis$Lo[i]
}
cat("Semi-final outcomes:", sf_outcomes, "\n")Semi-final outcomes: 0 1
Semi-final winners: Minnesota Ohio State
finals <-
data.frame(Team1 = sf_winners[1], Team2 = sf_winners[2]) |>
dplyr::left_join(y = seeds |> dplyr::rename(Team1 = Team, Team1.Seed = Seed), by = "Team1") |>
dplyr::left_join(y = seeds |> dplyr::rename(Team2 = Team, Team2.Seed = Seed), by = "Team2") |>
dplyr::mutate(
Hi = ifelse(Team1.Seed < Team2.Seed, Team1, Team2),
Lo = ifelse(Team1.Seed < Team2.Seed, Team2, Team1)) |>
dplyr::select(Hi, Lo) |>
dplyr::left_join(
y = possible_matchups |> dplyr::select(Hi, Lo, prob), by = c("Hi", "Lo"))
finals Hi Lo prob
1 Ohio State Minnesota 0.5490778
for loopSF1_winner and SF2_winnerFinals_Hi and Finals_LoChampion SF1_Winner SF2_Winner Finals_Hi Finals_Lo Champion
1 Wisconsin Ohio State Wisconsin Ohio State Wisconsin
2 Wisconsin Ohio State Wisconsin Ohio State Ohio State
3 Wisconsin Cornell Wisconsin Cornell Wisconsin
4 Wisconsin Ohio State Wisconsin Ohio State Wisconsin
5 Wisconsin Cornell Wisconsin Cornell Wisconsin
6 Wisconsin Ohio State Wisconsin Ohio State Wisconsin
7 Wisconsin Ohio State Wisconsin Ohio State Wisconsin
8 Wisconsin Ohio State Wisconsin Ohio State Wisconsin
9 Wisconsin Cornell Wisconsin Cornell Wisconsin
10 Minnesota Ohio State Ohio State Minnesota Minnesota