[PYTHON] J'ai essayé d'analyser les données du tournoi de football de la Coupe du monde de football en Russie avec l'action de football

** Ceci est l'article du 18ème jour du CALENDRIER DE L'AVENT du Département Innovation Service de NTT Docomo. ** **

salut! Voici Osugi de NTT Docomo.

J'ai passé mes journées d'école à jouer au football et au futsal, et maintenant je fais du travail d'analyse de données liées au marketing.

Aujourd'hui, je voudrais présenter socceraction, un package python lié au football, tout en analysant les données de match lors du tournoi de la Coupe du Monde de la FIFA, Russie qui s'est tenu en 2018.

introduction

socceraction est présenté dans ** «Les actions parlent plus fort que les objectifs: valoriser les actions des joueurs dans le football» ** [^ 1], qui a remporté le prix du meilleur article du KDD2019 Appried Data Sciense Track.

[^1 ]: Decroos, Tom, et al. "Actions speak louder than goals: Valuing player actions in soccer." Proceedings of the 25th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining. ACM, 2019.

Cet article propose un nouvel index pour évaluer le comportement des joueurs de football lors d'un match, et a spécifiquement le contenu suivant.

--Définition de ** SPADL (Soccer Player Action Description Language) ** qui représente les actions des joueurs pendant un match --Définition de ** VAEP (Valuing Actions by Estimating Probabilities) **, qui est un cadre pour l'évaluation des joueurs --Prédiction du score / probabilité de but lors de l'attaque / de la défense --Résultats et considérations issus d'analyses utilisant les données des principales ligues européennes de 2012/2013 à 2017/2018

Et à propos de l'action de football, vous pouvez effectuer les opérations suivantes en utilisant ce package.

  1. ** Convertissez les données de correspondance en SPADL **
  2. ** Calcul de la probabilité de succès lors de l'attaque / de la défense de chaque action **
  3. ** Calcul de VAEP en utilisant la probabilité de succès dans chaque action **

En d'autres termes, socceraction permet d'essayer facilement les méthodes analytiques décrites dans l'article. Vous pouvez également effectuer une série d'analyses en vous référant aux blocs-notes publics publiés sur github. Cette fois, je voudrais toucher les données basées sur ce cahier public.

Le code utilisé dans cet article est publié à la fin de cet article, j'espère donc que tout le monde l'essayera.

En fait, analyser avec l'action de football

Vous pouvez installer l'action de football avec pip comme suit.

pip install socceraction

1. Téléchargez les données

--Référence

1.1 À propos des données utilisées cette fois

Dans l'article, les données de Wyscout sont utilisées, mais dans le cahier public de l'action de football, les données sont obtenues à partir de StatsBomb. Je fais. Dans socceraction, vous pouvez également traiter les données Opta dans SPADL.

Cette fois, j'utilise également les données de StatsBomb.

