def fit_player_data(position, season, gameweek, model=ConjugatePlayerModel(), dbsession=session): """ fit the data for a particular position (FWD, MID, DEF) """ data = process_player_data(position, season, gameweek, dbsession) print("Fitting player model for", position, "...") model = fastcopy(model) fitted_model = model.fit(data) df = pd.DataFrame(fitted_model.get_probs()) df["pos"] = position df = (df.rename(columns={ "index": "player_id" }).sort_values("player_id").set_index("player_id")) return df
def apply_strategy(strat, tag, baseline_dict=None, num_iter=1, update_func_and_args=None, verbose=False): """ apply a set of transfers over a number of gameweeks, and total up the score, taking into account points hits. strat is a tuple, with the first element being the dictionary {gw:ntransfers,...} and the second element being the total points hit. """ sid = make_strategy_id(strat) starting_squad = get_starting_squad() if verbose: print("Trying strategy {}".format(strat)) best_score = 0 best_strategy_output = {} gameweeks = sorted(strat[0].keys()) # go through gameweeks in order if verbose: print(" --> doing strategy {}".format(sid)) strategy_output = { "total_score": -1 * strat[1], # points hit from this strategy "points_per_gw": {}, "players_in": {}, "players_out": {}, "cards_played": {}, } new_squad = fastcopy(starting_squad) ## If we use "free hit" card, we need to remember the team from the week before it squad_before_free_hit = None # determine if bench boost or triple captain used in this strategy bench_boost_gw = None triple_captain_gw = None for gw, n_transfers in strat[0].items(): if n_transfers in ["B0", "B1"]: bench_boost_gw = gw elif n_transfers in ["T0", "T1"]: triple_captain_gw = gw for igw, gw in enumerate(gameweeks): ## how many gameweeks ahead should we look at for the purpose of estimating points? gw_range = gameweeks[igw:] # range of gameweeks to end of window ## if we used a free hit in the previous gw, we will have stored the previous squad, so ## we go back to that one now. if squad_before_free_hit: new_squad = fastcopy(squad_before_free_hit) squad_before_free_hit = None ## process this gameweek if strat[0][gw] == 0: # no transfers that gameweek rp, ap = [], [] ## lists of removed-players, added-players elif strat[0][gw] == 1: # one transfer - choose optimum new_squad, rp, ap = make_optimum_transfer( new_squad, tag, gw_range, update_func_and_args=update_func_and_args, bench_boost_gw=bench_boost_gw, triple_captain_gw=triple_captain_gw, ) elif strat[0][gw] == 2: ## two transfers - choose optimum new_squad, rp, ap = make_optimum_double_transfer( new_squad, tag, gw_range, update_func_and_args=update_func_and_args, bench_boost_gw=bench_boost_gw, triple_captain_gw=triple_captain_gw, ) elif strat[0][gw] == "W": ## wildcard - a whole new squad! rp = [p.player_id for p in new_squad.players] budget = get_team_value(new_squad) new_squad = make_new_squad( budget, num_iter, tag, gw_range, update_func_and_args=update_func_and_args, bench_boost_gw=bench_boost_gw, triple_captain_gw=triple_captain_gw, ) ap = [p.player_id for p in new_squad.players] elif strat[0][gw] == "F": ## free hit - a whole new squad! ## remember the starting team (so we can revert to it later) squad_before_free_hit = fastcopy(new_squad) ## now make a new squad for this gw, as is done for wildcard new_team = [p.player_id for p in new_squad.players] budget = get_team_value(new_team) new_squad = make_new_squad( budget, num_iter, tag, [gw], # free hit should be optimised for this gameweek only update_func_and_args=update_func_and_args, bench_boost_gw=bench_boost_gw, triple_captain_gw=triple_captain_gw, ) ap = [p.player_id for p in new_squad.players] elif strat[0][gw] in ["B0", "T0" ]: # bench boost/triple captain and no transfer rp, ap = [], [] ## lists of removed-players, added-players elif strat[0][gw] in [ "B1", "T1", ]: # bench boost/triple captain and one transfer new_squad, rp, ap = make_optimum_transfer( new_squad, tag, gw_range, update_func_and_args=update_func_and_args, bench_boost_gw=bench_boost_gw, triple_captain_gw=triple_captain_gw, ) else: # choose randomly new_squad, rp, ap = make_random_transfers( new_squad, tag, strat[0][gw], gw_range, num_iter=num_iter, update_func_and_args=update_func_and_args, bench_boost_gw=bench_boost_gw, triple_captain_gw=triple_captain_gw, ) if gw == bench_boost_gw: score = new_squad.get_expected_points(gw, tag, bench_boost=True) elif gw == triple_captain_gw: score = new_squad.get_expected_points(gw, tag, triple_captain=True) else: score = new_squad.get_expected_points(gw, tag) ## if we're ever >5 points below the baseline, bail out! strategy_output["total_score"] += score if baseline_dict and baseline_dict[gw] - strategy_output[ "total_score"] > 5: break strategy_output["points_per_gw"][gw] = score # record whether we're playing a chip this gameweek # only first character to remove number transfers in case of bench boost or # triple captain ("B" and "T", not "B0", "B1", "T0", "T1") strategy_output["cards_played"][gw] = (strat[0][gw][0] if isinstance( strat[0][gw], str) else None) strategy_output["players_in"][gw] = ap strategy_output["players_out"][gw] = rp ## end of loop over gameweeks if strategy_output["total_score"] > best_score: best_score = strategy_output["total_score"] best_strategy_output = strategy_output if verbose: print("Total score: {}".format(best_strategy_output["total_score"])) return best_strategy_output
def make_optimum_double_transfer( squad, tag, gameweek_range=None, season=CURRENT_SEASON, update_func_and_args=None, verbose=False, bench_boost_gw=None, triple_captain_gw=None, ): """ If we want to just make two transfers, it's not unfeasible to try all possibilities in turn. We will order the list of potential subs via the sum of expected points over a specified range of gameweeks. """ if not gameweek_range: gameweek_range = [NEXT_GAMEWEEK] transfer_gw = min(gameweek_range) # the week we're making the transfer best_score = 0.0 best_pid_out, best_pid_in = 0, 0 ordered_player_lists = {} for pos in ["GK", "DEF", "MID", "FWD"]: ordered_player_lists[pos] = get_predicted_points( gameweek=gameweek_range, position=pos, tag=tag) for i in range(len(squad.players) - 1): positions_needed = [] pout_1 = squad.players[i] new_squad_remove_1 = fastcopy(squad) new_squad_remove_1.remove_player(pout_1.player_id, season=season, gameweek=transfer_gw) for j in range(i + 1, len(squad.players)): if update_func_and_args: ## call function to update progress bar. ## this was passed as a tuple (func, increment, pid) update_func_and_args[0](update_func_and_args[1], update_func_and_args[2]) pout_2 = squad.players[j] new_squad_remove_2 = fastcopy(new_squad_remove_1) new_squad_remove_2.remove_player(pout_2.player_id, season=season, gameweek=transfer_gw) if verbose: print("Removing players {} {}".format(i, j)) ## what positions do we need to fill? positions_needed = [pout_1.position, pout_2.position] # now loop over lists of players and add players back in for pin_1 in ordered_player_lists[positions_needed[0]]: if (pin_1[0].player_id == pout_1.player_id or pin_1[0].player_id == pout_2.player_id): continue ## no point in adding same player back in new_squad_add_1 = fastcopy(new_squad_remove_2) added_1_ok = new_squad_add_1.add_player(pin_1[0], season=season, gameweek=transfer_gw) if not added_1_ok: continue for pin_2 in ordered_player_lists[positions_needed[1]]: new_squad_add_2 = fastcopy(new_squad_add_1) if (pin_2[0] == pin_1[0] or pin_2[0].player_id == pout_1.player_id or pin_2[0].player_id == pout_2.player_id): continue ## no point in adding same player back in added_2_ok = new_squad_add_2.add_player( pin_2[0], season=season, gameweek=transfer_gw) if added_2_ok: # calculate the score total_points = 0.0 for gw in gameweek_range: if gw == bench_boost_gw: total_points += new_squad_add_2.get_expected_points( gw, tag, bench_boost=True) elif gw == triple_captain_gw: total_points += new_squad_add_2.get_expected_points( gw, tag, triple_captain=True) else: total_points += new_squad_add_2.get_expected_points( gw, tag) if total_points > best_score: best_score = total_points best_pid_out = [pout_1.player_id, pout_2.player_id] best_pid_in = [ pin_1[0].player_id, pin_2[0].player_id ] best_squad = new_squad_add_2 break return best_squad, best_pid_out, best_pid_in
def make_random_transfers( squad, tag, nsubs=1, gw_range=None, num_iter=1, update_func_and_args=None, season=CURRENT_SEASON, bench_boost_gw=None, triple_captain_gw=None, ): """ choose nsubs random players to sub out, and then select players using a triangular PDF to preferentially select the replacements with the best expected score to fill their place. Do this num_iter times and choose the best total score over gw_range gameweeks. """ best_score = 0.0 best_squad = None best_pid_out = [] best_pid_in = [] max_tries = 100 for i in range(num_iter): if update_func_and_args: ## call function to update progress bar. ## this was passed as a tuple (func, increment, pid) update_func_and_args[0](update_func_and_args[1], update_func_and_args[2]) new_squad = fastcopy(squad) if not gw_range: gw_range = [NEXT_GAMEWEEK] transfer_gw = min(gw_range) # the week we're making the transfer players_to_remove = [] # this is the index within the squad removed_players = [] # this is the player_ids ## order the players in the squad by predicted_points - least-to-most player_list = [] for p in squad.players: p.calc_predicted_points(tag) player_list.append( (p.player_id, p.predicted_points[tag][gw_range[0]])) player_list.sort(key=itemgetter(1), reverse=False) while len(players_to_remove) < nsubs: index = int(random.triangular(0, len(player_list), 0)) if not index in players_to_remove: players_to_remove.append(index) positions_needed = [] for p in players_to_remove: positions_needed.append(squad.players[p].position) removed_players.append(squad.players[p].player_id) new_squad.remove_player(removed_players[-1], season=season, gameweek=transfer_gw) budget = new_squad.budget predicted_points = {} for pos in set(positions_needed): predicted_points[pos] = get_predicted_points(position=pos, gameweek=gw_range, tag=tag) complete_squad = False added_players = [] attempt = 0 while not complete_squad: ## sample with a triangular PDF - preferentially select players near ## the start added_players = [] for pos in positions_needed: index = int(random.triangular(0, len(predicted_points[pos]), 0)) pid_to_add = predicted_points[pos][index][0] added_ok = new_squad.add_player(pid_to_add, season=season, gameweek=transfer_gw) if added_ok: added_players.append(pid_to_add) complete_squad = new_squad.is_complete() if not complete_squad: # try to avoid getting stuck in a loop attempt += 1 if attempt > max_tries: new_squad = fastcopy(squad) break # take those players out again. for ap in added_players: removed_ok = new_squad.remove_player(ap.player_id, season=season, gameweek=transfer_gw) if not removed_ok: print("Problem removing {}".format(ap.name)) added_players = [] ## calculate the score total_points = 0.0 for gw in gw_range: if gw == bench_boost_gw: total_points += new_squad.get_expected_points(gw, tag, bench_boost=True) elif gw == triple_captain_gw: total_points += new_squad.get_expected_points( gw, tag, triple_captain=True) else: total_points += new_squad.get_expected_points(gw, tag) if total_points > best_score: best_score = total_points best_pid_out = removed_players best_pid_in = [ap.player_id for ap in added_players] best_squad = new_squad ## end of loop over n_iter return best_squad, best_pid_out, best_pid_in
def make_optimum_transfer( squad, tag, gameweek_range=None, season=CURRENT_SEASON, update_func_and_args=None, bench_boost_gw=None, triple_captain_gw=None, ): """ If we want to just make one transfer, it's not unfeasible to try all possibilities in turn. We will order the list of potential transfers via the sum of expected points over a specified range of gameweeks. """ if not gameweek_range: gameweek_range = [NEXT_GAMEWEEK] transfer_gw = min(gameweek_range) # the week we're making the transfer best_score = 0.0 best_pid_out, best_pid_in = 0, 0 ordered_player_lists = {} for pos in ["GK", "DEF", "MID", "FWD"]: ordered_player_lists[pos] = get_predicted_points( gameweek=gameweek_range, position=pos, tag=tag) for p_out in squad.players: if update_func_and_args: ## call function to update progress bar. ## this was passed as a tuple (func, increment, pid) update_func_and_args[0](update_func_and_args[1], update_func_and_args[2]) new_squad = fastcopy(squad) position = p_out.position new_squad.remove_player(p_out.player_id, season=season, gameweek=transfer_gw) for p_in in ordered_player_lists[position]: if p_in[0].player_id == p_out.player_id: continue # no point in adding the same player back in added_ok = new_squad.add_player(p_in[0], season=season, gameweek=transfer_gw) if added_ok: break total_points = 0.0 for gw in gameweek_range: if gw == bench_boost_gw: total_points += new_squad.get_expected_points(gw, tag, bench_boost=True) elif gw == triple_captain_gw: total_points += new_squad.get_expected_points( gw, tag, triple_captain=True) else: total_points += new_squad.get_expected_points(gw, tag) if total_points > best_score: best_score = total_points best_pid_out = p_out.player_id best_pid_in = p_in[0].player_id best_squad = new_squad return best_squad, [best_pid_out], [best_pid_in]