Getting Started and Helper Functions

# Load necessary libraries
library(dplyr)
library(data.table)
library(knitr)

Input Model Predicted Spreads

# To input model predicted spreads
predict_ncaa_round <- function(round){
    # Subset data by round
    if(round == 0){rows <- c(1:4)}
    if(round == 1){rows <- c(5:36)}    
    if(round == 2){rows <- c(37:52)}
    if(round == 3){rows <- c(53:60)}
    if(round == 4){rows <- c(61:64)}    
    if(round == 5){rows <- c(65:66)}
    if(round == 6){rows <- 67}
    
    # Append model predicted spread for higher seeds
    bracket <- bracket[rows,] %>% 
        left_join(select(PredictionData, TeamName.x, TeamName.y, Prediction, Win_Prob), by = c('TeamName.x' = 'TeamName.x', 'TeamName.y'= 'TeamName.y'))  
    
    # Get the rows of lesser seeds
    na_rows <- which(is.na(bracket$Prediction))

    # Impute prediction data from lesser seeds
    for (i in na_rows) {
        bracket[i, 7] <- -PredictionData[which(bracket[i, 5] == PredictionData$TeamName.y & bracket[i,6] == PredictionData$TeamName.x), 9]
    
        bracket[i, 8] <- 1 - PredictionData[which(bracket[i, 5] == PredictionData$TeamName.y & bracket[i,6] == PredictionData$TeamName.x), 10]
    }
    
    bracket
}

Determining Model Choice

# To determine who model would bet on
ATSchoice <- function(bracket){
    bracket$ModelChoice <- NA
    for (i in c(1:length(bracket$Diff))){
        if(bracket$Diff[i] < -2) 
            {bracket$ModelChoice[i] <- bracket$TeamName.y[i]}
        if(bracket$Diff[i] > 2) 
            {bracket$ModelChoice[i] <- bracket$TeamName.x[i]}
        if(bracket$Diff[i] <= 2 & bracket$Diff[i] >= -2) 
            {bracket$ModelChoice[i] <- "No choice"}
    }
    bracket
}

Assessing Model Accuracy

ATSresults <- function(bracket){
    # Determine ATS winner
    bracket$ATSWinner <- NA
    for (i in c(1:length(bracket$Diff))){
        FinalDiff <- bracket$Team.x.score[i] - bracket$Team.y.score[i]
        if(!is.na(FinalDiff)) {
            if(FinalDiff < bracket$Spread[i]) 
                {bracket$ATSWinner[i] <- bracket$TeamName.y[i]}
            if(FinalDiff > bracket$Spread[i]) 
                {bracket$ATSWinner[i] <- bracket$TeamName.x[i]}
            if(FinalDiff == bracket$Spread[i]) 
                {bracket$ATSWinner[i] <- "Push"}
        }
    }
    # Determine if Model Prediction was Correct, Incorrect, or Not Made
    bracket$Accuracy <- NA
    for (i in c(1:length(bracket$Diff))){
        if(!is.na(bracket$ATSWinner[i])) {
            if(bracket$ModelChoice[i] == bracket$ATSWinner[i])      
                {bracket$Accuracy[i] <- "Correct"}
            if(bracket$ModelChoice[i] != bracket$ATSWinner[i]) 
                {bracket$Accuracy[i] <- "Incorrect"}
            if(bracket$ATSWinner[i] == "Push") 
                {bracket$Accuracy[i] <- "Push"}
        }
        if(is.na(bracket$ATSWinner[i])) 
            {bracket$Accuracy[i] <- NA}
        if(bracket$ModelChoice[i] == "No choice")
            {bracket$Accuracy[i] <- NA}
    }
    bracket
}

Putting winners into next round

# Function to advance winners from "x" final bracket into "bracket" object
advance_winners <- function(x){
    for (i in c(1:length(x$Slot))){
        slot <- x$Slot[i]
        if(x$Team.x.score[i] > x$Team.y.score[i]) {
            winner <- x$TeamName.x[i]
        } else{
            winner <- x$TeamName.y[i]
        }
    
        rownum <- which(bracket[,c(3:4)] == slot, arr.ind = TRUE)[1]
        colnum <- which(bracket[,c(3:4)] == slot, arr.ind = TRUE)[2]

        if(colnum == 1) 
            {bracket$TeamName.x[rownum] <- winner}
        else 
            {bracket$TeamName.y[rownum] <- winner}
    }   
    bracket
}

Creating Initial Bracket

The prediction model is a XGBoost model with 5-fold cross validation. The cleaning and preparation of the data and the training and testing of that model are in a script which can be found here [insert link here]. The prediction data for all possible pairs of opponents are in the file “saferesults.Rds”.

# Load prediction data from model
PredictionData <- readRDS("saferesults.Rds")
#kable(head(PredictionData))

# Load the 2018 bracket data and team names
tourneyslots <- fread("NCAATourneySlots.csv") %>% filter(Season == 2018)
tourneySeeds <- fread('NCAATourneySeeds.csv') %>% filter(Season == 2018)
teams <- fread('teams.csv')

# Attach team names to seed info
tourneySeeds <- tourneySeeds %>% 
    left_join(teams, by = 'TeamID') %>% 
    select(Season, Seed, TeamID, TeamName)

# Manually set the play-in winners
tourneyslots[5,4] <- "W16b"
tourneyslots[10,4] <- "W11a"
tourneyslots[18, 4] <- "X11b"
tourneyslots[29, 4] <- "Z16b"

# Complete Bracket
bracket <- tourneyslots %>% 
    left_join(select(tourneySeeds, -c(Season, TeamID)), by=c('StrongSeed'='Seed')) %>% 
    left_join(select(tourneySeeds, -c(Season, TeamID)), by=c('WeakSeed'='Seed')) 

Play-in Round

Model Predictions

# Append model predicted spreads
playin <- predict_ncaa_round(round = 0)
# All Play-in Spreads are retroactive
# Spreads are positive if strong seed is favored, negative if underdogs
playinspreads <- data.frame(
    matrix(
        c(
"St Bonaventure", -3.5,
"Long Island", -4.5,
"Arizona St", 1,
"NC Central", -4.5), 
    ncol = 2, byrow = TRUE), stringsAsFactors = FALSE
)

names(playinspreads) <- c("Team", "Spread")
playinspreads$Spread <- as.numeric(playinspreads$Spread)

# Append spreads on to bracket
playin <- playin %>% 
    left_join(playinspreads, by = c('TeamName.x' = 'Team')) %>% 
    mutate(Diff = Prediction - Spread)

A team needs to have a predicted spread more than 2 points beyond the sportsbook spread in order to have greater than a 55% chance of winning ATS (against the spread), which is the minimum winning percent needed to break even. In the ModelChoice variable, the model will return “No Choice” if the model prediction is within 2 points of the sportsbook spread or otherwise return which team the model predicts has a > 55% chance to cover the spread.

# Determine who model would bet on ATS
playin <- ATSchoice(playin)

Note: The Prediction variable outputs how many points the StrongSeed is expected to beat the WeakSeed by. So Prediction will be 3 if the StrongSeed team is favored by 3 points. This is the opposite of conventional wagering notation (-3). The Spread variable is not the conventional spread, but gives how many points the StrongSeed team is favored over the WeakSeed team by. The Win_Prob variable is the probability of the StrongSeed team winning the game straight up.