StatsBomb Logo [Source de l'image]: https://github.com/statsbomb/open-data/blob/master/README.md

Les données ouvertes ont été publiées [^ 3] sur StatsBomb, et depuis le 17 décembre 2019, les données des tournois suivants ont été publiées.

Si vous le regardez comme ça, vous pouvez voir qu'il existe une multitude de données sur la Liga (Liga Espanola: Espagne). Cette fois, nous utiliserons les données de la Coupe du monde de Russie 2018, à laquelle l'équipe nationale japonaise a également participé.

1.2 Comment obtenir des données

Fichier zip comme dans 1-download-statsbomb-data.ipynb Les données peuvent être obtenues par acquisition / expansion. Cette fois, afin de convertir en SPADL et de calculer VAEP avec précision, je l'ai également obtenu en référence au cahier ci-dessus.

Au fait, une autre façon d'obtenir les données StatsBomb est d'utiliser le package python statsbomb.

Vous pouvez également obtenir les données StatsBomb avec le code ci-dessous.

# https://pypi.org/project/statsbomb/
import statsbomb as sb 

# Competitions
comps = sb.Competitions()
comps_df = comps.get_dataframe() #Liste des tournois

# Matches(FIFA World Cup : competition_id(event_id) = 43, session_id = 3)
matches = sb.Matches(event_id='43', season_id='3')
matches_df = matches.get_dataframe() #Liste de match

# Events(Japan VS Belgium : event_id = '7584')
# event_Les détails du type sont liés ci-dessous
# https://github.com/imrankhan17/statsbomb-parser/blob/master/statsbomb/events.yaml
events = sb.Events(event_id='7584')
events.get_dataframe(event_type='substitution') #Données au moment du changement de joueur

2. Convertissez les données en SPADL

--Référence

2.1 À propos du format SPADL

Le format de SPADL (Soccer Player Action Description Language) est le suivant.

Dans le bloc-notes de référence, le code permettant de modifier les données StatsBomb au format SPADL ci-dessus est écrit et vous pouvez facilement le convertir en SPADL en vous référant à cela. Ici, la procédure est la suivante.

  1. Convertissez StatsBomb JSON en fichier au format HDF5 avec socceraction.spadl.api.statsbombjson_to_statsbombh5 (statsbomb_json, statsbomb_h5)
  2. Convertissez les fichiers StatsBomb au format SPADL avec statsbombh5_to_spadlh5 (statsbomb_h5, spadl_h5)

Vous pouvez également tracer les données au format SPADL comme suit en utilisant matplotsoccer.actions dans le package python ** matplotsoccer **.

image.png

Ici, j'ai tracé une scène [^ 9] du match Japon-Belgique, qui serait très impressionnante pour les fans de football japonais. Vous pouvez voir qui joue quand et quel genre de jeu avec des figures et des tables.

[^ 9]: scène du troisième point de l'équipe nationale belge

3. Prédiction de la probabilité par modèle

--Référence

3.1 Créer un modèle prédictif

Ensuite, créez un montant de fonctionnalité basé sur SPADL et trouvez la probabilité de score / objectif en attaque / défense. Cette fois, j'ai essayé de créer un modèle de prédiction en incluant la lecture précédente dans le montant de la fonctionnalité. Les quantités de caractéristiques utilisées sont les suivantes. Il existe deux types de cela, la dernière pièce et la pièce précédente.

Cette fonction et la variable objective peuvent être dérivées en utilisant «socceraction.classification.features» et «socceraction.classification.labels».

À l'aide de ces fonctionnalités, nous avons exécuté un modèle de prédiction avec xgboost et confirmé la précision de la prédiction. Ensuite, la précision de prédiction du modèle de prédiction créé cette fois est la suivante.

Scores Concedes
brier_score_loss 0.0092 0.0025
AUC 0.8512 0.8865

3.2 Confirmation des fonctionnalités importantes par SHAP

Nous avons également utilisé SHAP [^ 5] [^ 6] pour voir comment les fonctionnalités contribuent au modèle de prédiction. Regardons le montant de la fonctionnalité qui contribue à la probabilité de score avec summary_plot.

[^5 ]: Lundberg, Scott M., and Su-In Lee. "A unified approach to interpreting model predictions." Advances in Neural Information Processing Systems. 2017. [^6 ]: Lundberg, Scott M., et al. "Explainable AI for Trees: From Local Explanations to Global Understanding." arXiv preprint arXiv:1905.04610 (2019).

image.png

Avec SHAP, vous pouvez vérifier visuellement comment le montant de la fonction affecte la variable objectif.

Vous pouvez également examiner de plus près chaque variable si vous en trouvez une qui vous intéresse. Dans le dependency_plot ci-dessous, nous voyons comment la distance au but pendant l'action contribue à la probabilité de score. image.png Puisque l'axe horizontal est la distance de l'objectif à la fin de l'action et l'axe vertical est la valeur SHAP, ici vous pouvez voir que plus la distance après l'action est courte, plus la probabilité de score est élevée.

4. Calcul du VAEP

VAEP (Valuing Actions by Estimating Probabilities) sera calculé sur la base de la probabilité de notation / probabilité de concéder par le modèle de prédiction calculé en 3. La VAEP de l'action $ a_i $ de l'équipe $ x $ est calculée comme suit:

V(a_i,x) = \Delta P_{scores}(a_i,x) + (- \Delta P_{concedes}(a_i,x))

En ce moment, $ \ Delta P_ {scores} (a_i, x) $ signifie l'augmentation de la probabilité de score due à l'action, et $ \ Delta P_ {concède} (a_i, x) $ signifie l'augmentation de la probabilité de but due à l'action. Je vais.

En d'autres termes, la VAEP sera plus élevée pour les actions qui (1) augmentent la probabilité de notation et (2) diminuent la probabilité de concéder.

Calculez en fait VAEP en utilisant l'action de football. Ici, il peut être calculé en utilisant socceraction.vaep.value (). En conséquence de la disposition par ordre décroissant du VAEP total, les résultats suivants ont été obtenus.

image.png

Le résultat était que l'équipe nationale française gagnante, Embape, avait le VAEP total le plus élevé.

Dans ce qui précède, j'ai essayé de sortir le total de VAEP, mais ce résultat seul ne tient pas compte du temps de lecture. Par conséquent, le VAEP par 90 minutes est calculé en faisant la moyenne du temps de lecture tel que pratiqué dans l'article. De plus, comme condition, nous nous limitons uniquement aux joueurs qui ont participé pendant 180 minutes ou plus.

image.png

En regardant le VAEP par 90 minutes, le représentant de la Russie Dennis Chelishev est arrivé en premier. Chelishev a été actif en marquant 4 buts sur 5 matchs, mais parce qu'il y a eu de nombreuses apparitions en milieu de match et des changements à mi-parcours En regardant le VAEP par 90 minutes, il semble que le classement soit remonté à la 1ère place. Il était également intéressant de noter que Toni Claus, l'équipe nationale allemande, qui avait été éliminée de la ligue de groupe lors de ce tournoi, était au premier rang.

En plus de cela, en émettant un VAEP moyen par jeu, il peut être possible d'extraire des joueurs qui font du bon travail même si le nombre de jeux est faible.

5. Visualisation des résultats pour chaque scène

Ajoutons le VAEP calculé à l'intrigue par matplotsoccer introduit précédemment. image.png Cela permet de quantifier et d'évaluer le comportement de chaque joueur dans la scène cible. En regardant ce chiffre, à l'exception du tir et de l'assistance, la passe de Debruine est la plus cotée.

Résumé

Cette fois, nous avons introduit l'action de football en utilisant les données réelles du tournoi de la Coupe du monde de football en Russie organisé en 2018. Franchement, j'ai trouvé très pratique de pouvoir convertir plusieurs sources de données telles que StatsBomb et Wyscout en SPADL. De plus, les données de StatsBomb sont très détaillées et j'ai senti qu'elles pourraient être utilisées pour diverses analyses. (Merci de pouvoir l'utiliser gratuitement ...) Le cahier public sur le github d'action de football est également traité au format HDF5, et je ne l'utilise généralement pas beaucoup, alors je l'ai appris. Et surtout, j'ai trouvé très intéressant de pouvoir analyser les données de match réelles de cette manière! Si vous êtes intéressé par l'analyse du sport et du football comme moi, essayez le football.

Référence: Code utilisé cette fois

1. Acquisition de données et visualisation des scènes de scoring

# ----
#Public référencé-licence MIT notebook
# (c) 2019 KU Leuven Machine Learning Research Group
# Released under the MIT license.
# see https://github.com/ML-KULeuven/socceraction/blob/master/LICENSE
# ----

# package
%load_ext autoreload
%autoreload 2
import os; import sys;
import tqdm
import requests
import math
import zipfile
import warnings
import pandas as pd
warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning)
import socceraction.spadl.api as spadl
import matplotsoccer
import matplotlib

#Nom de dossier/Spécifiez le nom du fichier
datafolder = "hogehoge" #Spécifiez le nom du dossier
statsbombzip = os.path.join(datafolder, "open-data-master.zip")
statsbombroot = os.path.join(datafolder, "statsbomb-root")
statsbombdata = os.path.join(datafolder, "statsbomb-root", "open-data-master", "data")
#Extraire le fichier zip
with zipfile.ZipFile(statsbombzip, 'r') as zipObj:
    zipObj.extractall(statsbombroot)

# StatsBomb(json)Données de SPADL(HDF5)Convertir en
## StatsBomb(Raw Data) : json -> StatsBomb(Raw Data) : h5
statsbomb_json =  os.path.join(datafolder,"statsbomb-root","open-data-master","data")
statsbomb_h5 = os.path.join(datafolder,"statsbomb.h5")
spadl_h5 = os.path.join(datafolder,"spadl-statsbomb.h5")
spadl.statsbombjson_to_statsbombh5(statsbomb_json,statsbomb_h5)
tablenames = ["matches","players","teams","competitions"]
tables = {name : pd.read_hdf(statsbomb_h5,key=name) for name in tablenames}
match_id = tables["matches"].match_id[0]
tables["events"] = pd.read_hdf(statsbomb_h5,f"events/match_{match_id}")
for k,df in tables.items():
    print("#",k)
    print(df.columns,"\n")
## StatsBomb(Raw Data) : h5 -> SPADL : h5
spadl.statsbombh5_to_spadlh5(statsbomb_h5,spadl_h5)
tablenames = ["games","players","teams","competitions","actiontypes","bodyparts","results"]
tables = {name : pd.read_hdf(spadl_h5,key=name) for name in tablenames}
game_id = tables["games"].game_id[0]
tables["actions"] = pd.read_hdf(spadl_h5,f"actions/game_{game_id}")
for k,df in tables.items():
    print("#",k)
    print(df.columns,"\n")

#Coupe du monde Fifa:Visualisez le match entre le Japon et la Belgique

## game_Extraction de l'identifiant
tablenames = ["games","players","teams","competitions","actiontypes","bodyparts","results"]
tables = {name: pd.read_hdf(spadl_h5, key=name) for name in tablenames}
games = tables["games"].merge(tables["competitions"])
game_id = games[(games.competition_name == "FIFA World Cup") 
              & (games.away_team_name == "Japan")
              & (games.home_team_name == "Belgium")].game_id.values[0]
game_id # 7584

##Action liée à la notation_Extraction de l'identifiant
actions = pd.read_hdf(spadl_h5, f"actions/game_{game_id}")
actions = (
    actions.merge(tables["actiontypes"])
    .merge(tables["results"])
    .merge(tables["bodyparts"])
    .merge(tables["players"],"left",on="player_id")
    .merge(tables["teams"],"left",on="team_id")
    .sort_values(["period_id", "time_seconds", "timestamp"])
    .reset_index(drop=True))
actions["player"] = actions[["player_nickname",
                             "player_name"]].apply(lambda x: x[0] if x[0] else x[1],axis=1)
list(actions[(actions.type_name=='shot')&(actions.result_name=='success')].index)
# [1215, 1334, 1658, 1742, 2153]

##Belgique 3e point
shot = 2153
a = actions[shot-8:shot+1]
games = tables["games"]
g = list(games[games.game_id == a.game_id.values[0]].itertuples())[0]
minute = int((a.period_id.values[0]-1)*45 +a.time_seconds.values[0] // 60) + 1
game_info = f"{g.match_date} {g.home_team_name} {g.home_score}-{g.away_score} {g.away_team_name} {minute}'"
print(game_info)
labels = a[["time_seconds", "type_name", "player", "team_name"]]
matplotsoccer.actions(
    location=a[["start_x", "start_y", "end_x", "end_y"]],
    action_type=a.type_name,
    team= a.team_name,
    result= a.result_name == "success",
    label=labels,
    labeltitle=["time","actiontype","player","team"],
    zoom=False,
    figsize=6)

2. Création d'entités, création de modèles de prédiction, calcul SHAP

# ----
#Public référencé-licence MIT notebook
# (c) 2019 KU Leuven Machine Learning Research Group
# Released under the MIT license.
# see https://github.com/ML-KULeuven/socceraction/blob/master/LICENSE
# ----

# package
%load_ext autoreload
%autoreload 2
import os; import sys; sys.path.insert(0,'hogehoge')#Nom de dossier
import pandas as pd
import tqdm
import warnings
warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning)
import socceraction.classification.features as fs
import socceraction.classification.labels as lab
import xgboost
from sklearn.metrics import roc_auc_score,brier_score_loss
import shap
shap.initjs()

#Définition du nom du fichier et du nom du dossier
datafolder = "hogehoge" #Spécifiez le nom du dossier
spadl_h5 = os.path.join(datafolder,"spadl-statsbomb.h5")
features_h5 = os.path.join(datafolder,"features.h5")
labels_h5 = os.path.join(datafolder,"labels.h5")
predictions_h5 = os.path.join(datafolder,"predictions.h5")

#Lecture des données
games = pd.read_hdf(spadl_h5,"games")
games = games[games.competition_name == "FIFA World Cup"]
print("nb of games:", len(games))

actiontypes = pd.read_hdf(spadl_h5, "actiontypes")
bodyparts = pd.read_hdf(spadl_h5, "bodyparts")
results = pd.read_hdf(spadl_h5, "results")

#Créer une étiquette
yfns = [lab.scores,lab.concedes,lab.goal_from_shot]
for game in tqdm.tqdm(list(games.itertuples()),
                      desc=f"Computing and storing labels in {labels_h5}"):
    actions = pd.read_hdf(spadl_h5,f"actions/game_{game.game_id}")
    actions = (
        actions.merge(actiontypes,how="left")
        .merge(results,how="left")
        .merge(bodyparts,how="left")
        .sort_values(["period_id", "time_seconds", "timestamp",'action_id'])
        .reset_index(drop=True))
    Y = pd.concat([fn(actions) for fn in yfns],axis=1)
    Y.to_hdf(labels_h5,f"game_{game.game_id}")

#Création de quantité de fonctionnalités

xfns = [fs.actiontype,
       fs.actiontype_onehot,
       fs.bodypart,
       fs.bodypart_onehot,
       fs.result,
       fs.result_onehot,
       fs.goalscore,
       fs.startlocation,
       fs.endlocation,
       fs.movement,
       fs.space_delta,
       fs.startpolar,
       fs.endpolar,
       fs.team,
       fs.time,
       fs.time_delta]

for game in tqdm.tqdm(list(games.itertuples()),
                      desc=f"Generating and storing features in {features_h5}"):
    actions = pd.read_hdf(spadl_h5,f"actions/game_{game.game_id}")
    actions = (
        actions.merge(actiontypes,how="left")
        .merge(results,how="left")
        .merge(bodyparts,how="left")
        .sort_values(["period_id", "time_seconds", "timestamp",'action_id'])
        .reset_index(drop=True))
    gamestates = fs.gamestates(actions,2)
    gamestates = fs.play_left_to_right(gamestates,game.home_team_id)
    
    X = pd.concat([fn(gamestates) for fn in xfns],axis=1)
    X.to_hdf(features_h5,f"game_{game.game_id}")

xfns = [fs.actiontype_onehot,
       fs.bodypart_onehot,
       fs.result,
       fs.goalscore,
       fs.startlocation,
       fs.endlocation,
       fs.movement,
       fs.space_delta,
       fs.startpolar,
       fs.endpolar,
       fs.team,
       fs.time_delta]
nb_prev_actions = 2

Xcols = fs.feature_column_names(xfns,nb_prev_actions)
X = []
for game_id in tqdm.tqdm(games.game_id,desc="selecting features"):
    Xi = pd.read_hdf(features_h5,f"game_{game_id}")
    X.append(Xi[Xcols])
X = pd.concat(X)

Ycols = ["scores","concedes"]
Y = []
for game_id in tqdm.tqdm(games.game_id,desc="selecting label"):
    Yi = pd.read_hdf(labels_h5,f"game_{game_id}")
    Y.append(Yi[Ycols])
Y = pd.concat(Y)
print("X:", list(X.columns))
print("Y:", list(Y.columns))

#Construction de modèles prédictifs par xgboost

%%time
# scores
model_scores = xgboost.XGBClassifier()
model_scores.fit(X,Y['scores'])
# concedes
model_concedes = xgboost.XGBClassifier()
model_concedes.fit(X,Y['concedes'])

Y_hat = pd.DataFrame()
Y_hat['scores'] = model_scores.predict_proba(X)[:,1]
Y_hat['concedes'] = model_concedes.predict_proba(X)[:,1]

#Précision de la prédiction
print(f"scores_brier : \t\t{brier_score_loss(Y['scores'],Y_hat['scores']).round(4)}")
print(f"concedes_brier : \t{brier_score_loss(Y['concedes'],Y_hat['concedes']).round(4)}")

print(f"scores_auc : \t\t{roc_auc_score(Y['scores'],Y_hat['scores']).round(4)}")
print(f"concedes_auc : \t{roc_auc_score(Y['concedes'],Y_hat['concedes']).round(4)}")

#Identification des facteurs prédictifs à l'aide du SHAP(scores)
explainer_scores = shap.TreeExplainer(model_scores)
shap_scores = explainer_scores.shap_values(X)
## summary_plot
shap.summary_plot(shap_scores,features=X,feature_names=X.columns)
## dependence_plot
shap.dependence_plot('end_dist_to_goal_a0',
                     shap_scores,
                     features=X,
                     feature_names=X.columns,
                     interaction_index='end_dist_to_goal_a0')

#Sauvegarde des résultats de prédiction
A = []
for game_id in tqdm.tqdm(games.game_id,"loading game ids"):
    Ai = pd.read_hdf(spadl_h5,f"actions/game_{game_id}")
    A.append(Ai[["game_id"]])
A = pd.concat(A)
A = A.reset_index(drop=True)

grouped_predictions = pd.concat([A,Y_hat],axis=1).groupby("game_id")
for k,df in tqdm.tqdm(grouped_predictions,desc="saving predictions per game"):
    df = df.reset_index(drop=True)
    df[Y_hat.columns].to_hdf(predictions_h5,f"game_{int(k)}")

3. Calcul du VAEP

# ----
#Public référencé-licence MIT notebook
# (c) 2019 KU Leuven Machine Learning Research Group
# Released under the MIT license.
# see https://github.com/ML-KULeuven/socceraction/blob/master/LICENSE
# ----

# package
%load_ext autoreload
%autoreload 2
import os; import sys; sys.path.insert(0,'hogehoge') #Nom de dossier
import pandas as pd
import tqdm
import warnings
warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning)
import socceraction.vaep as vaep
import matplotsoccer
import matplotlib

