def first_team(self): if not self.squad_full: verbose_print("Squad not full, can't pick first team.") return df = self.selected df.sort_values(by=["score", "score_per_value", "value"], ascending=[False, False, True], inplace=True) first_team = pd.DataFrame() # First pick a keeper: keeper = df.loc[df["position"] == "GK", :].iloc[0].to_dict() first_team = first_team.append(keeper, ignore_index=True) df = df.loc[df["position"] != "GK"] # Then pick the rest of the team: max_picks = {"DEF": 5, "MID": 5, "FWD": 3} picked = {"DEF": 0, "MID": 0, "FWD": 0} for row in df.iterrows(): if len(first_team) == 11: break player = row[1].to_dict() if picked[player["position"]] < max_picks[player["position"]]: first_team = first_team.append(player, ignore_index=True) picked[player["position"]] += 1 # Can't have 5 midfielders AND 5 defenders: if picked["DEF"] == 5: max_picks["MID"] = 4 if picked["MID"] == 5: max_picks["DEF"] = 4 first_team["code"] = first_team["code"].astype(int) return first_team
def load_squad(self, filename): fp = os.path.join(DIR_SQUADS, f"{filename}.csv") assert os.path.isfile(fp), f"Squad file not found: {filename}" df = pd.read_csv(fp, encoding="utf-8") self.selected = df verbose_print( f"Squad of {len(self.selected)} players loaded from {fp}")
def add_player(self, code: int, year: int, week: int, live=False): """Add a player to the current squad.""" player = self.PlayerInformation.get_player(code, year, week, live) score = self.PlayerScorer.get_player_gw_score(code, year, week) player["score"] = score player["score_per_value"] = score / player["value"] self.Squad.add_player(**player) verbose_print(f"Player added to squad: {player['name']}")
def remove_player(self, code): if code not in list(self.selected["code"]): raise ValueError(f"code not in team: {code}") name = self.selected.loc[self.selected["code"] == code, "name"].values[0] self.selected = self.selected.loc[ self.selected["code"] != code].reset_index(drop=True) verbose_print( f"Removed player from squad ({len(self.selected)} remain): {name}")
def vice_captain(self): if not self.squad_full: verbose_print("Squad not full, can't pick vice-captain.") return else: df = self.selected df.sort_values(by=["score", "score_per_value", "value"], ascending=[False, False, True], inplace=True) vc = df.iloc[1] return {vc["code"]: vc["name"]}
def optimiser(self, year: int, week: int, iterations: int = 100, live: bool = False, r: int = 2, score_per_value: bool = False): """Optimise the currently selected squad by selecting r-length combinations of players in the squad, then comparing them against all r-length combinations of non-selected (available) players and seeing if substituting the combinations raises the overall team score.""" score_col = "score_per_value" if score_per_value else "score" increase = 0 original_score = self.Squad.total_score_per_val if score_per_value else self.Squad.total_score for i in range(iterations): optimisations = squad_optimisations(self, year, week, live=live, r=r, score_per_value=score_per_value) if not len(optimisations): new_score = self.Squad.total_score_per_val if score_per_value else self.Squad.total_score verbose_print("Can't optimise team anymore:" f"\n> Original total {score_col}: {original_score:,.3f}" f"\n> Raised {score_col} by total: {increase:,.3f}" f"\n> New total {score_col}: {new_score:,.3f}") return for row in optimisations.iterrows(): opt = row[1].to_dict() to_remove = opt["players_to_remove"] # Create a copy of the current team to try replacements: test = self._test_new_squad current_squad = self.Squad.selected.copy() test.selected = current_squad.loc[~current_squad["code"].isin(to_remove)] # Try to add each new player: try: for p in range(r): add_player = dict(code=int(opt[f"p{p}"])) add_player["name"] = self.PlayerInformation.player_names(live=live)[add_player["code"]] add_player["position"] = opt[f"p{p}_position"] add_player["value"] = opt[f"p{p}_val"] add_player["score"] = opt[f"p{p}_score"] add_player["score_per_value"] = opt[f"p{p}_score_per_val"] add_player["team"] = self.PlayerInformation.player_teams(year, live=live)[add_player["code"]] test.add_player(**add_player) if score_per_value: added = (test.total_score_per_val - self.Squad.total_score_per_val) else: added = (test.total_score - self.Squad.total_score) verbose_print(f"Increased {score_col} by: {added}") increase += added self.Squad.selected = test.selected break except (AlreadySelected, MaxedOutTeam): continue
def total_first_team_value(self): if not self.squad_full: verbose_print("Squad not full.") return return self.first_team["value"].sum()
def save_squad(self, filename): fp = os.path.join(DIR_SQUADS, f"{filename}.csv") self.selected.to_csv(fp, encoding="utf-8", index=False) verbose_print(f"Squad of {len(self.selected)} players saved at: {fp}")
def get_player_pool(self, year, week, live=False, position=None, min_score=None, min_score_per_value=None, max_val=None, drop_unavailable=True, drop_selected=True, drop_maxed_out=True): """Get a pool of players to select from which meet the criteria.""" df = self.players(year, week, live) n = len(df) if position: position = [position] if isinstance(position, str) else position assert all([p in ["GK", "DEF", "MID", "FWD"] for p in position]), \ f"invalid position(s): {', '.join(position)}" df = df.loc[df["position"].isin(position)] verbose_print(f"Dropped {n - len(df):,} rows for `position`") n = len(df) if isinstance(min_score, Number): df = df.loc[df[self.scoring_metric] > min_score] verbose_print(f"Dropped {n - len(df):,} rows for `min_score`") n = len(df) if isinstance(min_score_per_value, Number): df = df.loc[df[self.val_metric] > min_score_per_value] verbose_print(f"Dropped {n - len(df):,} rows for `min_score_per_value`") n = len(df) if isinstance(max_val, Number): df = df.loc[df["value"] <= max_val] verbose_print(f"Dropped {n - len(df):,} rows for `max_val`") n = len(df) if isinstance(self.min_minute_percent, Number): df = df.loc[df["minutes_percent"] >= self.min_minute_percent] verbose_print(f"Dropped {n - len(df):,} rows for `self.min_minute_percent`") n = len(df) if drop_unavailable: available = self.player_availability(year, week, live, self.percent_chance) df["available"] = df.index.map(available) # Drop False available (or NaN - assumed to have left league): df["available"].fillna(False, inplace=True) df = df.loc[df["available"]] verbose_print(f"Dropped {n - len(df):,} rows for `drop_unavailable`") n = len(df) # Remove already selected players: if drop_selected: df = df.loc[~df.index.isin(self.Squad.selected_list)] verbose_print(f"Dropped {n - len(df):,} rows for `drop_selected`") n = len(df) # Remove players from teams already maxed out: if drop_maxed_out: if len(self.Squad.maxed_out_teams): df = df.loc[~df["team"].isin(self.Squad.maxed_out_teams)] verbose_print(f"Dropped {n - len(df):,} rows for `drop_maxed_out`") n = len(df) # Rename scoring columns to generic names: df.rename(columns={self.scoring_metric: "score", self.val_metric: "score_per_value"}, inplace=True) # Drop players with NaNs in these columns - they have left the league: df.dropna(subset=["position", "value", "team", "score_per_value"], inplace=True) return df