#### Output of Model Predictions ATS
playinpredictionstable <- playin[1:4,] %>% select(StrongSeed, TeamName.x, WeakSeed, TeamName.y, Spread, Prediction, ModelChoice, Diff)
kable(playinpredictionstable)
StrongSeed TeamName.x WeakSeed TeamName.y Spread Prediction ModelChoice Diff
W11a St Bonaventure W11b UCLA -3.5 2.6 St Bonaventure 6.1
W16a Long Island W16b Radford -4.5 0.9 Long Island 5.4
X11a Arizona St X11b Syracuse 1.0 1.3 No choice 0.3
Z16a NC Central Z16b TX Southern -4.5 9.3 NC Central 13.8

Game Results and Model Performance

# Input Game Results Manually
# Eventually I need to scrape this data from web
playinresults <- data.frame(
    matrix(
        c(
"St Bonaventure", 65, 58, 
"Long Island", 61, 71,
"Arizona St", 56, 60,
"NC Central", 46, 64), ncol = 3, byrow = TRUE), stringsAsFactors = FALSE)

names(playinresults) <- c("Team", "Team.x.score", "Team.y.score")
playinresults$Team.x.score <- as.numeric(playinresults$Team.x.score)
playinresults$Team.y.score <- as.numeric(playinresults$Team.y.score)

# Append Game Results to Bracket
playin <- playin %>% 
    left_join(playinresults, by = c('TeamName.x' = 'Team'))
# Determine Model Accuracy
playin <- ATSresults(playin)
# Table of all outcomes
output_table_0 <- playin[1:4,] %>% 
    filter(!is.na(TeamName.x)) %>% 
    select(StrongSeed, TeamName.x, WeakSeed, TeamName.y, Spread, Prediction, ModelChoice, Team.x.score, Team.y.score, ATSWinner, Accuracy)
kable(output_table_0)
StrongSeed TeamName.x WeakSeed TeamName.y Spread Prediction ModelChoice Team.x.score Team.y.score ATSWinner Accuracy
W11a St Bonaventure W11b UCLA -3.5 2.6 St Bonaventure 65 58 St Bonaventure Correct
W16a Long Island W16b Radford -4.5 0.9 Long Island 61 71 Radford Incorrect
X11a Arizona St X11b Syracuse 1.0 1.3 No choice 56 60 Syracuse NA
Z16a NC Central Z16b TX Southern -4.5 9.3 NC Central 46 64 TX Southern Incorrect
# Summary of results
table(output_table_0$Accuracy)
## 
##   Correct Incorrect 
##         1         2

The model’s record was 1-2 ATS for the play-in games.

Round 1

Model Predictions

# Append model predicted spreads
round1 <- predict_ncaa_round(1)
# All Round 1 Spreads as of 12:30pm Thursday
# Spreads are positive if strong seed is favored, negative if underdogs
round1spreads <- data.frame(
    matrix(
        c(
"Rhode Island", 1.5,
"Tennessee", 11.5,
"Gonzaga", 13.5,
"Kansas", 14.5,
"Duke", 20,
"Miami FL", 2,
"Ohio St", 8,
"Seton Hall", 3,
"Villanova", 23,
"Kentucky", 5,
"Houston", 4,
"Texas Tech", 11,
"Virginia Tech", 2.5,
"Arizona", 8.5,
"Michigan", 10,
"Florida", 5.5,
"Texas A&M", 2.5,
"Purdue", 20.5,
"Wichita St", 11.5,
"Cincinnati", 14,
"North Carolina", 19.5,
"Arkansas", -1.5,
"West Virginia", 10.5,
"Nevada", 0,
"Creighton", 1,
"Michigan St", 14.5,
"Xavier", 19.5,
"Auburn", 9,
"Virginia", 21,
"TCU", 4,
"Missouri", -1.5,
"Clemson", 4.5), 
    ncol = 2, byrow = TRUE), stringsAsFactors = FALSE
)

names(round1spreads) <- c("Team", "Spread")
round1spreads$Spread <- as.numeric(round1spreads$Spread)

# Append spreads on to bracket
round1 <- round1 %>% 
    left_join(round1spreads, by = c('TeamName.x' = 'Team')) %>% 
    mutate(Diff = Prediction - Spread)

A team needs to have a predicted spread more than 2 points beyond the sportsbook spread in order to have greater than a 55% chance of winning ATS (against the spread), which is the minimum winning percent needed to break even. In the ModelChoice variable, the model will return “No Choice” if the model prediction is within 2 points of the sportsbook spread or otherwise return which team the model predicts has a > 55% chance to cover the spread.

# Determine who model would bet on ATS
round1 <- ATSchoice(round1)

There is something wrong with my predictions for the top seeds, especially 1s and 2s, so I stayed away from following my model on any of those. Other kagglers have had this same issue and have manually input values for them, some trouble I didn’t want to bother with until I had the rest of the model running well. I’ll focus on that at some point in the future. For now, I’m going to manually input “No Choice” for all 1 and 2 seeds of Villanova, Kansas, Duke, Purdue, Cincinnati, Xavier, UNC, and Virginia.

# Manually set 1 and 2 seeds to no choice
round1$ModelChoice[c(1,2,9,10,17,18,25,26)] <- "No choice"
# Output of Model Predictions ATS
round1predictionstable <- round1 %>% select(StrongSeed, TeamName.x, WeakSeed, TeamName.y, Spread, Prediction, ModelChoice, Diff)

kable(round1predictionstable)
StrongSeed TeamName.x WeakSeed TeamName.y Spread Prediction ModelChoice Diff
W01 Villanova W16b Radford 23.0 9.5 No choice -13.5
W02 Purdue W15 CS Fullerton 20.5 13.4 No choice -7.1
W03 Texas Tech W14 SF Austin 11.0 11.4 No choice 0.4
W04 Wichita St W13 Marshall 11.5 4.3 Marshall -7.2
W05 West Virginia W12 Murray St 10.5 1.1 Murray St -9.4
W06 Florida W11a St Bonaventure 5.5 8.0 Florida 2.5
W07 Arkansas W10 Butler -1.5 -1.4 No choice 0.1
W08 Virginia Tech W09 Alabama 2.5 9.2 Virginia Tech 6.7
X01 Kansas X16 Penn 14.5 6.8 No choice -7.7
X02 Duke X15 Iona 20.0 15.0 No choice -5.0
X03 Michigan St X14 Bucknell 14.5 7.8 Bucknell -6.7
X04 Auburn X13 Col Charleston 9.0 -0.1 Col Charleston -9.1
X05 Clemson X12 New Mexico St 4.5 -0.3 New Mexico St -4.8
X06 TCU X11b Syracuse 4.0 1.0 Syracuse -3.0
X07 Rhode Island X10 Oklahoma 1.5 4.7 Rhode Island 3.2
X08 Seton Hall X09 NC State 3.0 1.1 No choice -1.9
Y01 Virginia Y16 UMBC 21.0 3.5 No choice -17.5
Y02 Cincinnati Y15 Georgia St 14.0 7.1 No choice -6.9
Y03 Tennessee Y14 Wright St 11.5 5.4 Wright St -6.1
Y04 Arizona Y13 Buffalo 8.5 -7.1 Buffalo -15.6
Y05 Kentucky Y12 Davidson 5.0 1.7 Davidson -3.3
Y06 Miami FL Y11 Loyola-Chicago 2.0 6.7 Miami FL 4.7
Y07 Nevada Y10 Texas 0.0 6.2 Nevada 6.2
Y08 Creighton Y09 Kansas St 1.0 -1.1 Kansas St -2.1
Z01 Xavier Z16b TX Southern 19.5 15.6 No choice -3.9
Z02 North Carolina Z15 Lipscomb 19.5 13.1 No choice -6.4
Z03 Michigan Z14 Montana 10.0 5.2 Montana -4.8
Z04 Gonzaga Z13 UNC Greensboro 13.5 7.8 UNC Greensboro -5.7
Z05 Ohio St Z12 S Dakota St 8.0 8.9 No choice 0.9
Z06 Houston Z11 San Diego St 4.0 5.1 No choice 1.1
Z07 Texas A&M Z10 Providence 2.5 2.3 No choice -0.2
Z08 Missouri Z09 Florida St -1.5 -11.0 Florida St -9.5