#Définition du nom du fichier et du nom du dossier
datafolder = "hogehoge" #Nom de dossier
spadl_h5 = os.path.join(datafolder,"spadl-statsbomb.h5")
predictions_h5 = os.path.join(datafolder,"predictions.h5")

#Obtenez des données
games = pd.read_hdf(spadl_h5,"games")
games = games[games.competition_name == "FIFA World Cup"]
print("nb of games:", len(games))

players = pd.read_hdf(spadl_h5,"players")
teams = pd.read_hdf(spadl_h5,"teams")
actiontypes = pd.read_hdf(spadl_h5, "actiontypes")
bodyparts = pd.read_hdf(spadl_h5, "bodyparts")
results = pd.read_hdf(spadl_h5, "results")

#Calcul de VAEP
A = []
for game in tqdm.tqdm(list(games.itertuples())):
    actions = pd.read_hdf(spadl_h5,f"actions/game_{game.game_id}")
    actions = (
        actions.merge(actiontypes)
        .merge(results)
        .merge(bodyparts)
        .merge(players,"left",on="player_id")
        .merge(teams,"left",on="team_id")
        .sort_values(["period_id", "time_seconds", "timestamp"])
        .reset_index(drop=True)
    )
    preds = pd.read_hdf(predictions_h5,f"game_{game.game_id}")
    values = vaep.value(actions,preds.scores,preds.concedes)
    A.append(pd.concat([actions,preds,values],axis=1))
