Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update season projection method to project weekly ranks, then project weekly scores based on those ranks #68

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Package: ffsimulator
Title: Simulate Fantasy Football Seasons
Version: 1.2.3.01
Version: 1.2.3.02
Authors@R:
person(given = "Tan",
family = "Ho",
role = c("aut", "cre", "cph"),
email = "tan@tanho.ca",
comment = c(ORCID = "0000-0001-8388-5155"))
Description: Uses bootstrap resampling to run fantasy football season
simulations supported by historical rankings and 'nflfastR' data,
simulations supported by historical 'FantasyPros' rankings and 'nflverse' data,
calculating optimal lineups, and returning aggregated results.
License: MIT + file LICENSE
URL: https://ffsimulator.ffverse.com,
Expand Down
19 changes: 15 additions & 4 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ S3method(plot,ff_simulation_week)
S3method(print,ff_simulation)
S3method(print,ff_simulation_week)
S3method(print,ff_war)
export("%>%")
export(.ffs_cache)
export(.ffs_cache_dir)
export(.ffs_cache_example)
export(.ffs_read_data)
export(autoplot.ff_simulation)
export(autoplot.ff_simulation_week)
export(espn_connect)
Expand Down Expand Up @@ -44,20 +45,30 @@ export(ffs_summarise_week)
export(ffs_summarize_season)
export(ffs_summarize_simulation)
export(ffs_summarize_week)
export(ffs_update_data_template)
export(fleaflicker_connect)
export(fp_injury_table)
export(fp_rankings_history)
export(fp_rankings_history_week)
export(mfl_connect)
export(sleeper_connect)
importFrom(data.table,":=")
importFrom(data.table,.BY)
importFrom(data.table,.EACHI)
importFrom(data.table,.GRP)
importFrom(data.table,.I)
importFrom(data.table,.N)
importFrom(data.table,.NGRP)
importFrom(data.table,.SD)
importFrom(data.table,`:=`)
importFrom(data.table,data.table)
importFrom(ffscrapr,espn_connect)
importFrom(ffscrapr,ff_connect)
importFrom(ffscrapr,ff_scoringhistory)
importFrom(ffscrapr,ff_starter_positions)
importFrom(ffscrapr,fleaflicker_connect)
importFrom(ffscrapr,mfl_connect)
importFrom(ffscrapr,sleeper_connect)
importFrom(magrittr,"%>%")
importFrom(magrittr,`%>%`)
importFrom(rlang,.data)
importFrom(rlang,.env)
importFrom(rlang,`%||%`)
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- Update available fantasypros rankings to include 2022
- Require ggplot2 3.4.0 for plotting features
- Draws from a slightly broader sample of players for season outcomes
- Remove packaged rankings built-in in favour of reading files from user cache,
falling back to system.file() internal data. Adds script for updating.