The model recommendations for round 1 are:

  • URI -2.5
  • Wright St +12
  • UNC Greensboro +12.5
  • Miami FL -2
  • Davidson +5
  • Va Tech -2.5
  • Buffalo +9
  • Florida -5.5
  • Montana +11
  • Marshall +11.5
  • Murray St +10.5
  • Bucknell +14.5 (I’m not putting my own money on this, but will consider it an outcome to evaluate the model on)
  • Col. Charleston +9
  • New Mexico St +4.5
  • Syracuse +4
  • Kansas St +1
  • Nevada EVEN
  • Florida St -1.5

Game Results and Model Performance

# Input Game Results Manually
# Eventually I need to scrape this data from web
round1results <- data.frame(
    matrix(
        c(
"St Bonaventure", 65, 58, 
"Long Island", 61, 71,
"Arizona St", 56, 60,
"NC Central", 46, 64,
"Rhode Island", 83, 78,
"Tennessee", 73, 47,
"Gonzaga", 68, 64,
"Kansas", 76, 60,
"Duke", 89, 67,
"Miami FL", 62, 64,
"Ohio St", 81, 73,
"Seton Hall", 94, 83,
"Villanova", 87, 61,
"Kentucky", 78, 73,
"Houston", 67, 65,
"Texas Tech", 70, 60,
"Virginia Tech", 83, 86,
"Arizona", 68, 89,
"Michigan", 61, 47,
"Florida", 77, 62,
"Texas A&M", 73, 69,
"Purdue", 74, 48,
"Wichita St", 75, 81,
"Cincinnati", 68, 53,
"North Carolina", 84, 66,
"Arkansas", 62, 79,
"West Virginia", 85, 68,
"Nevada", 87, 83,
"Creighton", 59, 69,
"Michigan St", 82, 78,
"Xavier", 91, 72,
"Auburn", 62, 58,
"Virginia", 54, 74,
"TCU", 52, 57,
"Missouri", 54, 67,
"Clemson", 79, 68), ncol = 3, byrow = TRUE), stringsAsFactors = FALSE)

names(round1results) <- c("Team", "Team.x.score", "Team.y.score")
round1results$Team.x.score <- as.numeric(round1results$Team.x.score)
round1results$Team.y.score <- as.numeric(round1results$Team.y.score)

# Append Game Results to Bracket
round1 <- round1 %>% 
    left_join(round1results, by = c('TeamName.x' = 'Team'))
# Determine Model's Accuracy ATS
round1 <- ATSresults(round1)

Note: The Prediction variable outputs how many points the StrongSeed is expected to beat the WeakSeed by. So Prediction will be 3 if the StrongSeed team is favored by 3 points. This is the opposite of conventional wagering notation (-3). The Spread variable is not the conventional spread, but gives how many points the StrongSeed team is favored over the WeakSeed team by. The Win_Prob variable is the probability of the StrongSeed team winning the game straight up.

# TABLE OF ALL RESULTS
output_table_1 <- round1 %>% 
    filter(!is.na(TeamName.x)) %>% 
    select(StrongSeed, TeamName.x, WeakSeed, TeamName.y, Spread, Prediction, ModelChoice, Team.x.score, Team.y.score, ATSWinner, Accuracy)

output_table_1 <- rbind(output_table_0, output_table_1)
kable(output_table_1)
StrongSeed TeamName.x WeakSeed TeamName.y Spread Prediction ModelChoice Team.x.score Team.y.score ATSWinner Accuracy
W11a St Bonaventure W11b UCLA -3.5 2.6 St Bonaventure 65 58 St Bonaventure Correct
W16a Long Island W16b Radford -4.5 0.9 Long Island 61 71 Radford Incorrect
X11a Arizona St X11b Syracuse 1.0 1.3 No choice 56 60 Syracuse NA
Z16a NC Central Z16b TX Southern -4.5 9.3 NC Central 46 64 TX Southern Incorrect
W01 Villanova W16b Radford 23.0 9.5 No choice 87 61 Villanova NA
W02 Purdue W15 CS Fullerton 20.5 13.4 No choice 74 48 Purdue NA
W03 Texas Tech W14 SF Austin 11.0 11.4 No choice 70 60 SF Austin NA
W04 Wichita St W13 Marshall 11.5 4.3 Marshall 75 81 Marshall Correct
W05 West Virginia W12 Murray St 10.5 1.1 Murray St 85 68 West Virginia Incorrect
W06 Florida W11a St Bonaventure 5.5 8.0 Florida 77 62 Florida Correct
W07 Arkansas W10 Butler -1.5 -1.4 No choice 62 79 Butler NA
W08 Virginia Tech W09 Alabama 2.5 9.2 Virginia Tech 83 86 Alabama Incorrect
X01 Kansas X16 Penn 14.5 6.8 No choice 76 60 Kansas NA
X02 Duke X15 Iona 20.0 15.0 No choice 89 67 Duke NA
X03 Michigan St X14 Bucknell 14.5 7.8 Bucknell 82 78 Bucknell Correct
X04 Auburn X13 Col Charleston 9.0 -0.1 Col Charleston 62 58 Col Charleston Correct
X05 Clemson X12 New Mexico St 4.5 -0.3 New Mexico St 79 68 Clemson Incorrect
X06 TCU X11b Syracuse 4.0 1.0 Syracuse 52 57 Syracuse Correct
X07 Rhode Island X10 Oklahoma 1.5 4.7 Rhode Island 83 78 Rhode Island Correct
X08 Seton Hall X09 NC State 3.0 1.1 No choice 94 83 Seton Hall NA
Y01 Virginia Y16 UMBC 21.0 3.5 No choice 54 74 UMBC NA
Y02 Cincinnati Y15 Georgia St 14.0 7.1 No choice 68 53 Cincinnati NA
Y03 Tennessee Y14 Wright St 11.5 5.4 Wright St 73 47 Tennessee Incorrect
Y04 Arizona Y13 Buffalo 8.5 -7.1 Buffalo 68 89 Buffalo Correct
Y05 Kentucky Y12 Davidson 5.0 1.7 Davidson 78 73 Push Push
Y06 Miami FL Y11 Loyola-Chicago 2.0 6.7 Miami FL 62 64 Loyola-Chicago Incorrect
Y07 Nevada Y10 Texas 0.0 6.2 Nevada 87 83 Nevada Correct
Y08 Creighton Y09 Kansas St 1.0 -1.1 Kansas St 59 69 Kansas St Correct
Z01 Xavier Z16b TX Southern 19.5 15.6 No choice 91 72 TX Southern NA
Z02 North Carolina Z15 Lipscomb 19.5 13.1 No choice 84 66 Lipscomb NA
Z03 Michigan Z14 Montana 10.0 5.2 Montana 61 47 Michigan Incorrect
Z04 Gonzaga Z13 UNC Greensboro 13.5 7.8 UNC Greensboro 68 64 UNC Greensboro Correct
Z05 Ohio St Z12 S Dakota St 8.0 8.9 No choice 81 73 Push NA
Z06 Houston Z11 San Diego St 4.0 5.1 No choice 67 65 San Diego St NA
Z07 Texas A&M Z10 Providence 2.5 2.3 No choice 73 69 Texas A&M NA
Z08 Missouri Z09 Florida St -1.5 -11.0 Florida St 54 67 Florida St Correct
# Summary of results
table(output_table_1$Accuracy)
## 
##   Correct Incorrect      Push 
##        12         8         1