A = pd.concat(A).sort_values(["game_id","period_id", "time_seconds", "timestamp"]).reset_index(drop=True)
A.columns
A["player"] = A[["player_nickname",
                 "player_name"]].apply(lambda x: x[0] if x[0] else x[1],axis=1)

#Calculez le VAEP total de chaque joueur et vérifiez par ordre décroissant
summary = A.groupby(['player',
                     'team_name',
                     'player'])[['offensive_value',
                                 'defensive_value',
                                 'vaep_value']].sum().reset_index()

summary.sort_values('vaep_value',ascending = False).head(10)

#Calculez le VAEP moyen par 90 minutes et vérifiez par ordre décroissant
players = A_[["player_id",
              "team_name",
              "player",
              "vaep_value",
              "count"]].groupby(["player_id",
                                "team_name",
                                "player"]).sum().reset_index()
players = players.sort_values("vaep_value",ascending=False)

pg = pd.read_hdf(spadl_h5,"player_games")
pg = pg[pg.game_id.isin(games.game_id)]
mp = pg[["player_id","minutes_played"]].groupby("player_id").sum().reset_index()
stats = players.merge(mp)
stats = stats[stats.minutes_played > 180]
stats["vaep_rating"] = stats.vaep_value * 90 / stats.minutes_played

