File size: 6,991 Bytes
1f7470c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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]