The model’s record was 11-6-1 ATS in round 1 action and is 12-8-1 ATS over the entire tournament. The model would have been 3-5 in the 1 and 2 seeded games.

One last interesting note is that a lot of 1 seeds seemed to not cover the first half spread. I heard this has some historical traction to it. Something to keep in mind next year.

Round 2

Model Predictions

# Recording Round 1 Winners into Round 2
bracket <- advance_winners(round1)
# Model Predictions for Round 2
round2 <- predict_ncaa_round(2)
# All Round 2 Spreads as of 1:00am Saturday
# Spreads are positive if strong seed is favored, negative if underdogs
round2spreads <- data.frame(
    matrix(
        c(
"Villanova", 11,
"Purdue", 3.5,
"Texas Tech", 0,
"Marshall", -11,
"Kansas", 4.5,
"Duke", 10,
"Michigan St", 9,
"Auburn", 1,
"UMBC", -12,
"Cincinnati", 7,
"Tennessee", 6,
"Buffalo", -7.5,
"Xavier", 4,
"North Carolina", 6,
"Michigan", 3,
"Gonzaga", 2.5), 
    ncol = 2, byrow = TRUE), stringsAsFactors = FALSE
)

names(round2spreads) <- c("Team", "Spread")
round2spreads$Spread <- as.numeric(round2spreads$Spread)

# Append spreads on to bracket
round2 <- round2 %>% 
    left_join(round2spreads, by = c('TeamName.x' = 'Team')) %>% 
    mutate(Diff = Prediction - Spread)

Here are the model predictions for all round 2 games.

# Determine who model would bet on ATS
round2 <- ATSchoice(round2)
# Output of model recommendations
round2_output_table <- round2 %>% 
    select(StrongSeed, WeakSeed, TeamName.x, TeamName.y, Spread, Prediction, Diff, Win_Prob, ModelChoice)

kable(round2_output_table)
StrongSeed WeakSeed TeamName.x TeamName.y Spread Prediction Diff Win_Prob ModelChoice
R1W1 R1W8 Villanova Alabama 11.0 8.3 -2.7 0.737 Alabama
R1W2 R1W7 Purdue Butler 3.5 8.1 4.6 0.734 Purdue
R1W3 R1W6 Texas Tech Florida 0.0 -0.7 -0.7 0.479 No choice
R1W4 R1W5 Marshall West Virginia -11.0 -4.6 6.4 0.368 Marshall
R1X1 R1X8 Kansas Seton Hall 4.5 2.2 -2.3 0.563 Seton Hall
R1X2 R1X7 Duke Rhode Island 10.0 3.8 -6.2 0.610 Rhode Island
R1X3 R1X6 Michigan St Syracuse 9.0 5.2 -3.8 0.649 Syracuse
R1X4 R1X5 Auburn Clemson 1.0 -0.6 -1.6 0.482 No choice
R1Y1 R1Y8 UMBC Kansas St -12.0 -3.5 8.5 0.399 UMBC
R1Y2 R1Y7 Cincinnati Nevada 7.0 9.1 2.1 0.762 Cincinnati
R1Y3 R1Y6 Tennessee Loyola-Chicago 6.0 5.8 -0.2 0.666 No choice
R1Y4 R1Y5 Buffalo Kentucky -7.5 2.2 9.7 0.565 Buffalo
R1Z1 R1Z8 Xavier Florida St 4.0 -0.2 -4.2 0.492 Florida St
R1Z2 R1Z7 North Carolina Texas A&M 6.0 3.3 -2.7 0.596 Texas A&M
R1Z3 R1Z6 Michigan Houston 3.0 3.5 0.5 0.601 No choice
R1Z4 R1Z5 Gonzaga Ohio St 2.5 10.6 8.1 0.803 Gonzaga

The model is still picking against all #1 seeds, but no longer against all #2 seeds. So I’m going to start evaluating model on all recommendations, while still hesitant to wager on #1 seed games. The recommendations are:

  • Alabama +11
  • Purdue -3.5
  • Marshall +11
  • Seton Hall +4.5
  • URI +10
  • Syracuse +9
  • UMBC +12
  • Cincinnati -7
  • Buffalo +7.5
  • Florida St +4
  • Texas A&M +6
  • Gonzaga -2.5

Game Results and Model Performance

# Manual input of round 2 results
# Need to automate this!
round2results <- data.frame(
    matrix(
        c(
"Villanova", 81, 58,
"Texas Tech", 69, 66,
"Duke", 87, 62,
"Tennessee", 62, 63,
"Buffalo", 75, 95,
"Michigan", 64, 63,
"Gonzaga", 90, 84,
"Kansas", 83, 79,
"Purdue", 76, 73,
"Michigan St", 53, 55,
"North Carolina", 65, 86,
"Cincinnati", 73, 75,
"Auburn", 53, 84,
"UMBC", 43, 50,
"Xavier", 70, 75,
"Marshall", 71, 94),  
    ncol = 3, byrow = TRUE), stringsAsFactors = FALSE
)
names(round2results) <- c("Team", "Team.x.score", "Team.y.score")
round2results$Team.x.score <- as.numeric(round2results$Team.x.score)
round2results$Team.y.score <- as.numeric(round2results$Team.y.score)

# Append Game Results to Bracket
round2 <- round2 %>% 
    left_join(round2results, by = c('TeamName.x' = 'Team'))
# Determine Model's Accuracy ATS
round2 <- ATSresults(round2)
# Table of results
output_table_2 <- round2 %>% 
    select(StrongSeed, TeamName.x, WeakSeed, TeamName.y, Spread, Prediction, ModelChoice, Team.x.score, Team.y.score, ATSWinner, Accuracy)

output_table_2 <- rbind(output_table_1, output_table_2)