stats.sort_values("vaep_rating",ascending=False).head(10)

#Visualisation par matplotsoccer

##Extraction de scènes avec des objectifs
shot_goal_index = A[(A.game_id == 7584)&A.type_name.str.contains("shot")&(A.result_name=='success')]

##La troisième visualisation de la Belgique
def get_time(period_id,time_seconds):
    m = int((period_id-1)*45 + time_seconds // 60)
    s = time_seconds % 60
    if s == int(s):
        s = int(s)
    return f"{m}m{s}s"

###Extraction de scènes
a = A.iloc[shot_goal_index.index[4]-6:shot_goal_index.index[4]+1,:].sort_values('action_id')
a["player"] = a[["player_nickname",
                 "player_name"]].apply(lambda x: x[0] if x[0] else x[1],axis=1)

###Informations sur le match
g = list(games[games.game_id == a.game_id.values[0]].itertuples())[0]
game_info = f"{g.match_date} {g.home_team_name} {g.home_score}-{g.away_score} {g.away_team_name}"
minute = get_time(int(a[a.index == a.index[-1]].period_id),int(a[a.index == a.index[-1]].time_seconds))
print(f"{game_info} {minute}' {a[a.index == a.index[-1]].type_name.values[0]} {a[a.index == a.index[-1]].player_name.values[0]}")

###Mise en forme des données
a["scores"] = a.scores.apply(lambda x : "%.3f" % x )
a["vaep_value"] = a.vaep_value.apply(lambda x : "%.3f" % x )
a["time"] = a[["period_id","time_seconds"]].apply(lambda x: get_time(*x),axis=1)
cols = ["time","type_name","player","team_name","scores","vaep_value"]

###terrain
matplotsoccer.actions(a[["start_x","start_y","end_x","end_y"]],
                      a.type_name,
                      team=a.team_name,
                      result = a.result_name == "success",
                      label=a[cols],
                      labeltitle = cols,
                      zoom=False)

Recommended Posts

J'ai essayé d'analyser les données du tournoi de football de la Coupe du monde de football en Russie avec l'action de football
J'ai essayé de sauvegarder les données avec discorde
J'ai essayé d'analyser la négativité de Nono Morikubo. [Comparer avec Posipa]
J'ai essayé d'obtenir et d'analyser les données statistiques de la nouvelle Corona avec Python: données de l'Université John's Hopkins
J'ai essayé de visualiser les données de course du jeu de course (Assetto Corsa) avec Plotly
J'ai essayé de trouver l'entropie de l'image avec python
J'ai essayé d'analyser les émotions de tout le roman "Weather Child" ☔️
J'ai essayé de trouver la moyenne de plusieurs colonnes avec TensorFlow
J'en ai marre de Python, alors j'ai essayé d'analyser les données avec nehan (je veux aller vivre même avec Corona) -Partie 2)
J'en ai marre de Python, alors j'ai essayé d'analyser les données avec nehan (je veux aller vivre même avec Corona) -Partie 1)
J'ai essayé d'afficher les données du groupe de points DB de la préfecture de Shizuoka avec Vue + Leaflet
J'ai essayé d'automatiser l'arrosage du pot avec Raspberry Pi
[Pandas] J'ai essayé d'analyser les données de ventes avec Python [Pour les débutants]
J'ai essayé d'agrandir la taille du volume logique avec LVM
J'ai utilisé la commande coupe du monde pour vérifier le résultat de la Coupe du monde.
J'ai essayé d'améliorer l'efficacité du travail quotidien avec Python
J'ai essayé d'obtenir le code d'authentification de l'API Qiita avec Python.
J'ai essayé d'extraire automatiquement les mouvements des joueurs Wiire avec un logiciel
(Python) J'ai essayé d'analyser 1 million de mains ~ J'ai essayé d'estimer le nombre d'AA ~
J'ai essayé de vérifier et d'analyser l'accélération de Python par Cython
J'ai essayé de rationaliser le rôle standard des nouveaux employés avec Python
J'ai essayé de visualiser le texte du roman "Weather Child" avec Word Cloud
J'ai essayé d'obtenir les informations sur le film de l'API TMDb avec Python
J'ai essayé de prédire le comportement du nouveau virus corona avec le modèle SEIR.
J'ai essayé Web Scraping pour analyser les paroles.
J'ai essayé d'obtenir des données CloudWatch avec Python
J'ai essayé de corriger la forme trapézoïdale de l'image
Qiita Job J'ai essayé d'analyser le travail
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
J'ai essayé de visualiser facilement les tweets de JAWS DAYS 2017 avec Python + ELK
J'ai essayé de récupérer les données de l'ordinateur portable en le démarrant sur Ubuntu
L'histoire de la fabrication de soracom_exporter (j'ai essayé de surveiller SORACOM Air avec Prometheus)
J'ai essayé de créer un modèle avec l'exemple d'Amazon SageMaker Autopilot
J'ai essayé d'envoyer automatiquement la littérature du nouveau virus corona à LINE avec Python
J'ai essayé d'extraire des fonctionnalités avec SIFT d'OpenCV
J'ai essayé de résumer la forme de base de GPLVM
J'ai essayé de toucher un fichier CSV avec Python
J'ai essayé de résoudre Soma Cube avec python
J'ai essayé d'utiliser l'API de Sakenowa Data Project
J'ai essayé de visualiser les informations spacha de VTuber
J'ai essayé d'effacer la partie négative de Meros
J'ai essayé de résoudre le problème avec Python Vol.1
J'ai essayé de classer les voix des acteurs de la voix
J'ai essayé de résumer les opérations de chaîne de Python
J'ai essayé d'automatiser la mise à jour de l'article du blog Livedoor avec Python et sélénium.
J'ai essayé de visualiser les caractéristiques des nouvelles informations sur les personnes infectées par le virus corona avec wordcloud
[First data science ⑥] J'ai essayé de visualiser le prix du marché des restaurants à Tokyo
Je voulais juste extraire les données de la date et de l'heure souhaitées avec Django
J'ai essayé de comparer la vitesse de traitement avec dplyr de R et pandas de Python
Le 15e temps réel hors ligne, j'ai essayé de résoudre le problème de l'écriture avec python
[Courses de chevaux] J'ai essayé de quantifier la force du cheval de course
J'ai essayé la "correction gamma" de l'image avec Python + OpenCV
J'ai essayé de simuler la propagation de l'infection avec Python
J'ai essayé d'obtenir les informations de localisation du bus Odakyu
J'ai essayé de publier automatiquement sur ChatWork au moment du déploiement avec Fabric et ChatWork Api
J'ai essayé de réécrire le serveur WEB de la 1ère édition de programmation Linux normale avec C ++ 14
J'ai essayé de résoudre le problème de F02 comment écrire en temps réel hors ligne avec Python