# league stuff: tables, standings, stats, etc.
#' Bulid Stats Table
#'
#' @param scores scores to use to build stats table
#'
#' @return a stats table (as tibble)
#' @export
buildStats <- function(scores) {
scores <- droplevels(scores)
teamlist <- sort(unique(c(as.character(scores$HomeTeam), as.character(scores$AwayTeam))))
# remainderSeasonDC(nsims=10, cores=1, scores=scor, schedule = sched, regress = TRUE) testing passes results instead of home & Away goals
if ("HomeGoals" %in% colnames(scores)) {
tmp1 <- scores %>%
dplyr::group_by(.data$HomeTeam) %>%
dplyr::summarise(
GP = dplyr::n(),
W = sum(.data$HomeGoals > .data$AwayGoals & .data$OTStatus == ""),
OTW = sum(.data$HomeGoals > .data$AwayGoals & .data$OTStatus == "OT"),
SOW = sum(.data$HomeGoals > .data$AwayGoals & .data$OTStatus == "SO"),
OTL = sum(.data$HomeGoals < .data$AwayGoals & .data$OTStatus == "OT"),
SOL = sum(.data$HomeGoals < .data$AwayGoals & .data$OTStatus == "SO"),
L = sum(.data$HomeGoals < .data$AwayGoals & .data$OTStatus == ""),
P = as.numeric(.data$W * 2 + .data$OTW * 2 + .data$SOW * 2 + .data$OTL + .data$SOL)
) %>%
dplyr::ungroup()
tmp2 <- scores %>%
dplyr::group_by(.data$AwayTeam) %>%
dplyr::summarise(
GP = dplyr::n(),
W = sum(.data$AwayGoals > .data$HomeGoals & .data$OTStatus == ""),
OTW = sum(.data$AwayGoals > .data$HomeGoals & .data$OTStatus == "OT"),
SOW = sum(.data$AwayGoals > .data$HomeGoals & .data$OTStatus == "SO"),
OTL = sum(.data$AwayGoals < .data$HomeGoals & .data$OTStatus == "OT"),
SOL = sum(.data$AwayGoals < .data$HomeGoals & .data$OTStatus == "SO"),
L = sum(.data$AwayGoals < .data$HomeGoals & .data$OTStatus == ""),
P = as.numeric(.data$W * 2 + .data$OTW * 2 + .data$SOW * 2 + .data$OTL + .data$SOL)
) %>%
dplyr::ungroup()
} else if ("Result" %in% colnames(scores)) {
tmp1 <- scores %>%
dplyr::group_by(.data$HomeTeam) %>%
dplyr::summarise(
GP = dplyr::n(),
W = sum(.data$Result == 1),
OTW = sum(.data$Result == 0.75),
SOW = sum(.data$Result == 0.60),
OTL = sum(.data$Result == 0.40),
SOL = sum(.data$Result == 0.25),
L = sum(.data$Result == 0),
P = as.numeric(.data$W * 2 + .data$OTW * 2 + .data$SOW * 2 + .data$OTL + .data$SOL)
) %>%
dplyr::ungroup()
tmp2 <- scores %>%
dplyr::group_by(.data$AwayTeam) %>%
dplyr::summarise(
GP = dplyr::n(),
W = sum(.data$Result == 0),
OTW = sum(.data$Result == 0.25),
SOW = sum(.data$Result == 0.40),
OTL = sum(.data$Result == 0.60),
SOL = sum(.data$Result == 0.75),
L = sum(.data$Result == 1),
P = as.numeric(.data$W * 2 + .data$OTW * 2 + .data$SOW * 2 + .data$OTL + .data$SOL)
) %>%
dplyr::ungroup()
} else {
stop("Scores must contain home & away goal info or result info.")
}
team_stats <- data.frame(
Team = teamlist,
GP = tmp1$GP + tmp2$GP,
Points = tmp1$P + tmp2$P,
W = tmp1$W + tmp2$W,
L = tmp1$L + tmp2$L,
OTL = tmp1$OTL + tmp2$OTL,
OTW = tmp1$OTW + tmp2$OTW,
SOL = tmp1$SOL + tmp2$SOL,
SOW = tmp1$SOW + tmp2$SOW,
stringsAsFactors = FALSE
)
team_stats <- team_stats %>%
dtplyr::lazy_dt() %>%
dplyr::mutate(
PointPercent = .data$Points / .data$GP,
ROW = .data$W + .data$OTW,
ROSW = .data$W + .data$OTW + .data$SOW,
Rand = stats::runif(dplyr::n())
) %>%
dplyr::mutate(
Rank = order(order(-.data$Points, -.data$PointPercent, -.data$W, -.data$ROW, -.data$ROSW, .data$Rand)), # include Random for random ties sorting, else Anaheim will always beat Vegas if they're tied.
Conf = getTeamConferences(.data$Team), # convenience data, dropped later
Div = getTeamDivisions(.data$Team)
) %>%
dplyr::group_by(.data$Conf) %>%
dplyr::mutate(ConfRank = rank(.data$Rank, ties.method = "random")) %>%
dplyr::ungroup() %>%
dplyr::group_by(.data$Div) %>%
dplyr::mutate(DivRank = rank(.data$Rank, ties.method = "random")) %>%
dplyr::ungroup() %>%
dplyr::mutate(Playoffs = ifelse(.data$DivRank <= 3, 1, 0)) %>%
dplyr::group_by(.data$Conf, .data$Playoffs) %>%
dplyr::mutate(Playoffs = ifelse(.data$Rank %in% utils::tail(sort(.data$Rank), 2), 1, .data$Playoffs)) %>% ## Renaming top two playoff teams as 'in' doesn't matter, because they're in already
dplyr::ungroup() %>%
dplyr::select(-c("Conf", "Div", "PointPercent", "ROW", "ROSW", "Rand")) %>%
tibble::as_tibble()
return(team_stats)
}
#' Today's Odds
#'
#' @description Determine today's games' odds (if today has games), or a specified date's odds
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#' @param today The date for which you want game odds
#' @param schedule The schedule, default to internal schedule
#' @param expected_mean the mean lambda & mu, used only for regression
#' @param season_percent the percent complete of the season, used for regression
#' @param include_xG Whether to include xG values in reported odds
#'
#' @return a data frame of HomeTeam, AwayTeam, HomeWin, AwayWin, Draw, or NULL if no games today
#' @export
#'
#' @examples todayOdds(today = as.Date("2019-11-01"))
todayOdds <- function(params = NULL, today = Sys.Date(), schedule = HockeyModel::schedule, expected_mean = NULL, season_percent = NULL, include_xG = FALSE) {
return(todayDC(params = params, today = today, schedule = schedule, expected_mean = expected_mean, season_percent = season_percent, include_xG = include_xG))
}
#' Simulate the remainder of the season
#'
#' @param scores Past (historical) season scores. Defaults to HockeyModel::Scores
#' @param schedule Future unplayed games. Defaults to HockeyModel::schedule
#' @param nsims number of simulations to run
#' @param cores number of cores to use in parallel.
#' @param progress whether to show a progress bar.
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return a data frame of results
#' @export
simulateSeasonParallel <- function(scores = HockeyModel::scores, nsims = 10000, schedule = HockeyModel::schedule, cores = NULL, progress = FALSE, params = NULL) {
teamlist <- c()
if (!is.null(scores)) {
season_sofar <- scores[scores$Date > as.Date(getSeasonStartDate()), ]
season_sofar <- season_sofar[, c("Date", "HomeTeam", "AwayTeam", "Result")]
} else {
season_sofar <- NULL
}
teamlist <- c(teamlist, sort(unique(c(as.character(schedule$Home), as.character(schedule$Away)))))
cores <- parseCores(cores)
odds_table <- remainderSeasonDC(scores = scores, schedule = schedule, params = params, odds = T)
odds_table$HOT <- extraTimeSolver(odds_table$HomeWin, odds_table$AwayWin, odds_table$Draw)[, 2]
odds_table$AOT <- extraTimeSolver(odds_table$HomeWin, odds_table$AwayWin, odds_table$Draw)[, 3]
if (cores > 1 & requireNamespace("parallel", quietly = TRUE)) {
`%dopar%` <- foreach::`%dopar%` # This hack passes R CMD CHK
cl <- parallel::makeCluster(cores)
doSNOW::registerDoSNOW(cl)
if (progress) {
pb <- utils::txtProgressBar(max = nsims, style = 3)
progress <- function(n) utils::setTxtProgressBar(pb, n)
opts <- list(progress = progress)
} else {
opts <- list()
}
all_results <- foreach::foreach(i = 1:nsims, .combine = "rbind", .options.snow = opts, .packages = c("HockeyModel")) %dopar% {
# Generate Games results once
tmp <- odds_table
tmp$res1 <- stats::runif(n = nrow(tmp))
tmp$res2 <- stats::runif(n = nrow(tmp))
tmp$Result <- 1 * (as.numeric(tmp$res1 < tmp$HomeWin)) +
0.75 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT)) * as.numeric(tmp$res2 < 0.6858606)) +
0.6 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT)) * as.numeric(tmp$res2 > 0.6858606)) +
0.4 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT + tmp$AOT)) * as.numeric(tmp$res2 > 0.6858606)) +
0.25 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT + tmp$AOT)) * as.numeric(tmp$res2 < 0.6858606)) +
0
tmp$HomeWin <- NULL
tmp$AwayWin <- NULL
tmp$HOT <- NULL
tmp$AOT <- NULL
tmp$Draw <- NULL
tmp$res1 <- NULL
tmp$res2 <- NULL
tmp <- rbind(season_sofar, tmp)
# Make the season table
table <- buildStats(tmp)
table$SimNo <- i
table
}
if (progress) {
close(pb)
}
parallel::stopCluster(cl)
gc(verbose = FALSE)
} else {
if (cores > 1 & !requireNamespace("parallel", quietly = TRUE)) {
message("Parallel processing is only available if the parallels package is installed.")
}
all_results <- list()
for (i in 1:nsims) {
tmp <- odds_table
tmp$HOT <- extraTimeSolver(tmp$HomeWin, tmp$AwayWin, tmp$Draw)[, 2]
tmp$AOT <- extraTimeSolver(tmp$HomeWin, tmp$AwayWin, tmp$Draw)[, 3]
tmp$res1 <- stats::runif(n = nrow(tmp))
tmp$res2 <- stats::runif(n = nrow(tmp))
tmp$Result <- 1 * (as.numeric(tmp$res1 < tmp$HomeWin)) +
0.75 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT)) * as.numeric(tmp$res2 < 0.6858606)) +
0.6 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT)) * as.numeric(tmp$res2 > 0.6858606)) +
0.4 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT + tmp$AOT)) * as.numeric(tmp$res2 > 0.6858606)) +
0.25 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT + tmp$AOT)) * as.numeric(tmp$res2 < 0.6858606)) +
0
tmp$HomeWin <- NULL
tmp$AwayWin <- NULL
tmp$HOT <- NULL
tmp$AOT <- NULL
tmp$Draw <- NULL
tmp$res1 <- NULL
tmp$res2 <- NULL
tmp <- rbind(season_sofar, tmp)
# Make the season table
table <- buildStats(tmp)
table$SimNo <- i
all_results[[i]] <- table
}
all_results <- dplyr::bind_rows(all_results)
}
summary_results <- all_results %>%
dtplyr::lazy_dt() %>%
dplyr::group_by(.data$Team) %>%
dplyr::summarise(
Playoffs = mean(.data$Playoffs),
meanPoints = mean(.data$Points, na.rm = TRUE),
maxPoints = max(.data$Points, na.rm = TRUE),
minPoints = min(.data$Points, na.rm = TRUE),
meanWins = mean(.data$W, na.rm = TRUE),
maxWins = max(.data$W, na.rm = TRUE),
Presidents = sum(.data$Rank == 1) / dplyr::n(),
meanRank = mean(.data$Rank, na.rm = TRUE),
bestRank = min(.data$Rank, na.rm = TRUE),
# meanConfRank = mean(.data$ConfRank, na.rm = TRUE),
# bestConfRank = min(.data$ConfRank, na.rm = TRUE),
meanDivRank = mean(.data$DivRank, na.rm = TRUE),
bestDivRank = min(.data$DivRank, na.rm = TRUE),
sdPoints = stats::sd(.data$Points, na.rm = TRUE),
sdWins = stats::sd(.data$W, na.rm = TRUE),
sdRank = stats::sd(.data$Rank, na.rm = TRUE),
# sdConfRank = stats::sd(.data$ConfRank, na.rm = TRUE),
sdDivRank = stats::sd(.data$DivRank, na.rm = TRUE)
) %>%
tibble::as_tibble()
return(list(summary_results = summary_results, raw_results = all_results))
}
#' Compile predictions to one object
#'
#' @description compiles predictions from a group of .RDS files to one data.frame
#' @param dir Directory holding the prediction .RDS files.
#'
#' @return a data frame.
#' @export
compile_predictions <- function(dir = getOption("HockeyModel.prediction.path")) {
# Find the files
filelist <- list.files(path = dir)
pdates <- substr(filelist, 1, 10) # gets the dates list of prediction
pdates <- pdates[pdates != "graphics"]
all_predictions <- lapply(pdates, function(f) readRDS(file.path(dir, (paste0(f, "-predictions.RDS"))))) # Read all the files
names(all_predictions) <- pdates
all_predictions <- dplyr::bind_rows(all_predictions, .id = "predictionDate")
return(all_predictions)
}
#' 'Loopless' simulation
#'
#' @param nsims number of simulations to run (approximate)
#' @param cores number of cores in parallel to process
#' @param schedule games to play
#' @param scores Season to this point
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#' @param season_sofar The results of the season to date
#' @param likelihood_graphic whether to create a likelihood graphic
#' @param odds_table a table of odds for all games in schedule. Null, unless provided. Should be similar to the output of `remainderSeasonDC(odds=TRUE)`,
#' which is a data.frame of HomeTeam, AwayTeam, HomeWin, AwayWin, Draw, GameID and Date
#'
#' @return a two member list, of all results and summary results
#' @export
loopless_sim <- function(nsims = 1e5, cores = NULL, schedule = HockeyModel::schedule, scores = HockeyModel::scores, params = NULL, season_sofar = NULL, likelihood_graphic = TRUE, odds_table = NULL) {
params <- parse_dc_params(params)
cores <- parseCores(cores)
nsims <- floor(nsims / cores)
schedule <- schedule[!(schedule$GameID %in% scores$GameID), ]
schedule <- add_postponed_to_schedule_end(schedule)
if (is.null(odds_table) | !(all(c("HomeTeam", "AwayTeam", "HomeWin", "AwayWin", "Draw", "GameID", "Date") %in% colnames(odds_table)))) {
odds_table <- remainderSeasonDC(scores = scores, schedule = schedule, params = params, nsims = nsims, odds = T)
}
season_sofar <- scores[scores$Date >= as.Date(getSeasonStartDate(getSeason(schedule[, "Date"][1]))), ]
if (nrow(season_sofar) > 0) {
season_sofar <- season_sofar[, c("Date", "HomeTeam", "AwayTeam", "Result", "GameID")]
odds_table <- odds_table[!(odds_table$GameID %in% season_sofar$GameID), ]
all_season <- dplyr::bind_rows(season_sofar, odds_table)
} else {
all_season <- odds_table
all_season$Result <- NA
}
oddsseason <- extraTimeSolver(all_season$HomeWin, all_season$AwayWin, 1 - (all_season$HomeWin + all_season$AwayWin))
all_season$HomeOT <- oddsseason[, 2] * 0.6858606
all_season$HomeSO <- oddsseason[, 2] * 0.3141394
all_season$AwaySO <- oddsseason[, 3] * 0.3141394
all_season$AwayOT <- oddsseason[, 3] * 0.6858606
rm(oddsseason, season_sofar, odds_table)
if (cores == 1 | !requireNamespace("parallel", quietly = TRUE)) {
if (cores > 1) {
message("Multi-core processing requires the parallel package.")
}
# for testing only, really.
all_results <- sim_engine(all_season = all_season, nsims = nsims, params = params)
} else {
# this fixes CRAN checks
`%dopar%` <- foreach::`%dopar%`
# `%do%` <- foreach::`%do%`
cl <- parallel::makeCluster(cores)
doSNOW::registerDoSNOW(cl)
# Ram management issues. Send smaller chunks more often, hopefully this helps.
all_results <- foreach::foreach(i = seq_along(1:(cores * 100)), .combine = "rbind", .packages = "HockeyModel") %dopar% {
all_results <- sim_engine(all_season = all_season, nsims = ceiling(nsims / 100), params = params)
return(all_results)
}
parallel::stopCluster(cl)
gc(verbose = FALSE)
}
summary_results <- all_results %>%
# dtplyr::lazy_dt() %>%
dplyr::group_by(.data$Team) %>%
dplyr::summarise(
Playoffs = mean(.data$Playoffs),
meanPoints = mean(.data$Points, na.rm = TRUE),
maxPoints = max(.data$Points, na.rm = TRUE),
minPoints = min(.data$Points, na.rm = TRUE),
meanWins = mean(.data$W, na.rm = TRUE),
maxWins = max(.data$W, na.rm = TRUE),
Presidents = sum(.data$Rank == 1) / dplyr::n(),
meanRank = mean(.data$Rank, na.rm = TRUE),
bestRank = min(.data$Rank, na.rm = TRUE),
meanConfRank = mean(.data$ConfRank, na.rm = TRUE),
bestConfRank = min(.data$ConfRank, na.rm = TRUE),
meanDivRank = mean(.data$DivRank, na.rm = TRUE),
bestDivRank = min(.data$DivRank, na.rm = TRUE),
sdPoints = stats::sd(.data$Points, na.rm = TRUE),
sdWins = stats::sd(.data$W, na.rm = TRUE),
sdRank = stats::sd(.data$Rank, na.rm = TRUE),
sdConfRank = stats::sd(.data$ConfRank, na.rm = TRUE),
sdDivRank = stats::sd(.data$DivRank, na.rm = TRUE),
p_rank1 = sum(.data$ConfRank == 1 & .data$DivRank == 1) / dplyr::n(),
p_rank2 = sum(.data$ConfRank != 1 & .data$DivRank == 1) / dplyr::n(),
# Solving 3 & 4 & 5 & 6 doesn't *really* matter, because 3/4 play the 5/6 within their own division.
# In 2nd round, 1/8 or 2/7 play the 3/6 or 4/5 from their own division. No re-seeding occurs.
# See: https://en.wikipedia.org/wiki/Stanley_Cup_playoffs#Current_format
p_rank_34 = sum(.data$DivRank == 2) / dplyr::n(),
p_rank_56 = sum(.data$DivRank == 3) / dplyr::n(),
p_rank7 = sum(.data$Wildcard == 1) / dplyr::n(),
p_rank8 = sum(.data$Wildcard == 2) / dplyr::n()
) %>%
tibble::as_tibble()
if (likelihood_graphic) {
plot_point_likelihood(preds = all_results)
}
return(list(summary_results = summary_results, raw_results = all_results))
}
#' Simulation engine to be parallelized or used in single core
#'
#' @param all_season One seasons' scores & odds schedule
#' @param nsims Number of simulations to run
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return results of `nsims` season simulations, as one long data frame score table.
#' @export
sim_engine <- function(all_season, nsims, params = NULL) {
params <- parse_dc_params(params)
season_length <- nrow(all_season)
# multi_season<-dplyr::bind_rows(replicate(nsims, all_season[,c('HomeTeam', 'AwayTeam', 'Result', 'GameID')], simplify = FALSE))
# multi_season$sim<-rep(1:nsims, each = season_length)
resultslist <- list()
# TODO: This can be vectorized or delooped by doing Result prediction on long_season?
for (g in all_season$GameID) {
if (is.na(all_season[all_season$GameID == g, ]$Result)) {
odds <- as.vector(all_season[all_season$GameID == g, c("HomeWin", "HomeOT", "HomeSO", "AwaySO", "AwayOT", "AwayWin")])
# multi_season[multi_season$GameID == g,]$Result <- sampleResult(odds[[1]], odds[[2]], odds[[3]], odds[[4]], odds[[5]], odds[[6]], size=nsims)
resultslist[[as.character(g)]] <- sampleResult(odds[[1]], odds[[2]], odds[[3]], odds[[4]], odds[[5]], odds[[6]], size = nsims)
} else {
resultslist[[as.character(g)]] <- rep(all_season[all_season$GameID == g, ]$Result, nsims)
}
}
long_season <- data.frame(
Team = c(rep(all_season$HomeTeam, each = nsims), rep(all_season$AwayTeam, each = nsims)),
SimNo = c(rep(1:nsims, season_length), rep(1:nsims, season_length)),
Result = c(unlist(resultslist), 1 - unlist(resultslist))
)
rm(resultslist)
all_results <- long_season %>%
dtplyr::lazy_dt() %>%
dplyr::group_by(.data$SimNo, .data$Team) %>%
dplyr::summarise(
W = sum(.data$Result == 1),
OTW = sum(.data$Result == 0.75),
SOW = sum(.data$Result == 0.6),
SOL = sum(.data$Result == 0.4),
OTL = sum(.data$Result == 0.25),
L = sum(.data$Result == 0)
) %>%
as.data.frame()
rm(long_season)
all_results$Points <- all_results$W * 2 + all_results$OTW * 2 + all_results$SOW * 2 + all_results$OTL + all_results$SOL
all_results$Conference <- unlist(getTeamConferences(all_results$Team))
all_results$Division <- getTeamDivisions(all_results$Team)
all_results$Wildcard <- 100
all_results <- all_results %>%
dtplyr::lazy_dt() %>%
dplyr::group_by(.data$SimNo) %>%
dplyr::mutate(Rank = rank(-.data$Points, ties.method = "random")) %>%
dplyr::ungroup() %>%
dplyr::group_by(.data$SimNo, .data$Conference) %>%
dplyr::mutate(ConfRank = rank(.data$Rank)) %>%
dplyr::ungroup() %>%
dplyr::group_by(.data$SimNo, .data$Division) %>%
dplyr::mutate(DivRank = rank(.data$ConfRank)) %>%
dplyr::ungroup() %>%
dplyr::mutate(Playoffs = ifelse(.data$DivRank <= 3, 1, 0)) %>%
dplyr::group_by(.data$SimNo, .data$Conference) %>%
dplyr::arrange(.data$Playoffs, .data$ConfRank) %>%
dplyr::mutate(Wildcard = ifelse(.data$Playoffs == 0, dplyr::row_number(), 100)) %>%
dplyr::ungroup() %>%
dplyr::arrange(.data$SimNo, .data$Team) %>%
dplyr::select(
.data$SimNo, .data$Team, .data$W, .data$OTW,
.data$SOW, .data$SOL, .data$OTL, .data$Points,
.data$Wildcard, .data$Rank, .data$ConfRank,
.data$DivRank, .data$Playoffs
) %>%
tibble::as_tibble()
all_results[!is.na(all_results$Wildcard) & all_results$Wildcard <= 2, ]$Playoffs <- 1
all_results$Wildcard[is.na(all_results$Wildcard)] <- 0
# all_results$Wildcard<-NULL
return(all_results)
}
#' Playoff Win Calculator
#'
#' @description wraps PlayoffSeriesOdds with odds generator for a given home and away team
#' @param home_team Home Ice Advantage Team
#' @param away_team Opponent Team
#' @param home_wins Home Ice Advantage Team Wins in Series
#' @param away_wins Opponent Team Wins in Series
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return Odds from 0-1 of home team winning. Away odds are 1 - return value
#' @export
playoffWin <- function(home_team, away_team, home_wins = 0, away_wins = 0, params = NULL) {
params <- parse_dc_params(params)
home_odds <- DCPredict(home = home_team, away = away_team, draws = FALSE, params = params)[1]
away_odds <- 1 - DCPredict(home = away_team, away = home_team, draws = FALSE, params = params)[1]
return(playoffSeriesOdds(home_odds = home_odds, away_odds = away_odds, home_win = home_wins, away_win = away_wins))
}
#' Random Series Winner
#'
#' @description generate a random series winner given a home and away team
#'
#' @param home_team Home Team name (required)
#' @param away_team Away Team name (Required)
#' @param home_wins Number of home wins (default 0)
#' @param away_wins Number of away team wins (default 0)
#' @param homeAwayOdds pre-calculated home & away team parings odds of a home win. Overrides playoffwin calculation
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return TRUE if the home team wins, else FALSE
#' @export
randomSeriesWinner <- function(home_team, away_team, home_wins = 0, away_wins = 0, homeAwayOdds = NULL, params = NULL) {
if (is.null(homeAwayOdds)) {
params <- parse_dc_params(params)
return(ifelse(stats::runif(1) < playoffWin(home_team = home_team, away_team = away_team, home_wins = home_wins, away_wins = away_wins, params = params),
home_team, away_team
))
} else {
hao <- homeAwayOdds[homeAwayOdds$HomeTeam == home_team & homeAwayOdds$AwayTeam == away_team, ]
if (nrow(hao) == 1) {
return(ifelse(stats::runif(1) < hao$HomeOdds, home_team, away_team))
} else {
# Calculated odds aren't in there, get it manually
params <- parse_dc_params(params)
return(ifelse(stats::runif(1) < playoffWin(home_team = home_team, away_team = away_team, home_wins = home_wins, away_wins = away_wins, params = params),
home_team, away_team
))
}
}
}
#' Statistical Playoff Series Odds Solver
#'
#' @description Given home and away win odds, produce the odds of the 'home advantage' team winning the series. From \url{http://www.stat.umn.edu/geyer/playoff.html}, modified to function with odds determination.
#' @references \url{http://www.stat.umn.edu/geyer/playoff.html}
#'
#' @param home_odds Team odds with home-ice advantage at home
#' @param away_odds Team odds with home-ice advantage at away (on the road)
#' @param home_win Number of home ice advantage team wins thus far in the series. Default to 0 (prediction before series start)
#' @param away_win Number of away team wins thus far in the series
#' @param ngames Number of games in the series, defaults to 7
#' @param game_home vector of T/F for 'home team' home games. Defaults for NHL best of 7 series: \code{c(T,T,F,F,T,F,T)}
#' @param predict_games_to_win (Defualt False) If TRUE, returns the table of ways the series could finish.
#'
#' @return numeric odds of home team win series (1-odds for away odds)
#' @export
playoffSeriesOdds <- function(home_odds, away_odds, home_win = 0, away_win = 0, ngames = NULL, game_home = NULL, predict_games_to_win = FALSE) {
if (is.null(ngames)) {
ngames <- 7
}
if (is.null(game_home) & ngames == 7) {
game_home <- c(TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE)
} else {
game_home <- rep(c(TRUE, FALSE), as.integer(ngames + 1 / 2))[1:ngames]
}
game_to <- ceiling(ngames / 2)
if (length(home_odds) > 1 | length(away_odds) > 1) {
stop("handle only one series at a time")
}
p1_home <- home_odds
p1_road <- away_odds
if (p1_home < 0 | p1_home > 1 | p1_road < 0 | p1_road > 1) {
stop("impossible odds")
}
home_win <- as.integer(home_win)
away_win <- as.integer(away_win)
if (home_win < 0 | away_win < 0) {
stop("negative number of wins impossible")
}
if (home_win >= game_to) {
message("series already won")
return(1)
}
if (away_win >= game_to) {
message("series already won")
return(0)
}
games_played <- home_win + away_win
if (games_played > ngames) {
stop("total wins greater than games impossible")
}
if (games_played == ngames) {
stop("nothing to do, series over")
}
x.g <- games_played
x.w1 <- home_win
x.w2 <- away_win
x.p <- 1.0
finished_series <- NULL
for (i in (games_played + 1):ngames) {
p1now <- ifelse(game_home[i], p1_home, p1_road)
l <- length(x.g)
y.w1 <- c(x.w1 + 1, x.w1[l])
y.w2 <- c(x.w2[1], x.w2 + 1)
y.g <- c(x.g + 1, x.g[1] + 1)
y.p <- c(x.p * p1now, 0)
y.p <- y.p + c(0, x.p * (1 - p1now))
unfinished_series <- y.w1 < game_to & y.w2 < game_to
if (any(!unfinished_series)) {
series <- cbind(y.g, y.w1, y.w2, y.p)
finished_series <- rbind(finished_series, series[!unfinished_series, ])
}
x.g <- y.g[unfinished_series]
x.w1 <- y.w1[unfinished_series]
x.w2 <- y.w2[unfinished_series]
x.p <- y.p[unfinished_series]
}
if (predict_games_to_win == FALSE) {
p1total <- sum(finished_series[finished_series[, 2] == game_to, 4])
return(p1total)
} else {
return(unfinished_series)
}
}
#' simulate Playoffs
#'
#' @description Solves playoff odds by MC simulation.
#'
#' @param summary_results summary results
#' @param nsims Number of playoff sims to run. Too many takes a long time.
#' @param cores Number of processor cores to use
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return a data frame of each teams' odds of winning each round (First Round, Second Round, Conference Finals and Stanley Cup)
#' @export
simulatePlayoffs <- function(summary_results = NULL, nsims = 1e5, cores = NULL, params = NULL) {
params <- parse_dc_params(params)
cores <- parseCores(cores)
# TODO use compile_predictions for this?
if (is.null(summary_results)) {
filelist <- list.files(path = getOption("HockeyModel.prediction.path"))
pdates <- substr(filelist, 1, 10) # gets the dates list of prediction
pdates <- pdates[pdates != "graphics"]
lastp <- as.Date(max(pdates))
summary_results <- readRDS(file.path(getOption("HockeyModel.prediction.path"), paste0(lastp, "-predictions.RDS")))
}
summary_results <- summary_results %>%
dplyr::mutate(
"Conf" = getTeamConferences(.data$Team),
"Div" = getTeamDivisions(.data$Team)
)
if ("p_rank3" %in% names(summary_results)) {
# Shortcut for having prank3, 4, 5, 6 instead of prank34, and prank56. add them
summary_results <- summary_results %>%
dplyr::mutate(
"p_rank_34" = .data$p_rank3 + .data$p_rank4,
"p_rank_56" = .data$p_rank5 + .data$p_rank6
)
}
east_results <- summary_results %>% dplyr::filter(.data$Conf == "Eastern")
west_results <- summary_results %>% dplyr::filter(.data$Conf == "Western")
homeAwayOdds <- getAllHomeAwayOdds(summary_results$Team, params = params)
simresults <- data.frame(
"SimNo" = integer(),
"l1" = character(),
"l2" = character(),
"l3" = character(),
"l4" = character(),
"l5" = character(),
"l6" = character(),
"l7" = character(),
"l8" = character(),
"series1" = character(),
"series2" = character(),
"series3" = character(),
"series4" = character(),
"series5" = character(),
"series6" = character(),
"series7" = character(),
"series8" = character(),
"series9" = character(),
"series10" = character(),
"series11" = character(),
"series12" = character(),
"series13" = character(),
"series14" = character(),
"series15" = character()
)
# currentSeries<-getAPISeries()
currentSeries <- data.frame(
"Round" = integer(), "Series" = integer(), "HomeTeam" = character(), "AwayTeam" = character(),
"HomeWins" = integer(), "AwayWins" = integer(), "HomeSeed" = integer(), "AwaySeed" = integer(),
"Statsu" = character(), "SeriesID" = integer()
)
if (nrow(currentSeries) == 0) {
message("too early to mix in real-life series")
completedSeries <- data.frame("Series" = character(), "Winner" = character(), "Loser" = character())
currentSeries <- data.frame(
"Round" = integer(), "Series" = integer(), "HomeTeam" = character(), "AwayTeam" = character(),
"HomeWins" = integer(), "AwayWins" = integer(), "HomeSeed" = integer(), "AwaySeed" = integer(),
"Statsu" = character(), "SeriesID" = integer()
)
} else {
completedSeries <- getCompletedSeries(currentSeries)
for (s in currentSeries[currentSeries$Status == "Ongoing", ]$SeriesID) {
homeAwayOdds[homeAwayOdds$HomeTeam == currentSeries[currentSeries$SeriesID == s, ]$HomeTeam &
homeAwayOdds$AwayTeam == currentSeries[currentSeries$SeriesID == s, ]$AwayTeam, ]$HomeOdds <-
playoffWin(
home_team = currentSeries[currentSeries$SeriesID == s, ]$HomeTeam,
away_team = currentSeries[currentSeries$SeriesID == s, ]$AwayTeam,
home_wins = currentSeries[currentSeries$SeriesID == s, ]$HomeWins,
away_wins = currentSeries[currentSeries$SeriesID == s, ]$AwayWins,
params = params
)
}
}
if (cores > 1) {
cl <- parallel::makeCluster(cores)
doSNOW::registerDoSNOW(cl)
`%dopar%` <- foreach::`%dopar%`
simresults <- foreach::foreach(i = 1:(cores * 100), .combine = "rbind", .packages = "HockeyModel") %dopar% {
simresults <- playoffSolverEngine(nsims = ceiling(nsims / (cores * 100)), completedSeries = completedSeries, east_results = east_results, west_results = west_results, currentSeries = currentSeries, summary_results = summary_results, homeAwayOdds = homeAwayOdds)
return(simresults)
}
parallel::stopCluster(cl)
gc(verbose = FALSE)
} else {
# Single cores is easier for testing
simresults <- playoffSolverEngine(nsims = nsims, completedSeries = completedSeries, east_results = east_results, west_results = west_results, currentSeries = currentSeries, summary_results = summary_results, homeAwayOdds = homeAwayOdds)
}
simodds <- data.frame("Team" = summary_results$Team)
simodds <- simodds %>%
dplyr::rowwise() %>%
dplyr::mutate(
"Make_Playoffs" = summary_results[summary_results$Team == .data$Team, ]$Playoffs,
"Win_First_Round" = (nrow(simresults[simresults$series1 == .data$Team, ]) +
nrow(simresults[simresults$series2 == .data$Team, ]) +
nrow(simresults[simresults$series3 == .data$Team, ]) +
nrow(simresults[simresults$series4 == .data$Team, ]) +
nrow(simresults[simresults$series5 == .data$Team, ]) +
nrow(simresults[simresults$series6 == .data$Team, ]) +
nrow(simresults[simresults$series7 == .data$Team, ]) +
nrow(simresults[simresults$series8 == .data$Team, ])) / nrow(simresults),
"Win_Second_Round" = (nrow(simresults[simresults$series9 == .data$Team, ]) +
nrow(simresults[simresults$series10 == .data$Team, ]) +
nrow(simresults[simresults$series11 == .data$Team, ]) +
nrow(simresults[simresults$series12 == .data$Team, ])) / nrow(simresults),
"Win_Conference" = (nrow(simresults[simresults$series13 == .data$Team, ]) +
nrow(simresults[simresults$series14 == .data$Team, ])) / nrow(simresults),
"Win_Cup" = nrow(simresults[simresults$series15 == .data$Team, ]) / nrow(simresults)
) %>%
dplyr::arrange(dplyr::desc(.data$Win_Cup), dplyr::desc(.data$Win_Conference), dplyr::desc(.data$Win_Second_Round), dplyr::desc(.data$Win_First_Round), dplyr::desc(.data$Make_Playoffs), .data$Team) %>%
as.data.frame()
return(simodds)
}
reseedTwoTeams <- function(team1, team2, summary_results, p1 = NULL) {
t1p <- summary_results[summary_results$Team == team1, ]$meanPoints
t2p <- summary_results[summary_results$Team == team2, ]$meanPoints
if (!is.null(p1) && !is.null(nrow(p1)) && nrow(p1) > 0) {
if (team1 == p1) {
return(c(team1, team2))
} else if (team2 == p1) {
return(c(team2, team1))
}
}
if (t1p > t2p) {
return(c(team1, team2))
} else if (t2p > t1p) {
return(c(team2, team1))
} else {
if (stats::runif(1) < 0.5) {
return(c(team1, team2))
} else {
return(c(team2, team1))
}
}
}
#' Given current seriess and a series number and home and away teams, either return true series winner, random series winner (with home/away wins considered) or random series winner
#'
#' @param series_number Series number from 1:15
#' @param currentSeries the full list of series returned from getAPISeries
#' @param homeTeam Home Team extracted from summary_results
#' @param awayTeam away Team extracted from summary_results
#' @param homeAwayOdds if calculated, the odds of a home or away team win
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return a series winner (team name)
single_series_solver <- function(series_number, currentSeries, homeTeam, awayTeam, homeAwayOdds = NULL, params = NULL) {
params <- parse_dc_params(params)
if (is.na(currentSeries) || nrow(currentSeries) == 0) {
return(randomSeriesWinner(homeTeam, awayTeam, homeAwayOdds = homeAwayOdds, params = params))
}
series <- currentSeries[currentSeries$SeriesID == series_number, ]
if (nrow(series[series$Status == "Complete", ]) == 1) {
if (series$HomeTeam != homeTeam | series$AwayTeam != awayTeam) {
warning("Team Mismatch series ", series_number, ". Home Team expected ", series$HomeTeam, " got ", homeTeam, ". Away Team expected ", series$AwayTeam, " got ", awayTeam, ". Using API series information.")
}
return(ifelse(series$HomeWins > series$AwayWins, series$HomeTeam, series$AwayTeam))
} else if (nrow(currentSeries[currentSeries == series_number & currentSeries$Status == "Ongoing", ]) == 1) {
if (series$HomeTeam != homeTeam | series$AwayTeam != awayTeam) {
warning("Team Mismatch series ", series_number, ". Home Team expected ", series$HomeTeam, " got ", homeTeam, ". Away Team expected ", series$AwayTeam, " got ", awayTeam, ". Using API series information.")
}
return(randomSeriesWinner(series$HomeTeam, series$AwayTeam, home_wins = series$HomeWins, away_wins = series$AwayWins, homeAwayOdds = homeAwayOdds, params = params))
} else {
return(randomSeriesWinner(homeTeam, awayTeam, homeAwayOdds = homeAwayOdds, params = params))
}
}
getCompletedSeries <- function(currentSeries) {
completedSeries <- currentSeries %>%
dplyr::filter(.data$Status == "Complete") %>%
dplyr::mutate(
"Winner" = dplyr::case_when(
.data$HomeWins > .data$AwayWins ~ .data$HomeTeam,
.data$HomeWins < .data$AwayWins ~ .data$AwayTeam
),
"Loser" = dplyr::case_when(
.data$HomeWins > .data$AwayWins ~ .data$AwayTeam,
.data$HomeWins < .data$AwayWins ~ .data$HomeTeam
),
"Series" = paste0("series", .data$SeriesID)
) %>%
dplyr::select(c("Series", "Winner", "Loser"))
return(completedSeries)
}
#' Playoff Solver Engine
#'
#' @description Does the actual simulating. A function so it's parallelizable. Not to be called directly. Exported for parallel's use
#' @param nsims number of sims (in each core)
#' @param completedSeries completed series
#' @param east_results east_results
#' @param west_results west_results
#' @param currentSeries currentSeries
#' @param summary_results summary_results
#' @param homeAwayOdds precalculated home & away pairs of odds - if available.
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @export
playoffSolverEngine <- function(nsims, completedSeries, east_results, west_results, currentSeries, summary_results, homeAwayOdds, params = NULL) {
params <- parse_dc_params(params)
simresults <- data.frame(
"SimNo" = integer(),
"l1" = character(),
"l2" = character(),
"l3" = character(),
"l4" = character(),
"l5" = character(),
"l6" = character(),
"l7" = character(),
"l8" = character(),
"series1" = character(),
"series2" = character(),
"series3" = character(),
"series4" = character(),
"series5" = character(),
"series6" = character(),
"series7" = character(),
"series8" = character(),
"series9" = character(),
"series10" = character(),
"series11" = character(),
"series12" = character(),
"series13" = character(),
"series14" = character(),
"series15" = character()
)
srvec <- c()
east_results <- as.data.frame(east_results)
west_results <- as.data.frame(west_results)
for (sim in 1:nsims) {
if (all(paste0("series", 1:8) %in% completedSeries$Series)) {
series1 <- completedSeries[completedSeries$Series == "series1", ]$Winner
series2 <- completedSeries[completedSeries$Series == "series2", ]$Winner
series3 <- completedSeries[completedSeries$Series == "series3", ]$Winner
series4 <- completedSeries[completedSeries$Series == "series4", ]$Winner
series5 <- completedSeries[completedSeries$Series == "series5", ]$Winner
series6 <- completedSeries[completedSeries$Series == "series6", ]$Winner
series7 <- completedSeries[completedSeries$Series == "series7", ]$Winner
series8 <- completedSeries[completedSeries$Series == "series8", ]$Winner
l1 <- completedSeries[completedSeries$Series == "series1", ]$Loser
l2 <- completedSeries[completedSeries$Series == "series2", ]$Loser
l3 <- completedSeries[completedSeries$Series == "series3", ]$Loser
l4 <- completedSeries[completedSeries$Series == "series4", ]$Loser
l5 <- completedSeries[completedSeries$Series == "series5", ]$Loser
l6 <- completedSeries[completedSeries$Series == "series6", ]$Loser
l7 <- completedSeries[completedSeries$Series == "series7", ]$Loser
l8 <- completedSeries[completedSeries$Series == "series8", ]$Loser
} else {
# solve east conference
er <- east_results
eastseries <- list() # p1 to p8 (conference winner, div #2, 2nd place, second div#2, second div#3, wc1, div #3, wc2)
serieslist <- list()
for (s in 1:4) {
if (paste0("series", s) %in% completedSeries$series) {
eastseries[paste0("p", s)] <- completedSeries[completedSeries$Series == paste0("series", s), ]$Winner
eastseries[paste0("p", 9 - s)] <- completedSeries[completedSeries$Series == paste0("series", s), ]$Loser
serieslist[paste0("series", s)] <- completedSeries[completedSeries$Series == paste0("series", s), ]$Winner
serieslist[paste0("l", s)] <- completedSeries[completedSeries$Series == paste0("series", s), ]$Loser
er <- er[er$Team != completedSeries[completedSeries$Series == paste0("series", s), ]$Winner, ]
er <- er[er$Team != completedSeries[completedSeries$Series == paste0("series", s), ]$Loser, ]
} else if (s %in% currentSeries$SeriesID) {
eastseries[paste0("p", s)] <- currentSeries[currentSeries$SeriesID == s, ]$HomeTeam
eastseries[paste0("p", 9 - s)] <- currentSeries[currentSeries$SeriesID == s, ]$AwayTeam
er <- er[er$Team != currentSeries[currentSeries$SeriesID == s, ]$HomeTeam, ]
er <- er[er$Team != currentSeries[currentSeries$SeriesID == s, ]$AwayTeam, ]
}
}
if (!("p1" %in% names(eastseries))) {
eastseries["p1"] <- er[sample(1:nrow(er), size = 1, prob = er$p_rank1), ]$Team
er <- er[er$Team != eastseries["p1"], ]
p1div <- getTeamDivisions(eastseries["p1"])
} else {
p1div <- getTeamDivisions(eastseries["p1"])
}
if (!("p2" %in% names(eastseries))) {
eastseries["p2"] <- er[er$Div == p1div, ]$Team[sample(1:nrow(er[er$Div == p1div, ]), size = 1, prob = er[er$Div == p1div, ]$p_rank_34)]
er <- er[er$Team != eastseries["p2"], ]
eastseries["p7"] <- er[er$Div == p1div, ]$Team[sample(1:nrow(er[er$Div == p1div, ]), size = 1, prob = er[er$Div == p1div, ]$p_rank_56)]
er <- er[er$Team != eastseries["p7"], ]
}
if (!("p3" %in% names(eastseries))) {
eastseries["p3"] <- er[er$Div != p1div, ]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank2)]
er <- er[er$Team != eastseries["p3"], ]
}
if (!("p4" %in% names(eastseries))) {
eastseries["p4"] <- er[er$Div != p1div, ]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank_34)]
er <- er[er$Team != eastseries["p4"], ]
eastseries["p5"] <- er[er$Div != p1div, ]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank_56)]
er <- er[er$Team != eastseries["p5"], ]
}
if (!("p6" %in% names(eastseries))) {
eastseries["p6"] <- er[sample(1:nrow(er), size = 1, prob = er$p_rank7), ]$Team
er <- er[er$Team != eastseries["p6"], ]
}
if (!("p8" %in% names(eastseries))) {
eastseries["p8"] <- er[sample(1:nrow(er), size = 1, prob = er$p_rank8), ]$Team
# er<-er[er$Team != eastseries['p8'],]
}
if (!("series1" %in% serieslist)) {
series1 <- single_series_solver(series_number = 1, currentSeries = currentSeries, homeTeam = eastseries[["p1"]], awayTeam = eastseries[["p8"]], homeAwayOdds = homeAwayOdds)
l1 <- ifelse(series1 == eastseries[["p1"]], eastseries[["p8"]], eastseries[["p1"]])
} else {
series1 <- serieslist[["series1"]]
l1 <- serieslist[["l1"]]
}
if (!("series2" %in% serieslist)) {
series2 <- single_series_solver(series_number = 2, currentSeries = currentSeries, homeTeam = eastseries[["p2"]], awayTeam = eastseries[["p7"]], homeAwayOdds = homeAwayOdds)
l2 <- ifelse(series2 == eastseries[["p2"]], eastseries[["p7"]], eastseries[["p2"]])
} else {
series2 <- serieslist[["series2"]]
l2 <- serieslist[["l2"]]
}
if (!("series3" %in% serieslist)) {
series3 <- single_series_solver(series_number = 3, currentSeries = currentSeries, homeTeam = eastseries[["p3"]], awayTeam = eastseries[["p6"]], homeAwayOdds = homeAwayOdds)
l3 <- ifelse(series3 == eastseries[["p3"]], eastseries[["p6"]], eastseries[["p3"]])
} else {
series3 <- serieslist[["series3"]]
l3 <- serieslist[["l3"]]
}
if (!("series4" %in% serieslist)) {
series4 <- single_series_solver(series_number = 4, currentSeries = currentSeries, homeTeam = eastseries[["p4"]], awayTeam = eastseries[["p5"]], homeAwayOdds = homeAwayOdds)
l4 <- ifelse(series4 == eastseries[["p4"]], eastseries[["p5"]], eastseries[["p4"]])
} else {
series4 <- serieslist[["series4"]]
l4 <- serieslist[["l4"]]
}
rm(er, serieslist, eastseries)
wr <- west_results
westseries <- list() # p1 to p8 (conference winner, div #2, 2nd place, second div#2, second div#3, wc1, div #3, wc2)
serieslist <- list()
for (s in 1:4) {
# of course, west series first rounds are # 5-8, so s+4 for all references
if (paste0("series", s) %in% completedSeries$series) {
westseries[paste0("p", s)] <- completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Winner
westseries[paste0("p", 9 - s)] <- completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Loser
serieslist[paste0("series", s)] <- completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Winner
serieslist[paste0("l", s)] <- completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Loser
wr <- wr[wr$Team != completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Winner, ]
wr <- wr[wr$Team != completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Loser, ]
} else if (s %in% currentSeries$SeriesID) {
westseries[paste0("p", s)] <- currentSeries[currentSeries$SeriesID == s + 4, ]$HomeTeam
westseries[paste0("p", 9 - s)] <- currentSeries[currentSeries$SeriesID == s + 4, ]$AwayTeam
wr <- wr[wr$Team != currentSeries[currentSeries$SeriesID == s + 4, ]$HomeTeam, ]
wr <- wr[wr$Team != currentSeries[currentSeries$SeriesID == s + 4, ]$AwayTeam, ]
}
}
if (!("p1" %in% names(westseries))) {
westseries["p1"] <- wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank1), ]$Team
wr <- wr[wr$Team != westseries["p1"], ]
p1div <- getTeamDivisions(westseries["p1"])
} else {
p1div <- getTeamDivisions(westseries["p1"])
}
if (!("p2" %in% names(westseries))) {
westseries["p2"] <- wr[wr$Div == p1div, ]$Team[sample(1:nrow(wr[wr$Div == p1div, ]), size = 1, prob = wr[wr$Div == p1div, ]$p_rank_34)]
wr <- wr[wr$Team != westseries["p2"], ]
westseries["p7"] <- wr[wr$Div == p1div, ]$Team[sample(1:nrow(wr[wr$Div == p1div, ]), size = 1, prob = wr[wr$Div == p1div, ]$p_rank_56)]
wr <- wr[wr$Team != westseries["p7"], ]
}
if (!("p3" %in% names(westseries))) {
westseries["p3"] <- wr[wr$Div != p1div, ]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank2)]
wr <- wr[wr$Team != westseries["p3"], ]
}
if (!("p4" %in% names(westseries))) {
westseries["p4"] <- wr[wr$Div != p1div, ]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank_34)]
wr <- wr[wr$Team != westseries["p4"], ]
westseries["p5"] <- wr[wr$Div != p1div, ]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank_56)]
wr <- wr[wr$Team != westseries["p5"], ]
}
if (!("p6" %in% names(westseries))) {
westseries["p6"] <- wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank7), ]$Team
wr <- wr[wr$Team != westseries["p6"], ]
}
if (!("p8" %in% names(westseries))) {
westseries["p8"] <- wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank8), ]$Team
# wr<-wr[wr$Team != westseries['p8'],]
}
if (!("series1" %in% serieslist)) {
series5 <- single_series_solver(series_number = 5, currentSeries = currentSeries, homeTeam = westseries[["p1"]], awayTeam = westseries[["p8"]], homeAwayOdds = homeAwayOdds)
l5 <- ifelse(series5 == westseries[["p1"]], westseries[["p8"]], westseries[["p1"]])
} else {
series5 <- serieslist[["series1"]]
l5 <- serieslist[["l1"]]
}
if (!("series2" %in% serieslist)) {
series6 <- single_series_solver(series_number = 6, currentSeries = currentSeries, homeTeam = westseries[["p2"]], awayTeam = westseries[["p7"]], homeAwayOdds = homeAwayOdds)
l6 <- ifelse(series6 == westseries[["p2"]], westseries[["p7"]], westseries[["p2"]])
} else {
series6 <- serieslist[["series2"]]
l6 <- serieslist[["l2"]]
}
if (!("series3" %in% serieslist)) {
series7 <- single_series_solver(series_number = 7, currentSeries = currentSeries, homeTeam = westseries[["p3"]], awayTeam = westseries[["p6"]], homeAwayOdds = homeAwayOdds)
l7 <- ifelse(series7 == westseries[["p3"]], westseries[["p6"]], westseries[["p3"]])
} else {
series7 <- serieslist[["series3"]]
l7 <- serieslist[["l3"]]
}
if (!("series4" %in% serieslist)) {
series8 <- single_series_solver(series_number = 8, currentSeries = currentSeries, homeTeam = westseries[["p4"]], awayTeam = westseries[["p5"]], homeAwayOdds = homeAwayOdds)
l8 <- ifelse(series8 == westseries[["p4"]], westseries[["p5"]], westseries[["p4"]])
} else {
series8 <- serieslist[["series4"]]
l8 <- serieslist[["l4"]]
}
rm(wr, serieslist, westseries)
# if('series1' %in% completedSeries$Series){
# series1 <- completedSeries[completedSeries$Series == 'series1', ]$Winner
# l1 <- completedSeries[completedSeries$Series == 'series1', ]$Loser
# er<-er[er$Team != series1,]
# er<-er[er$Team != l1,]
# } else if (1 %in% currentSeries$SeriesID){
# e1.1 <- currentSeries[currentSeries$SeriesID == 1, ]$HomeTeam
# er<-er[er$Team != e1.1,]
# ewc2 <- currentSeries[currentSeries$SeriesID == 1, ]$AwayTeam
# er<-er[er$Team != ewc2,]
# odds<-playoffWin(e1.1, ewc2, currentSeries[currentSeries$SeriesID == 1, ]$HomeWins,
# currentSeries[currentSeries$SeriesID == 1, ]$AwayWins, params = params)
# series1<-sample(c(e1.1, ewc2), size = 1, prob = c(odds, 1-odds))
# l1 <- ifelse(series4 == e1.1, ewc2, e1.1) #If winner = a, then b, else a
# } else {
# e1.1<-er[sample(1:nrow(er), size = 1, prob = er$p_rank1),]$Team
# er<-er[er$Team != e1.1,]
# ewc2<-er[sample(1:nrow(er), size = 1, prob = er$p_rank8),]$Team
# er<-er[er$Team != ewc2,]
# series1 <- single_series_solver(series_number = 1, currentSeries = currentSeries, homeTeam = e1.1, awayTeam = ewc2, homeAwayOdds = homeAwayOdds)
# l1 <- ifelse(series1 == e1.1, ewc2, e1.1) #If winner = a, then b, else a
# }
# p1div<-getTeamDivisions(e1.1)
#
# if('series2' %in% completedSeries$Series){
# series2 <- completedSeries[completedSeries$Series == 'series2', ]$Winner
# l2 <- completedSeries[completedSeries$Series == 'series2', ]$Loser
# er<-er[er$Team != series2,]
# er<-er[er$Team != l2,]
# } else if (2 %in% currentSeries$SeriesID){
# e1.2 <- currentSeries[currentSeries$SeriesID == 2, ]$HomeTeam
# er<-er[er$Team != e1.1,]
# e1.3 <- currentSeries[currentSeries$SeriesID == 2, ]$AwayTeam
# er<-er[er$Team != e1.3,]
# odds<-playoffWin(e1.2, e1.3, currentSeries[currentSeries$SeriesID == 2, ]$HomeWins,
# currentSeries[currentSeries$SeriesID == 2, ]$AwayWins, params = params)
# series2<-sample(c(e1.2, e1.3), size = 1, prob = c(odds, 1-odds))
# l2 <- ifelse(series2 == e1.2, e1.3, e1.2) #If winner = a, then b, else a
# } else {
# e1.2<-er[er$Div == p1div,]$Team[sample(1:nrow(er[er$Div == p1div, ]), size = 1, prob = er[er$Div == p1div, ]$p_rank_34)]
# er<-er[er$Team != e1.2,]
# e1.3<-er[er$Div == p1div,]$Team[sample(1:nrow(er[er$Div == p1div, ]), size = 1, prob = er[er$Div == p1div, ]$p_rank_56)]
# er<-er[er$Team != e1.3,]
# series2 <- single_series_solver(series_number = 2, currentSeries = currentSeries, homeTeam = e1.2, awayTeam = e1.3, homeAwayOdds = homeAwayOdds)
# l2 <- ifelse(series2 == e1.2, e1.3, e1.2) #If winner = a, then b, else a
# }
#
# if('series3' %in% completedSeries$Series){
# series3 <- completedSeries[completedSeries$Series == 'series3', ]$Winner
# l3 <- completedSeries[completedSeries$Series == 'series3', ]$Loser
# er<-er[er$Team != series3,]
# er<-er[er$Team != l3,]
# } else if (3 %in% currentSeries$SeriesID){
# e2.1 <- currentSeries[currentSeries$SeriesID == 3, ]$HomeTeam
# er<-er[er$Team != e2.1,]
# ewc1 <- currentSeries[currentSeries$SeriesID == 3, ]$AwayTeam
# er<-er[er$Team != ewc1,]
# odds<-playoffWin(e2.1, ewc1, currentSeries[currentSeries$SeriesID == 3, ]$HomeWins,
# currentSeries[currentSeries$SeriesID == 3, ]$AwayWins, params = params)
# series3<-sample(c(e2.1, ewc1), size = 1, prob = c(odds, 1-odds))
# l3 <- ifelse(series3 == e2.2, e2.3, e2.2) #If winner = a, then b, else a
# } else {
# e2.1<-er[sample(1:nrow(er), size = 1, prob = er$p_rank2),]$Team
# er<-er[er$Team != e2.1,]
# ewc1<-er[sample(1:nrow(er), size = 1, prob = er$p_rank7),]$Team
# er<-er[er$Team != ewc1,]
# series3 <- single_series_solver(series_number = 3, currentSeries = currentSeries, homeTeam = e2.1, awayTeam = ewc1, homeAwayOdds = homeAwayOdds)
# l3 <- ifelse(series3 == e2.1, ewc1, e2.1) #If winner = a, then b, else a
# }
#
# if('series4' %in% completedSeries$Series){
# series4 <- completedSeries[completedSeries$Series == 'series4', ]$Winner
# l4 <- completedSeries[completedSeries$Series == 'series4', ]$Loser
# er<-er[er$Team != series4,]
# er<-er[er$Team != l4,]
# } else if (4 %in% currentSeries$SeriesID){
# e2.2 <- currentSeries[currentSeries$SeriesID == 4, ]$HomeTeam
# er<-er[er$Team != e2.2,]
# e2.3 <- currentSeries[currentSeries$SeriesID == 4, ]$AwayTeam
# er<-er[er$Team != e2.3,]
# odds<-playoffWin(e2.2, e2.3, currentSeries[currentSeries$SeriesID == 4, ]$HomeWins,
# currentSeries[currentSeries$SeriesID == 4, ]$AwayWins, params = params)
# series4<-sample(c(e2.2, e2.3), size = 1, prob = c(odds, 1-odds))
# l4 <- ifelse(series4 == e2.2, e2.3, e2.2) #If winner = a, then b, else a
# } else {
# e2.2<-er[er$Div != p1div,]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank_34)]
# er<-er[er$Team != e2.2,]
# e2.3<-er[er$Div != p1div,]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank_56)]
# er<-er[er$Team != e2.3,]
# series4 <- single_series_solver(series_number = 4, currentSeries = currentSeries, homeTeam = e2.2, awayTeam = e2.3, homeAwayOdds = homeAwayOdds)
# l4 <- ifelse(series4 == e2.2, e2.3, e2.2) #If winner = a, then b, else a
# }
#
# wr<-west_results
#
# if('series5' %in% completedSeries$Series){
# series5 <- completedSeries[completedSeries$Series == 'series5', ]$Winner
# l5 <- completedSeries[completedSeries$Series == 'series5', ]$Loser
# wr<-wr[wr$Team != series5,]
# wr<-wr[wr$Team != l5,]
# } else if (5 %in% currentSeries$SeriesID){
# w1.1 <- currentSeries[currentSeries$SeriesID == 5, ]$HomeTeam
# wr<-wr[wr$Team != w1.1,]
# wwc2 <- currentSeries[currentSeries$SeriesID == 5, ]$AwayTeam
# wr<-wr[wr$Team != wwc2,]
# odds<-playoffWin(w1.1, wwc2, currentSeries[currentSeries$SeriesID == 5, ]$HomeWins,
# currentSeries[currentSeries$SeriesID == 5, ]$AwayWins, params = params)
# series5<-sample(c(w1.1, wwc2), size = 1, prob = c(odds, 1-odds))
# l5 <- ifelse(series5 == w1.1, wwc2, w1.1) #If winner = a, then b, else a
# } else {
# w1.1<-wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank1),]$Team
# wr<-wr[wr$Team != w1.1,]
# wwc2<-wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank8),]$Team
# wr<-wr[wr$Team != wwc2,]
# series5 <- single_series_solver(series_number = 5, currentSeries = currentSeries, homeTeam = w1.1, awayTeam = wwc2, homeAwayOdds = homeAwayOdds)
# l5 <- ifelse(series5 == w1.1, wwc2, w1.1) #If winner = a, then b, else a
# }
# p1div<-getTeamDivisions(w1.1)
#
# if('series6' %in% completedSeries$Series){
# series6 <- completedSeries[completedSeries$Series == 'series6', ]$Winner
# l6 <- completedSeries[completedSeries$Series == 'series6', ]$Loser
# wr<-wr[wr$Team != series6,]
# wr<-wr[wr$Team != l6,]
# } else if (6 %in% currentSeries$SeriesID) {
# w1.2 <- currentSeries[currentSeries$SeriesID == 6, ]$HomeTeam
# wr<-wr[wr$Team != w1.1,]
# w1.3 <- currentSeries[currentSeries$SeriesID == 6, ]$AwayTeam
# wr<-wr[wr$Team != w1.3,]
# odds<-playoffWin(w1.2, w1.3, currentSeries[currentSeries$SeriesID == 6, ]$HomeWins,
# currentSeries[currentSeries$SeriesID == 6, ]$AwayWins, params = params)
# series6<-sample(c(w1.2, w1.3), size = 1, prob = c(odds, 1-odds))
# l6 <- ifelse(series6 == w1.2, w1.3, w1.2) #If winner = a, then b, else a
# } else {
# w1.2<-wr[wr$Div == p1div,]$Team[sample(1:nrow(wr[wr$Div == p1div, ]), size = 1, prob = wr[wr$Div == p1div, ]$p_rank_34)]
# wr<-wr[wr$Team != w1.2,]
# w1.3<-wr[wr$Div == p1div,]$Team[sample(1:nrow(wr[wr$Div == p1div, ]), size = 1, prob = wr[wr$Div == p1div, ]$p_rank_56)]
# wr<-wr[wr$Team != w1.3,]
# series6 <- single_series_solver(series_number = 6, currentSeries = currentSeries, homeTeam = w1.2, awayTeam = w1.3, homeAwayOdds = homeAwayOdds)
# l6 <- ifelse(series6 == w1.2, w1.3, w1.2) #If winner = a, then b, else a
# }
#
# if('series7' %in% completedSeries$Series){
# series7 <- completedSeries[completedSeries$Series == 'series7', ]$Winner
# l7 <- completedSeries[completedSeries$Series == 'series7', ]$Loser
# wr<-wr[wr$Team != series7,]
# wr<-wr[wr$Team != l7,]
# } else if (7 %in% currentSeries$SeriesID){
# w2.1 <- currentSeries[currentSeries$SeriesID == 7, ]$HomeTeam
# wr<-wr[wr$Team != w1.1,]
# wwc1 <- currentSeries[currentSeries$SeriesID == 7, ]$AwayTeam
# wr<-wr[wr$Team != wwc1,]
# odds<-playoffWin(w2.1, wwc1, currentSeries[currentSeries$SeriesID == 7, ]$HomeWins,
# currentSeries[currentSeries$SeriesID == 7, ]$AwayWins, params = params)
# series7<-sample(c(w2.1, wwc1), size = 1, prob = c(odds, 1-odds))
# l7 <- ifelse(series7 == w2.1, wwc1, w2.1) #If winner = a, then b, else a
# } else {
# w2.1<-wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank2),]$Team
# wr<-wr[wr$Team != w2.1,]
# wwc1<-wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank7),]$Team
# wr<-wr[wr$Team != wwc1,]
# series7 <- single_series_solver(series_number = 7, currentSeries = currentSeries, homeTeam = w2.1, awayTeam = wwc1, homeAwayOdds = homeAwayOdds)
# l7 <- ifelse(series7 == w2.1, wwc1, w2.1) #If winner = a, then b, else a
# }
#
# if('series8' %in% completedSeries$Series){
# series8 <- completedSeries[completedSeries$Series == 'series8', ]$Winner
# l8 <- completedSeries[completedSeries$Series == 'series8', ]$Loser
# wr<-wr[wr$Team != series8,]
# wr<-wr[wr$Team != l8,]
# } else if (8 %in% currentSeries$SeriesID){
# w2.2 <- currentSeries[currentSeries$SeriesID == 8, ]$HomeTeam
# wr<-wr[wr$Team != w2.2,]
# w2.3 <- currentSeries[currentSeries$SeriesID == 8, ]$AwayTeam
# wr<-wr[wr$Team != w2.3,]
# odds<-playoffWin(w2.2, w2.3, currentSeries[currentSeries$SeriesID == 8, ]$HomeWins,
# currentSeries[currentSeries$SeriesID == 8, ]$AwayWins, params = params)
# series8<-sample(c(w2.2, w2.3), size = 1, prob = c(odds, 1-odds))
# l8 <- ifelse(series8 == w2.2, w2.3, w2.2) #If winner = a, then b, else a
# } else {
# w2.2<-wr[wr$Div != p1div,]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank_34)]
# wr<-wr[wr$Team != w2.2,]
# w2.3<-wr[wr$Div != p1div,]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank_56)]
# wr<-wr[wr$Team != w2.3,]
# series8 <- single_series_solver(series_number = 4, currentSeries = currentSeries, homeTeam = w2.2, awayTeam = w2.3, homeAwayOdds = homeAwayOdds)
# l8 <- ifelse(series8 == w2.2, w2.3, w2.2) #If winner = a, then b, else a
# }
}
# No reseeding for round 2 (but in reality yeah there is, wildCard doesn't have home advantage)
if ("series9" %in% completedSeries$Series) {
series9 <- completedSeries[completedSeries$Series == "series9", ]$Winner
} else {
rs <- reseedTwoTeams(series1, series2, summary_results, currentSeries[currentSeries$SeriesID == 1, ]$HomeTeam)
series9 <- single_series_solver(series_number = 9, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
}
if ("series10" %in% completedSeries$Series) {
series10 <- completedSeries[completedSeries$Series == "series10", ]$Winner
} else {
rs <- reseedTwoTeams(series3, series4, summary_results, currentSeries[currentSeries$SeriesID == 3, ]$HomeTeam)
series10 <- single_series_solver(series_number = 10, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
}
if ("series11" %in% completedSeries$Series) {
series11 <- completedSeries[completedSeries$Series == "series11", ]$Winner
} else {
rs <- reseedTwoTeams(series5, series6, summary_results, currentSeries[currentSeries$SeriesID == 5, ]$HomeTeam)
series11 <- single_series_solver(series_number = 11, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
}
if ("series12" %in% completedSeries$Series) {
series12 <- completedSeries[completedSeries$Series == "series12", ]$Winner
} else {
rs <- reseedTwoTeams(series7, series8, summary_results, currentSeries[currentSeries$SeriesID == 7, ]$HomeTeam)
series12 <- single_series_solver(series_number = 12, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
}
# Reseed for conference finals & stanley cup finals
if ("series13" %in% completedSeries$Series) {
series13 <- completedSeries[completedSeries$Series == "series13", ]$Winner
} else {
rs <- reseedTwoTeams(series9, series10, summary_results)
series13 <- single_series_solver(series_number = 13, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
}
if ("series14" %in% completedSeries$Series) {
series14 <- completedSeries[completedSeries$Series == "series14", ]$Winner
} else {
rs <- reseedTwoTeams(series11, series12, summary_results)
series14 <- single_series_solver(series_number = 14, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
}
# Stanley Cup Final
if ("series15" %in% completedSeries$Series) {
series15 <- completedSeries[completedSeries$Series == "series15", ]$Winner
} else {
rs <- reseedTwoTeams(series13, series14, summary_results)
series15 <- single_series_solver(series_number = 15, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
}
srvec <- c(srvec, sim, l1, l2, l3, l4, l5, l6, l7, l8, series1, series2, series3, series4, series5, series6, series7, series8, series9, series10, series11, series12, series13, series14, series15)
rm(l1, l2, l3, l4, l5, l6, l7, l8, series1, series2, series3, series4, series5, series6, series7, series8, series9, series10, series11, series12, series13, series14, series15)
}
srdf <- as.data.frame(matrix(srvec, ncol = 24, byrow = TRUE))
names(srdf) <- names(simresults)
simresults <- dplyr::as_tibble(srdf)
return(simresults)
}
getAllHomeAwayOdds <- function(teamlist, params = NULL) {
params <- parse_dc_params(params)
homeAwayOdds <- expand.grid("HomeTeam" = teamlist, "AwayTeam" = teamlist, stringsAsFactors = FALSE)
homeAwayOdds <- homeAwayOdds[homeAwayOdds$HomeTeam != homeAwayOdds$AwayTeam, ]
homeAwayOdds$HomeOdds <- apply(homeAwayOdds, 1, function(x) playoffWin(x[1], x[2], params = params))
return(homeAwayOdds)
}
#' Record Today's Predictions
#'
#' @description Record today's predictions to file (for easy later retrieval). Run \code{cleanupPredictionsFile} periodically to tidy
#'
#' @param today Day's predictions to record. Defaults to today, but can set any other day
#' @param filepath csv file location to store predictions. Will append to file.
#' @param schedule HockeyModel::schedule or supplied. \code{today} date must be in schedule
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#' @param include_xG Whether to record daily XG values
#' @param draws whether to record draw odds (True) or not (False). Default is True
#'
#' @return NULL
#' @export
recordTodaysPredictions <- function(today = Sys.Date(), filepath = file.path(getOption("HockeyModel.data.path"), "dailyodds.csv"), schedule = HockeyModel::schedule, params = NULL, include_xG = FALSE, draws = TRUE) {
params <- parse_dc_params(params)
stopifnot(is.Date(today))
today <- as.Date(today)
today_sched <- schedule[schedule$Date == today, ]
if (nrow(today_sched) == 0) {
stop("No games on date:", today)
}
today_preds <- todayDC(today = today, params = params, schedule = schedule, include_xG = include_xG, draws = draws)
preds <- dplyr::full_join(today_sched, today_preds, suffix = c("", ""), by = c("HomeTeam", "AwayTeam"))
if (!include_xG) {
if (!draws) {
preds <- preds[, c("Date", "GameID", "HomeTeam", "AwayTeam", "HomeWin", "AwayWin")]
} else {
preds <- preds[, c("Date", "GameID", "HomeTeam", "AwayTeam", "HomeWin", "AwayWin", "Draw")]
}
} else {
if (!draws) {
preds <- preds[, c("Date", "HomeTeam", "AwayTeam", "HomeWin", "AwayWin", "Home_xG", "Away_xG")]
} else {
preds <- preds[, c("Date", "HomeTeam", "AwayTeam", "HomeWin", "AwayWin", "Draw", "Home_xG", "Away_xG")]
}
}
if (file.exists(filepath)) {
utils::write.table(preds, file = filepath, append = TRUE, col.names = FALSE, row.names = FALSE, sep = ",", dec = ".")
} else {
utils::write.table(preds, file = filepath, append = FALSE, col.names = TRUE, row.names = FALSE, sep = ",", dec = ".")
}
}
#' Cleanup Predictions File
#'
#' @description Sometimes the predictions file may end up with game duplicates (last minute postponements, etc,) This deduplicates, taking only the latest instance of a prediction (Games are unique by GameID)
#'
#' @param filepath file path to cleanup
#'
#' @return NULL
#' @export
cleanupPredictionsFile <- function(filepath = file.path(getOption("HockeyModel.data.path"), "dailyodds.csv")) {
dailyodds <- utils::read.csv(filepath)
dailyodds <- dailyodds %>%
dplyr::mutate("Date" = as.Date(.data$Date)) %>%
dplyr::arrange(dplyr::desc(.data$Date)) %>%
dplyr::distinct(.data$GameID, .keep_all = TRUE) %>%
dplyr::arrange(.data$Date, .data$GameID) %>%
utils::write.table(file = filepath, append = FALSE, col.names = TRUE, row.names = FALSE, sep = ",", dec = ".")
return(TRUE)
}
build_past_predictions <- function(startDate, endDate, filepath = file.path(getOption("HockeyModel.data.path"), "dailyodds.csv"), include_xG = FALSE, draws = TRUE) {
stopifnot(is.Date(startDate))
stopifnot(is.Date(endDate))
startDate <- as.Date(startDate)
endDate <- as.Date(endDate)
scores <- HockeyModel::scores
schedule <- HockeyModel::schedule
for (day in seq.Date(startDate, endDate, by = 1)) {
d <- as.Date(day, origin = "1970-01-01")
if (nrow(schedule[schedule$Date == d, ]) == 0) {
next # no games that day, just skip it.
}
message("Results as of: ", d)
score <- scores[scores$Date < day, ]
score <- score[score$Date > (as.Date(startDate) - 4000), ] # only feed in ~ 11 years data to calculate m & rho
sched <- schedule[schedule$Date == d, ]
params <- list()
params$m <- getM(scores = score, currentDate = d)
params$rho <- getRho(m = params$m, scores = score)
w.day <- getWeibullParams(m = params$m, rho = params$rho, scores = score)
params$beta <- w.day$beta
params$eta <- w.day$eta
params$k <- w.day$k
recordTodaysPredictions(today = d, filepath = filepath, schedule = sched, params = params, include_xG = include_xG, draws = draws)
}
cleanupPredictionsFile(filepath = filepath)
return(TRUE)
}
#' Get Series Odds
#'
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return NULL if no series are currently set but not complete, else a data frame.
#' @export
getSeriesOdds <- function(params = NULL) {
series <- getAPISeries()
if (is.na(series)) {
return(NULL)
}
if (nrow(series) == 0) {
return(NULL)
}
series <- series[series$Status != "Complete", ]
if (nrow(series) == 0) {
return(NULL)
}
params <- parse_dc_params(params)
series$HomeSeed <- NULL
series$AwaySeed <- NULL
series$SeriesID <- NULL
series$HomeOdds <- 0
for (i in 1:nrow(series)) {
series[i, ]$HomeOdds <- playoffWin(series[i, ]$HomeTeam, series[i, ]$AwayTeam,
series[i, ]$HomeWins, series[i, ]$AwayWins,
params = params
)
}
series$AwayOdds <- 1 - series$HomeOdds
return(series)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.