kable(output_table_2)
StrongSeed TeamName.x WeakSeed TeamName.y Spread Prediction ModelChoice Team.x.score Team.y.score ATSWinner Accuracy
W11a St Bonaventure W11b UCLA -3.5 2.6 St Bonaventure 65 58 St Bonaventure Correct
W16a Long Island W16b Radford -4.5 0.9 Long Island 61 71 Radford Incorrect
X11a Arizona St X11b Syracuse 1.0 1.3 No choice 56 60 Syracuse NA
Z16a NC Central Z16b TX Southern -4.5 9.3 NC Central 46 64 TX Southern Incorrect
W01 Villanova W16b Radford 23.0 9.5 No choice 87 61 Villanova NA
W02 Purdue W15 CS Fullerton 20.5 13.4 No choice 74 48 Purdue NA
W03 Texas Tech W14 SF Austin 11.0 11.4 No choice 70 60 SF Austin NA
W04 Wichita St W13 Marshall 11.5 4.3 Marshall 75 81 Marshall Correct
W05 West Virginia W12 Murray St 10.5 1.1 Murray St 85 68 West Virginia Incorrect
W06 Florida W11a St Bonaventure 5.5 8.0 Florida 77 62 Florida Correct
W07 Arkansas W10 Butler -1.5 -1.4 No choice 62 79 Butler NA
W08 Virginia Tech W09 Alabama 2.5 9.2 Virginia Tech 83 86 Alabama Incorrect
X01 Kansas X16 Penn 14.5 6.8 No choice 76 60 Kansas NA
X02 Duke X15 Iona 20.0 15.0 No choice 89 67 Duke NA
X03 Michigan St X14 Bucknell 14.5 7.8 Bucknell 82 78 Bucknell Correct
X04 Auburn X13 Col Charleston 9.0 -0.1 Col Charleston 62 58 Col Charleston Correct
X05 Clemson X12 New Mexico St 4.5 -0.3 New Mexico St 79 68 Clemson Incorrect
X06 TCU X11b Syracuse 4.0 1.0 Syracuse 52 57 Syracuse Correct
X07 Rhode Island X10 Oklahoma 1.5 4.7 Rhode Island 83 78 Rhode Island Correct
X08 Seton Hall X09 NC State 3.0 1.1 No choice 94 83 Seton Hall NA
Y01 Virginia Y16 UMBC 21.0 3.5 No choice 54 74 UMBC NA
Y02 Cincinnati Y15 Georgia St 14.0 7.1 No choice 68 53 Cincinnati NA
Y03 Tennessee Y14 Wright St 11.5 5.4 Wright St 73 47 Tennessee Incorrect
Y04 Arizona Y13 Buffalo 8.5 -7.1 Buffalo 68 89 Buffalo Correct
Y05 Kentucky Y12 Davidson 5.0 1.7 Davidson 78 73 Push Push
Y06 Miami FL Y11 Loyola-Chicago 2.0 6.7 Miami FL 62 64 Loyola-Chicago Incorrect
Y07 Nevada Y10 Texas 0.0 6.2 Nevada 87 83 Nevada Correct
Y08 Creighton Y09 Kansas St 1.0 -1.1 Kansas St 59 69 Kansas St Correct
Z01 Xavier Z16b TX Southern 19.5 15.6 No choice 91 72 TX Southern NA
Z02 North Carolina Z15 Lipscomb 19.5 13.1 No choice 84 66 Lipscomb NA
Z03 Michigan Z14 Montana 10.0 5.2 Montana 61 47 Michigan Incorrect
Z04 Gonzaga Z13 UNC Greensboro 13.5 7.8 UNC Greensboro 68 64 UNC Greensboro Correct
Z05 Ohio St Z12 S Dakota St 8.0 8.9 No choice 81 73 Push NA
Z06 Houston Z11 San Diego St 4.0 5.1 No choice 67 65 San Diego St NA
Z07 Texas A&M Z10 Providence 2.5 2.3 No choice 73 69 Texas A&M NA
Z08 Missouri Z09 Florida St -1.5 -11.0 Florida St 54 67 Florida St Correct
R1W1 Villanova R1W8 Alabama 11.0 8.3 Alabama 81 58 Villanova Incorrect
R1W2 Purdue R1W7 Butler 3.5 8.1 Purdue 76 73 Butler Incorrect
R1W3 Texas Tech R1W6 Florida 0.0 -0.7 No choice 69 66 Texas Tech NA
R1W4 Marshall R1W5 West Virginia -11.0 -4.6 Marshall 71 94 West Virginia Incorrect
R1X1 Kansas R1X8 Seton Hall 4.5 2.2 Seton Hall 83 79 Seton Hall Correct
R1X2 Duke R1X7 Rhode Island 10.0 3.8 Rhode Island 87 62 Duke Incorrect
R1X3 Michigan St R1X6 Syracuse 9.0 5.2 Syracuse 53 55 Syracuse Correct
R1X4 Auburn R1X5 Clemson 1.0 -0.6 No choice 53 84 Clemson NA
R1Y1 UMBC R1Y8 Kansas St -12.0 -3.5 UMBC 43 50 UMBC Correct
R1Y2 Cincinnati R1Y7 Nevada 7.0 9.1 Cincinnati 73 75 Nevada Incorrect
R1Y3 Tennessee R1Y6 Loyola-Chicago 6.0 5.8 No choice 62 63 Loyola-Chicago NA
R1Y4 Buffalo R1Y5 Kentucky -7.5 2.2 Buffalo 75 95 Kentucky Incorrect
R1Z1 Xavier R1Z8 Florida St 4.0 -0.2 Florida St 70 75 Florida St Correct
R1Z2 North Carolina R1Z7 Texas A&M 6.0 3.3 Texas A&M 65 86 Texas A&M Correct
R1Z3 Michigan R1Z6 Houston 3.0 3.5 No choice 64 63 Houston NA
R1Z4 Gonzaga R1Z5 Ohio St 2.5 10.6 Gonzaga 90 84 Gonzaga Correct
# Summary of results
table(output_table_2$Accuracy)
## 
##   Correct Incorrect      Push 
##        18        14         1

In round 2, the model was 6-6 ATS. In total, the model is 18-14-1 through the first two rounds of the tournament for an overall success rate of 56.25%.

Sweet 16

Model Predictions

# Recording Round 2 Winners into Round 3
bracket <- advance_winners(round2)
# Model Predictions for Round 3
round3 <- predict_ncaa_round(3)
# All Round 3 Spreads as of 7:00pm Monday
# Spreads are positive if strong seed is favored, negative if underdogs
round3spreads <- data.frame(
    matrix(
        c(
"Villanova", 5.5,
"Purdue", 1.5,
"Kansas", 4.5,
"Duke", 11,
"Kansas St", -6,
"Nevada", 1.5,
"Florida St", -5.5,
"Texas A&M", -2.5), 
    ncol = 2, byrow = TRUE), stringsAsFactors = FALSE
)

names(round3spreads) <- c("Team", "Spread")
round3spreads$Spread <- as.numeric(round3spreads$Spread)

# Append spreads on to bracket
round3 <- round3 %>% 
    left_join(round3spreads, by = c('TeamName.x' = 'Team')) %>% 
    mutate(Diff = Prediction - Spread)

Here are the model predictions for all round 3 games.

# Determine who model would bet on ATS
round3 <- ATSchoice(round3)
# Output of model recommendations
round3_predictions <- round3 %>% 
    select(StrongSeed, WeakSeed, TeamName.x, TeamName.y, Spread, Prediction, Diff, Win_Prob, ModelChoice)