---
Expand Down
101 changes: 54 additions & 47 deletions R/0_ff_simulate.R → R/00-ff_simulate.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,53 +38,53 @@ ff_simulate <- function(conn,
base_seasons = 2012:2022,
actual_schedule = FALSE,
replacement_level = TRUE,
pos_filter = c("QB","RB","WR","TE","K"),
pos_filter = c("QB", "RB", "WR", "TE", "K"),
verbose = NULL,
return = c("default", "all")
) {
version = c("v2", "v1"),
return = c("default", "all")) {

#### TEST ####

# conn <- mfl_connect(2021,54040)
# conn <- sleeper_connect(2021,"734442977157603328")
# verbose <- NULL
# base_seasons = 2012:2020
# conn <- sleeper_connect(2023,"921444366491668480")
# base_seasons = 2012:2022
# gp_model = "simple"
# pos_filter = c("QB","RB","WR","TE","K")
# n_seasons = 100
# n_weeks = 14
# best_ball = FALSE
# seed = NULL
# base_seasons = 2012:2020
# actual_schedule = TRUE
# pos_filter = c("QB","RB","WR","TE","K")
# actual_schedule = FALSE
# replacement_level = TRUE
# verbose = TRUE
# return = "all"
# version = "v2"

#### Assertions ####
#### ASSERTIONS ####

if (!class(conn) %in% c("mfl_conn", "sleeper_conn", "flea_conn", "espn_conn")) {
stop("conn should be a connection object created by `ff_connect()` and friends!",
call. = FALSE
cli::cli_abort(
"conn should be a connection object created by `ff_connect()` and friends!"
)
}

gp_model <- rlang::arg_match0(gp_model, c("simple","none"))
return <- rlang::arg_match0(return, c("default","all"))
checkmate::assert_subset(pos_filter, c("QB","RB","WR","TE","K"))
gp_model <- rlang::arg_match0(gp_model, c("simple", "none"))
return <- rlang::arg_match0(return, c("default", "all"))
version <- rlang::arg_match0(version, c("v2", "v1"))
pos_filter <- rlang::arg_match(pos_filter, c("QB", "RB", "WR", "TE", "K"), multiple = TRUE)
checkmate::assert_numeric(base_seasons, lower = 2012, upper = 2022)
checkmate::assert_int(n_seasons, lower = 1)
checkmate::assert_int(n_weeks, lower = 1)
checkmate::assert_int(seed, null.ok = TRUE)
if (!is.null(seed)) set.seed(seed)
checkmate::assert_flag(best_ball)
if(!is.null(verbose)) set_verbose(verbose)
if (!is.null(verbose)) set_verbose(verbose)
checkmate::assert_flag(actual_schedule)
checkmate::assert_flag(replacement_level)

#### Import Data ####

vcli_rule("Starting simulation {Sys.time()}")
vcli_rule("Starting simulation {format(Sys.time())}")

vcli_start(msg = "Importing data")

Expand All @@ -101,34 +101,37 @@ ff_simulate <- function(conn,

weeks <- seq_len(n_weeks)

if(actual_schedule) {
if (actual_schedule) {
schedule <- ffs_schedule(conn)

weeks <- unique(schedule$week)

if(length(weeks)==0) {
if (length(weeks) == 0) {

cli::cli_alert_danger("No unplayed weeks to simulate!")
out <- structure(list(schedule = ffscrapr::ff_schedule(conn),
league_info = league_info,
simulation_params = list(
n_seasons = n_seasons,
n_weeks = n_weeks,
scrape_date = latest_rankings$scrape_date[[1]],
best_ball = best_ball,
seed = seed,
gp_model = gp_model,
actual_schedule = actual_schedule,
base_seasons = list(base_seasons)
)),
class = "ff_simulation")
out <- structure(
list(
schedule = ffscrapr::ff_schedule(conn),
league_info = league_info,
simulation_params = list(
n_seasons = n_seasons,
n_weeks = n_weeks,
scrape_date = latest_rankings$scrape_date[[1]],
best_ball = best_ball,
seed = seed,
gp_model = gp_model,
actual_schedule = actual_schedule,
base_seasons = list(base_seasons)
)
),
class = "ff_simulation")

return(out)

}

cli::cli_alert_info("Simulating only unplayed weeks: {
min(weeks,na.rm = TRUE)}-{
min(weeks, na.rm = TRUE)}-{
max(weeks, na.rm = TRUE)}")
}

Expand All @@ -138,28 +141,32 @@ ff_simulate <- function(conn,

vcli_start(msg = "Generating Projections")

if(!replacement_level) rosters_rl <- rosters
if (!replacement_level) rosters_rl <- rosters

if(replacement_level){
rosters_rl <- ffs_add_replacement_level(rosters = rosters,
latest_rankings = latest_rankings,
franchises = franchises,
lineup_constraints = lineup_constraints,
pos_filter = pos_filter)
if (replacement_level) {
rosters_rl <- ffs_add_replacement_level(
rosters = rosters,
latest_rankings = latest_rankings,
franchises = franchises,
lineup_constraints = lineup_constraints,
pos_filter = pos_filter
)
}

adp_outcomes <- ffs_adp_outcomes(
scoring_history = scoring_history,
gp_model = gp_model,
pos_filter = pos_filter
pos_filter = pos_filter,
version = version
)

projected_scores <- ffs_generate_projections(
adp_outcomes = adp_outcomes,
latest_rankings = latest_rankings,
n_seasons = n_seasons,
weeks = weeks,
rosters = rosters_rl
rosters = rosters_rl,
version = version
)

vcli_end(msg_done = "Generating Projections...done! {Sys.time()}")
Expand All @@ -186,12 +193,12 @@ ff_simulate <- function(conn,

vcli_start(msg = "Building Schedules")

if(actual_schedule) {
if (actual_schedule) {
schedules <- ffs_repeat_schedules(n_seasons = n_seasons,
actual_schedule = schedule)
}

if(!actual_schedule){
if (!actual_schedule) {
schedules <- ffs_build_schedules(
n_seasons = n_seasons,
n_weeks = n_weeks,
Expand All @@ -209,7 +216,7 @@ ff_simulate <- function(conn,

vcli_end(msg_done = "Summarising Simulation Data...done! {Sys.time()}")

if(return == "default"){
if (return == "default") {

out <- structure(
list(
Expand All @@ -235,7 +242,7 @@ ff_simulate <- function(conn,
)
}

if(return == "all"){
if (return == "all") {

out <- structure(
list(
Expand Down Expand Up @@ -268,7 +275,7 @@ ff_simulate <- function(conn,
)
}

vcli_rule("Simulation complete! {Sys.time()}")
vcli_rule("Simulation complete! {format(Sys.time())}")

return(out)
}
Expand Down
28 changes: 14 additions & 14 deletions R/0_ff_simulate_week.R → R/00-ff_simulate_week.R
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ ff_simulate_week <- function(conn,
base_seasons = 2012:2022,
actual_schedule = TRUE,
replacement_level = FALSE,
pos_filter = c("QB","RB","WR","TE","K"),
pos_filter = c("QB", "RB", "WR", "TE", "K"),
verbose = NULL,
return = c("default","all")) {
return = c("default", "all")) {

# conn <- mfl_connect(2021,54040)
# verbose <- NULL
Expand All @@ -61,13 +61,13 @@ ff_simulate_week <- function(conn,
}

return <- rlang::arg_match(return)
checkmate::assert_subset(pos_filter, c("QB","RB","WR","TE","K"))
checkmate::assert_subset(pos_filter, c("QB", "RB", "WR", "TE", "K"))
checkmate::assert_numeric(base_seasons, lower = 2012, upper = 2022)
checkmate::assert_int(n, lower = 1)
checkmate::assert_int(seed, null.ok = TRUE)
if (!is.null(seed)) set.seed(seed)
checkmate::assert_flag(best_ball)
if(!is.null(verbose)) set_verbose(verbose)
if (!is.null(verbose)) set_verbose(verbose)
checkmate::assert_flag(actual_schedule)
checkmate::assert_flag(replacement_level)

Expand All @@ -87,11 +87,11 @@ ff_simulate_week <- function(conn,

lineup_constraints <- ffs_starter_positions(conn)

if(actual_schedule) {
if (actual_schedule) {
schedule <- ffs_schedule(conn)
schedule <- schedule[schedule$week == min(schedule$week),] # simulate first unplayed week
schedule <- schedule[schedule$week == min(schedule$week), ] # simulate first unplayed week

if(nrow(schedule)==0) {
if (nrow(schedule) == 0) {
cli::cli_alert_danger("No unplayed weeks to simulate!")
out <- structure(list(schedule = ffscrapr::ff_schedule(conn),
league_info = league_info,
Expand All @@ -114,9 +114,9 @@ ff_simulate_week <- function(conn,

vcli_start(msg = "Generating Projections")

if(!replacement_level) rosters_rl <- rosters
if (!replacement_level) rosters_rl <- rosters

if(replacement_level){
if (replacement_level) {
rosters_rl <- ffs_add_replacement_level(rosters = rosters,
latest_rankings = latest_rankings,
franchises = franchises,
Expand Down Expand Up @@ -163,13 +163,13 @@ ff_simulate_week <- function(conn,

vcli_start(msg = "Building Schedules")

if(actual_schedule) {
if (actual_schedule) {
schedules <- ffs_repeat_schedules(n_seasons = n, actual_schedule = schedule)
schedules$week <- schedules$season
schedules$season <- 1
}

if(!actual_schedule){
if (!actual_schedule) {
schedules <- ffs_build_schedules(
n_seasons = n,
n_weeks = 1,
Expand All @@ -185,13 +185,13 @@ ff_simulate_week <- function(conn,
vcli_start(msg = "Summarising Simulation Data")

summary_week <- ffs_summarise_week(optimal_scores, schedules)
summary_simulation <- ffs_summarise_inseason(summary_week,n)
summary_simulation <- ffs_summarise_inseason(summary_week, n)

vcli_end(msg_done = "Summarising Simulation Data...done! {Sys.time()}")

#### Build and Return ####

if(return == "default"){
if (return == "default") {

out <- structure(
list(
Expand All @@ -214,7 +214,7 @@ ff_simulate_week <- function(conn,
)
}

if(return == "all"){
if (return == "all") {

out <- structure(
list(
Expand Down
Loading
Loading