Soccer-Data / stats_manager.py
2nzi's picture
start
1f7470c verified
import pandas as pd
def check_columns(df, required_columns):
"""
Vérifie si les colonnes requises sont présentes dans le DataFrame.
Parameters:
- df (pd.DataFrame): Le DataFrame à vérifier.
- required_columns (list): Liste des noms de colonnes requis.
Returns:
- bool: True si toutes les colonnes sont présentes, sinon False.
"""
return all(column in df.columns for column in required_columns)
class StatsManager:
def __init__(self, events):
self.events = events
def calculate_stat(self, required_columns, func):
"""
Vérifie si les colonnes nécessaires sont présentes, puis applique la fonction de calcul si elles sont présentes.
Parameters:
- required_columns (list): Liste des colonnes requises.
- func (callable): Fonction à appliquer si les colonnes sont présentes.
Returns:
- tuple: Les résultats de la fonction si les colonnes sont présentes, sinon (None, None).
"""
if check_columns(self.events, required_columns):
return func(self.events)
else:
return None, None
def get_possession(self):
return self.calculate_stat(['possession_team'], self._calculate_possession)
def _calculate_possession(self, events):
possession_counts = events['possession_team'].value_counts()
if len(possession_counts) < 2:
return None, None
total_possession = possession_counts.iloc[0] + possession_counts.iloc[1]
home_possession = round((possession_counts.iloc[0] / total_possession) * 100, 1)
away_possession = round((possession_counts.iloc[1] / total_possession) * 100, 1)
return home_possession, away_possession
def get_total_xg(self):
return self.calculate_stat(['shot_statsbomb_xg', 'team'], self._calculate_total_xg)
def _calculate_total_xg(self, events):
data = events[['shot_statsbomb_xg', 'team']].dropna(subset=['shot_statsbomb_xg'])
if data.empty:
return None, None
data = data.groupby('team')['shot_statsbomb_xg'].sum()
if len(data) < 2:
return None, None
return round(data.iloc[0], 2), round(data.iloc[1], 2)
def get_total_shots(self):
return self.calculate_stat(['shot_statsbomb_xg', 'team'], self._calculate_total_shots)
def _calculate_total_shots(self, events):
data = events[['shot_statsbomb_xg', 'team']].dropna(subset=['shot_statsbomb_xg'])
if data.empty:
return None, None
shot_counts = data.groupby('team').count()['shot_statsbomb_xg']
if len(shot_counts) < 2:
return None, None
return shot_counts.iloc[0], shot_counts.iloc[1]
def get_total_shots_off_target(self):
return self.calculate_stat(['shot_outcome', 'team'], self._calculate_total_shots_off_target)
def _calculate_total_shots_off_target(self, events):
off_target_outcomes = ['Off T', 'Blocked', 'Missed']
data = events[events['shot_outcome'].isin(off_target_outcomes)]
if data.empty:
return None, None
off_target_counts = data.groupby('team').size()
if len(off_target_counts) < 2:
return None, None
return off_target_counts.iloc[0], off_target_counts.iloc[1]
def get_total_shots_on_target(self):
return self.calculate_stat(['shot_outcome', 'team'], self._calculate_total_shots_on_target)
def _calculate_total_shots_on_target(self, events):
on_target_outcomes = ['Goal', 'Saved', 'Saved To Post', 'Shot Saved Off Target']
data = events[events['shot_outcome'].isin(on_target_outcomes)]
if data.empty:
return None, None
on_target_counts = data.groupby('team').size()
if len(on_target_counts) < 2:
return None, None
return on_target_counts.iloc[0], on_target_counts.iloc[1]
def get_total_passes(self):
return self.calculate_stat(['pass_end_location', 'team'], self._calculate_total_passes)
def _calculate_total_passes(self, events):
pass_counts = events.filter(regex='^pass_end_location|^team$').groupby('team').count()['pass_end_location']
if len(pass_counts) < 2:
return None, None
return pass_counts.iloc[0], pass_counts.iloc[1]
def get_successful_passes(self):
return self.calculate_stat(['pass_outcome', 'team'], self._calculate_successful_passes)
def _calculate_successful_passes(self, events):
home_passes, away_passes = self.get_total_passes()
unsuccessful_passes = events.filter(regex='^pass_outcome|^team$').groupby('team').count().reset_index()['pass_outcome']
if len(unsuccessful_passes) < 2:
return None, None
home_unsuccessful_passes = unsuccessful_passes.iloc[0]
away_unsuccessful_passes = unsuccessful_passes.iloc[1]
return home_passes - home_unsuccessful_passes, away_passes - away_unsuccessful_passes
def get_total_corners(self):
return self.calculate_stat(['pass_type', 'team'], self._calculate_total_corners)
def _calculate_total_corners(self, events):
corner_counts = events.filter(regex='^pass_type|^team$').query('pass_type == "Corner"').groupby('team').count()['pass_type']
if len(corner_counts) < 2:
return None, None
return corner_counts.iloc[0], corner_counts.iloc[1]
def get_total_fouls(self):
return self.calculate_stat(['type', 'team'], self._calculate_total_fouls)
def _calculate_total_fouls(self, events):
fouls = events[events['type'] == 'Foul Committed']
foul_counts = fouls.groupby('team').size()
if len(foul_counts) < 2:
return None, None
return foul_counts.iloc[0], foul_counts.iloc[1]
def get_total_yellow_cards(self):
return self.calculate_stat(['bad_behaviour_card', 'team'], self._calculate_total_yellow_cards)
def _calculate_total_yellow_cards(self, events):
yellow_card_counts = events.filter(regex='^bad_behaviour_card|^team$').query('bad_behaviour_card == "Yellow Card"').groupby('team').count()['bad_behaviour_card']
if len(yellow_card_counts) < 2:
return None, None
return yellow_card_counts.iloc[0], yellow_card_counts.iloc[1]
def get_total_red_cards(self):
return self.calculate_stat(['bad_behaviour_card', 'team'], self._calculate_total_red_cards)
def _calculate_total_red_cards(self, events):
red_card_counts = events.filter(regex='^bad_behaviour_card|^team$').query('bad_behaviour_card == "Red Card"').groupby('team').count()['bad_behaviour_card']
if len(red_card_counts) < 2:
return None, None
return red_card_counts.iloc[0], red_card_counts.iloc[1]