kable(round3_predictions)
StrongSeed WeakSeed TeamName.x TeamName.y Spread Prediction Diff Win_Prob ModelChoice
R2W1 R2W4 Villanova West Virginia 5.5 2.9 -2.6 0.583 West Virginia
R2W2 R2W3 Purdue Texas Tech 1.5 0.6 -0.9 0.518 No choice
R2X1 R2X4 Kansas Clemson 4.5 8.2 3.7 0.735 Kansas
R2X2 R2X3 Duke Syracuse 11.0 4.3 -6.7 0.625 Syracuse
R2Y1 R2Y4 Kansas St Kentucky -6.0 -2.3 3.7 0.435 Kansas St
R2Y2 R2Y3 Nevada Loyola-Chicago 1.5 -0.8 -2.3 0.477 Loyola-Chicago
R2Z1 R2Z4 Florida St Gonzaga -5.5 -5.9 -0.4 0.330 No choice
R2Z2 R2Z3 Texas A&M Michigan -2.5 -2.9 -0.4 0.416 No choice

The model recommendations for round 3 are not as strong as in previous rounds. The recommendations in order of strength are:

  • Syracuse +11
  • Kansas -4.5
  • Kansas St +6

Fringe picks:

  • West Virginia +5.5
  • Loyola-Chicago +1.5

Game Results and Model Performance

To be updated after games are played.

# Manual input of round 3 results
# Need to automate this!
round3results <- data.frame(
    matrix(
        c(
        "Villanova", 90, 78,
        "Purdue", 65, 78,
        "Kansas", 80, 76,
        "Duke", 69, 65,
        "Kansas St", 61, 58,
        "Nevada", 68, 69,
        "Florida St", 75, 60,
        "Texas A&M", 72, 99),  
    ncol = 3, byrow = TRUE), stringsAsFactors = FALSE
)
names(round3results) <- c("Team", "Team.x.score", "Team.y.score")
round3results$Team.x.score <- as.numeric(round3results$Team.x.score)
round3results$Team.y.score <- as.numeric(round3results$Team.y.score)

# Append Game Results to Bracket
round3 <- round3 %>% 
    left_join(round3results, by = c('TeamName.x' = 'Team'))
# Determine Model's Accuracy ATS
round3 <- ATSresults(round3)
# Table of results
round3_results_table <- round3 %>% 
    select(StrongSeed, TeamName.x, WeakSeed, TeamName.y, Spread, Prediction, ModelChoice, Team.x.score, Team.y.score, ATSWinner, Accuracy)

round3_results_table <- rbind(output_table_2, round3_results_table)

kable(round3_results_table)
StrongSeed TeamName.x WeakSeed TeamName.y Spread Prediction ModelChoice Team.x.score Team.y.score ATSWinner Accuracy
W11a St Bonaventure W11b UCLA -3.5 2.6 St Bonaventure 65 58 St Bonaventure Correct
W16a Long Island W16b Radford -4.5 0.9 Long Island 61 71 Radford Incorrect
X11a Arizona St X11b Syracuse 1.0 1.3 No choice 56 60 Syracuse NA
Z16a NC Central Z16b TX Southern -4.5 9.3 NC Central 46 64 TX Southern Incorrect
W01 Villanova W16b Radford 23.0 9.5 No choice 87 61 Villanova NA
W02 Purdue W15 CS Fullerton 20.5 13.4 No choice 74 48 Purdue NA
W03 Texas Tech W14 SF Austin 11.0 11.4 No choice 70 60 SF Austin NA
W04 Wichita St W13 Marshall 11.5 4.3 Marshall 75 81 Marshall Correct
W05 West Virginia W12 Murray St 10.5 1.1 Murray St 85 68 West Virginia Incorrect
W06 Florida W11a St Bonaventure 5.5 8.0 Florida 77 62 Florida Correct
W07 Arkansas W10 Butler -1.5 -1.4 No choice 62 79 Butler NA
W08 Virginia Tech W09 Alabama 2.5 9.2 Virginia Tech 83 86 Alabama Incorrect
X01 Kansas X16 Penn 14.5 6.8 No choice 76 60 Kansas NA
X02 Duke X15 Iona 20.0 15.0 No choice 89 67 Duke NA
X03 Michigan St X14 Bucknell 14.5 7.8 Bucknell 82 78 Bucknell Correct
X04 Auburn X13 Col Charleston 9.0 -0.1 Col Charleston 62 58 Col Charleston Correct
X05 Clemson X12 New Mexico St 4.5 -0.3 New Mexico St 79 68 Clemson Incorrect
X06 TCU X11b Syracuse 4.0 1.0 Syracuse 52 57 Syracuse Correct
X07 Rhode Island X10 Oklahoma 1.5 4.7 Rhode Island 83 78 Rhode Island Correct
X08 Seton Hall X09 NC State 3.0 1.1 No choice 94 83 Seton Hall NA
Y01 Virginia Y16 UMBC 21.0 3.5 No choice 54 74 UMBC NA
Y02 Cincinnati Y15 Georgia St 14.0 7.1 No choice 68 53 Cincinnati NA
Y03 Tennessee Y14 Wright St 11.5 5.4 Wright St 73 47 Tennessee Incorrect
Y04 Arizona Y13 Buffalo 8.5 -7.1 Buffalo 68 89 Buffalo Correct
Y05 Kentucky Y12 Davidson 5.0 1.7 Davidson 78 73 Push Push
Y06 Miami FL Y11 Loyola-Chicago 2.0 6.7 Miami FL 62 64 Loyola-Chicago Incorrect
Y07 Nevada Y10 Texas 0.0 6.2 Nevada 87 83 Nevada Correct
Y08 Creighton Y09 Kansas St 1.0 -1.1 Kansas St 59 69 Kansas St Correct
Z01 Xavier Z16b TX Southern 19.5 15.6 No choice 91 72 TX Southern NA
Z02 North Carolina Z15 Lipscomb 19.5 13.1 No choice 84 66 Lipscomb NA
Z03 Michigan Z14 Montana 10.0 5.2 Montana 61 47 Michigan Incorrect
Z04 Gonzaga Z13 UNC Greensboro 13.5 7.8 UNC Greensboro 68 64 UNC Greensboro Correct
Z05 Ohio St Z12 S Dakota St 8.0 8.9 No choice 81 73 Push NA
Z06 Houston Z11 San Diego St 4.0 5.1 No choice 67 65 San Diego St NA
Z07 Texas A&M Z10 Providence 2.5 2.3 No choice 73 69 Texas A&M NA
Z08 Missouri Z09 Florida St -1.5 -11.0 Florida St 54 67 Florida St Correct
R1W1 Villanova R1W8 Alabama 11.0 8.3 Alabama 81 58 Villanova Incorrect
R1W2 Purdue R1W7 Butler 3.5 8.1 Purdue 76 73 Butler Incorrect
R1W3 Texas Tech R1W6 Florida 0.0 -0.7 No choice 69 66 Texas Tech NA
R1W4 Marshall R1W5 West Virginia -11.0 -4.6 Marshall 71 94 West Virginia Incorrect
R1X1 Kansas R1X8 Seton Hall 4.5 2.2 Seton Hall 83 79 Seton Hall Correct
R1X2 Duke R1X7 Rhode Island 10.0 3.8 Rhode Island 87 62 Duke Incorrect
R1X3 Michigan St R1X6 Syracuse 9.0 5.2 Syracuse 53 55 Syracuse Correct
R1X4 Auburn R1X5 Clemson 1.0 -0.6 No choice 53 84 Clemson NA
R1Y1 UMBC R1Y8 Kansas St -12.0 -3.5 UMBC 43 50 UMBC Correct
R1Y2 Cincinnati R1Y7 Nevada 7.0 9.1 Cincinnati 73 75 Nevada Incorrect
R1Y3 Tennessee R1Y6 Loyola-Chicago 6.0 5.8 No choice 62 63 Loyola-Chicago NA
R1Y4 Buffalo R1Y5 Kentucky -7.5 2.2 Buffalo 75 95 Kentucky Incorrect
R1Z1 Xavier R1Z8 Florida St 4.0 -0.2 Florida St 70 75 Florida St Correct
R1Z2 North Carolina R1Z7 Texas A&M 6.0 3.3 Texas A&M 65 86 Texas A&M Correct
R1Z3 Michigan R1Z6 Houston 3.0 3.5 No choice 64 63 Houston NA
R1Z4 Gonzaga R1Z5 Ohio St 2.5 10.6 Gonzaga 90 84 Gonzaga Correct
R2W1 Villanova R2W4 West Virginia 5.5 2.9 West Virginia 90 78 Villanova Incorrect
R2W2 Purdue R2W3 Texas Tech 1.5 0.6 No choice 65 78 Texas Tech NA
R2X1 Kansas R2X4 Clemson 4.5 8.2 Kansas 80 76 Clemson Incorrect
R2X2 Duke R2X3 Syracuse 11.0 4.3 Syracuse 69 65 Syracuse Correct
R2Y1 Kansas St R2Y4 Kentucky -6.0 -2.3 Kansas St 61 58 Kansas St Correct
R2Y2 Nevada R2Y3 Loyola-Chicago 1.5 -0.8 Loyola-Chicago 68 69 Loyola-Chicago Correct
R2Z1 Florida St R2Z4 Gonzaga -5.5 -5.9 No choice 75 60 Florida St NA
R2Z2 Texas A&M R2Z3 Michigan -2.5 -2.9 No choice 72 99 Michigan NA
# Summary of results
table(round3_results_table$Accuracy)
## 
##   Correct Incorrect      Push 
##        21        16         1

The model went 3-2 ATS in the Sweet 16 and is now 21-16-1 throughout the tourney.

Elite 8

Model Predictions

# Recording Round 3 Winners into Round 4
bracket <- advance_winners(round3)
# Model Predictions for Round 4
round4 <- predict_ncaa_round(4)
# All Round 4 Spreads as of 7:00pm Monday
# Spreads are positive if strong seed is favored, negative if underdogs
round4spreads <- data.frame(
    matrix(
        c(
"Villanova", 7,
"Kansas", -3.5,
"Kansas St", 1.5,
"Florida St", -4.5), 
    ncol = 2, byrow = TRUE), stringsAsFactors = FALSE
)

names(round4spreads) <- c("Team", "Spread")
round4spreads$Spread <- as.numeric(round4spreads$Spread)

# Append spreads on to bracket
round4 <- round4 %>% 
    left_join(round4spreads, by = c('TeamName.x' = 'Team')) %>% 
    mutate(Diff = Prediction - Spread)

Here are the model predictions for all round 4 games.

# Determine who model would bet on ATS
round4 <- ATSchoice(round4)
# Output of model recommendations
round4_predictions <- round4 %>% 
    select(StrongSeed, WeakSeed, TeamName.x, TeamName.y, Spread, Prediction, Diff, Win_Prob, ModelChoice)

kable(round4_predictions)
StrongSeed WeakSeed TeamName.x TeamName.y Spread Prediction Diff Win_Prob ModelChoice
R3W1 R3W2 Villanova Texas Tech 7.0 -5.1 -12.1 0.353 Texas Tech
R3X1 R3X2 Kansas Duke -3.5 -3.4 0.1 0.402 No choice
R3Y1 R3Y2 Kansas St Loyola-Chicago 1.5 -1.9 -3.4 0.445 Loyola-Chicago
R3Z1 R3Z2 Florida St Michigan -4.5 -1.7 2.8 0.453 Florida St

The model recommendations for round 4 are …

Game Results and Model Performance

To be updated after games are played.

# Manual input of round 4 results
# Need to automate this!
round4results <- data.frame(
    matrix(
        c(
        "Villanova", 71, 59,
        "Kansas", 85, 81,
        "Kansas St", 62, 78,
        "Florida St", 54, 58),  
    ncol = 3, byrow = TRUE), stringsAsFactors = FALSE
)
names(round4results) <- c("Team", "Team.x.score", "Team.y.score")
round4results$Team.x.score <- as.numeric(round4results$Team.x.score)
round4results$Team.y.score <- as.numeric(round4results$Team.y.score)

# Append Game Results to Bracket
round4 <- round4 %>% 
    left_join(round4results, by = c('TeamName.x' = 'Team'))
# Determine Model's Accuracy ATS
round4 <- ATSresults(round4)
# Table of results
round4_results_table <- round4 %>% 
    select(StrongSeed, TeamName.x, WeakSeed, TeamName.y, Spread, Prediction, ModelChoice, Team.x.score, Team.y.score, ATSWinner, Accuracy)

round4_results_table <- rbind(round3_results_table, round4_results_table)

kable(round4_results_table)
StrongSeed TeamName.x WeakSeed TeamName.y Spread Prediction ModelChoice Team.x.score Team.y.score ATSWinner Accuracy
W11a St Bonaventure W11b UCLA -3.5 2.6 St Bonaventure 65 58 St Bonaventure Correct
W16a Long Island W16b Radford -4.5 0.9 Long Island 61 71 Radford Incorrect
X11a Arizona St X11b Syracuse 1.0 1.3 No choice 56 60 Syracuse NA
Z16a NC Central Z16b TX Southern -4.5 9.3 NC Central 46 64 TX Southern Incorrect
W01 Villanova W16b Radford 23.0 9.5 No choice 87 61 Villanova NA
W02 Purdue W15 CS Fullerton 20.5 13.4 No choice 74 48 Purdue NA
W03 Texas Tech W14 SF Austin 11.0 11.4 No choice 70 60 SF Austin NA
W04 Wichita St W13 Marshall 11.5 4.3 Marshall 75 81 Marshall Correct
W05 West Virginia W12 Murray St 10.5 1.1 Murray St 85 68 West Virginia Incorrect
W06 Florida W11a St Bonaventure 5.5 8.0 Florida 77 62 Florida Correct
W07 Arkansas W10 Butler -1.5 -1.4 No choice 62 79 Butler NA
W08 Virginia Tech W09 Alabama 2.5 9.2 Virginia Tech 83 86 Alabama Incorrect
X01 Kansas X16 Penn 14.5 6.8 No choice 76 60 Kansas NA
X02 Duke X15 Iona 20.0 15.0 No choice 89 67 Duke NA
X03 Michigan St X14 Bucknell 14.5 7.8 Bucknell 82 78 Bucknell Correct
X04 Auburn X13 Col Charleston 9.0 -0.1 Col Charleston 62 58 Col Charleston Correct
X05 Clemson X12 New Mexico St 4.5 -0.3 New Mexico St 79 68 Clemson Incorrect
X06 TCU X11b Syracuse 4.0 1.0 Syracuse 52 57 Syracuse Correct
X07 Rhode Island X10 Oklahoma 1.5 4.7 Rhode Island 83 78 Rhode Island Correct
X08 Seton Hall X09 NC State 3.0 1.1 No choice 94 83 Seton Hall NA
Y01 Virginia Y16 UMBC 21.0 3.5 No choice 54 74 UMBC NA
Y02 Cincinnati Y15 Georgia St 14.0 7.1 No choice 68 53 Cincinnati NA
Y03 Tennessee Y14 Wright St 11.5 5.4 Wright St 73 47 Tennessee Incorrect
Y04 Arizona Y13 Buffalo 8.5 -7.1 Buffalo 68 89 Buffalo Correct
Y05 Kentucky Y12 Davidson 5.0 1.7 Davidson 78 73 Push Push
Y06 Miami FL Y11 Loyola-Chicago 2.0 6.7 Miami FL 62 64 Loyola-Chicago Incorrect
Y07 Nevada Y10 Texas 0.0 6.2 Nevada 87 83 Nevada Correct
Y08 Creighton Y09 Kansas St 1.0 -1.1 Kansas St 59 69 Kansas St Correct
Z01 Xavier Z16b TX Southern 19.5 15.6 No choice 91 72 TX Southern NA
Z02 North Carolina Z15 Lipscomb 19.5 13.1 No choice 84 66 Lipscomb NA
Z03 Michigan Z14 Montana 10.0 5.2 Montana 61 47 Michigan Incorrect
Z04 Gonzaga Z13 UNC Greensboro 13.5 7.8 UNC Greensboro 68 64 UNC Greensboro Correct
Z05 Ohio St Z12 S Dakota St 8.0 8.9 No choice 81 73 Push NA
Z06 Houston Z11 San Diego St 4.0 5.1 No choice 67 65 San Diego St NA
Z07 Texas A&M Z10 Providence 2.5 2.3 No choice 73 69 Texas A&M NA
Z08 Missouri Z09 Florida St -1.5 -11.0 Florida St 54 67 Florida St Correct
R1W1 Villanova R1W8 Alabama 11.0 8.3 Alabama 81 58 Villanova Incorrect
R1W2 Purdue R1W7 Butler 3.5 8.1 Purdue 76 73 Butler Incorrect
R1W3 Texas Tech R1W6 Florida 0.0 -0.7 No choice 69 66 Texas Tech NA
R1W4 Marshall R1W5 West Virginia -11.0 -4.6 Marshall 71 94 West Virginia Incorrect
R1X1 Kansas R1X8 Seton Hall 4.5 2.2 Seton Hall 83 79 Seton Hall Correct
R1X2 Duke R1X7 Rhode Island 10.0 3.8 Rhode Island 87 62 Duke Incorrect
R1X3 Michigan St R1X6 Syracuse 9.0 5.2 Syracuse 53 55 Syracuse Correct
R1X4 Auburn R1X5 Clemson 1.0 -0.6 No choice 53 84 Clemson NA
R1Y1 UMBC R1Y8 Kansas St -12.0 -3.5 UMBC 43 50 UMBC Correct
R1Y2 Cincinnati R1Y7 Nevada 7.0 9.1 Cincinnati 73 75 Nevada Incorrect
R1Y3 Tennessee R1Y6 Loyola-Chicago 6.0 5.8 No choice 62 63 Loyola-Chicago NA
R1Y4 Buffalo R1Y5 Kentucky -7.5 2.2 Buffalo 75 95 Kentucky Incorrect
R1Z1 Xavier R1Z8 Florida St 4.0 -0.2 Florida St 70 75 Florida St Correct
R1Z2 North Carolina R1Z7 Texas A&M 6.0 3.3 Texas A&M 65 86 Texas A&M Correct
R1Z3 Michigan R1Z6 Houston 3.0 3.5 No choice 64 63 Houston NA
R1Z4 Gonzaga R1Z5 Ohio St 2.5 10.6 Gonzaga 90 84 Gonzaga Correct
R2W1 Villanova R2W4 West Virginia 5.5 2.9 West Virginia 90 78 Villanova Incorrect
R2W2 Purdue R2W3 Texas Tech 1.5 0.6 No choice 65 78 Texas Tech NA
R2X1 Kansas R2X4 Clemson 4.5 8.2 Kansas 80 76 Clemson Incorrect
R2X2 Duke R2X3 Syracuse 11.0 4.3 Syracuse 69 65 Syracuse Correct
R2Y1 Kansas St R2Y4 Kentucky -6.0 -2.3 Kansas St 61 58 Kansas St Correct
R2Y2 Nevada R2Y3 Loyola-Chicago 1.5 -0.8 Loyola-Chicago 68 69 Loyola-Chicago Correct
R2Z1 Florida St R2Z4 Gonzaga -5.5 -5.9 No choice 75 60 Florida St NA
R2Z2 Texas A&M R2Z3 Michigan -2.5 -2.9 No choice 72 99 Michigan NA
R3W1 Villanova R3W2 Texas Tech 7.0 -5.1 Texas Tech 71 59 Villanova Incorrect
R3X1 Kansas R3X2 Duke -3.5 -3.4 No choice 85 81 Kansas NA
R3Y1 Kansas St R3Y2 Loyola-Chicago 1.5 -1.9 Loyola-Chicago 62 78 Loyola-Chicago Correct
R3Z1 Florida St R3Z2 Michigan -4.5 -1.7 Florida St 54 58 Florida St Correct
# Summary of results
table(round4_results_table$Accuracy)
## 
##   Correct Incorrect      Push 
##        23        17         1

The model went 2-1 ATS in the Elite 8 and is now 23-17-1 through the entire tourney for a 57.5% success rate. Considering the sample size of 40 games, a 95% confidence interval for the true model’s accuracy is between 42.2% and 72.8%.

Final 4

Model Predictions

# Recording Round 5 Winners into Round 5
bracket <- advance_winners(round4)
# Model Predictions for Round 5
round5 <- predict_ncaa_round(5)
# All Round 5 Spreads as of 8:30pm Thursday
# Spreads are positive if strong seed is favored, negative if underdogs
round5spreads <- data.frame(
    matrix(
        c(
"Villanova", 5,
"Loyola-Chicago", -5.5), 
    ncol = 2, byrow = TRUE), stringsAsFactors = FALSE
)

names(round5spreads) <- c("Team", "Spread")
round5spreads$Spread <- as.numeric(round5spreads$Spread)

# Append spreads on to bracket
round5 <- round5 %>% 
    left_join(round5spreads, by = c('TeamName.x' = 'Team')) %>% 
    mutate(Diff = Prediction - Spread)

Here are the model predictions for all round 4 games.

# Determine who model would bet on ATS
round5 <- ATSchoice(round5)
# Output of model recommendations
round5_predictions <- round5 %>% 
    select(StrongSeed, WeakSeed, TeamName.x, TeamName.y, Spread, Prediction, Diff, Win_Prob, ModelChoice)

kable(round5_predictions)
StrongSeed WeakSeed TeamName.x TeamName.y Spread Prediction Diff Win_Prob ModelChoice
R4W1 R4X1 Villanova Kansas 5.0 1.9 -3.1 0.554 Kansas
R4Y1 R4Z1 Loyola-Chicago Michigan -5.5 -6.8 -1.3 0.305 No choice

The only model recommendations for the Final Four is Kansas.

Game Results and Model Performance

To be updated after games are played.