Chargement des données¶
In [ ]:
#changement de dossier
import os
os.chdir("C:/Users/ricco/Desktop/demo")
#chargement des données
import pandas
D = pandas.read_excel("Tennis_Players_AFDM.xlsx",index_col=0)
D
Out[ ]:
Taille | Lateralite | MainsRevers | Titres | Finales | TitresGC | RolandGarros | BestClassDouble | |
---|---|---|---|---|---|---|---|---|
Joueur | ||||||||
Agassi | 180 | droitier | deux | 60 | 30 | 8 | vainqueur | 123 |
Becker | 191 | droitier | une | 49 | 28 | 6 | demi | 6 |
Borg | 180 | droitier | deux | 64 | 25 | 11 | vainqueur | 890 |
Connors | 178 | gaucher | deux | 109 | 52 | 8 | demi | 370 |
Courier | 185 | droitier | deux | 23 | 13 | 4 | vainqueur | 20 |
Edberg | 187 | droitier | une | 41 | 36 | 6 | finale | 1 |
Kafelnikov | 190 | droitier | deux | 26 | 20 | 2 | vainqueur | 4 |
Kuerten | 190 | droitier | une | 20 | 9 | 3 | vainqueur | 38 |
Lendl | 187 | droitier | une | 94 | 50 | 8 | vainqueur | 20 |
McEnroe | 180 | gaucher | une | 77 | 31 | 7 | finale | 1 |
Nastase | 180 | droitier | une | 58 | 38 | 2 | vainqueur | 59 |
Rafter | 185 | droitier | une | 11 | 14 | 2 | demi | 6 |
Safin | 193 | droitier | deux | 15 | 12 | 2 | demi | 71 |
Sampras | 185 | droitier | une | 64 | 24 | 14 | demi | 27 |
Vilas | 180 | gaucher | une | 62 | 40 | 4 | vainqueur | 175 |
Wilander | 182 | droitier | deux | 33 | 27 | 7 | vainqueur | 3 |
Djokovic | 188 | droitier | deux | 79 | 34 | 17 | vainqueur | 114 |
Federer | 185 | droitier | une | 103 | 54 | 20 | vainqueur | 24 |
Murray | 191 | droitier | deux | 46 | 22 | 3 | finale | 51 |
Nadal | 185 | gaucher | deux | 85 | 37 | 19 | vainqueur | 26 |
In [ ]:
#individus actifs et variables actives
DActifs = D.iloc[:16,:7]
DActifs
Out[ ]:
Taille | Lateralite | MainsRevers | Titres | Finales | TitresGC | RolandGarros | |
---|---|---|---|---|---|---|---|
Joueur | |||||||
Agassi | 180 | droitier | deux | 60 | 30 | 8 | vainqueur |
Becker | 191 | droitier | une | 49 | 28 | 6 | demi |
Borg | 180 | droitier | deux | 64 | 25 | 11 | vainqueur |
Connors | 178 | gaucher | deux | 109 | 52 | 8 | demi |
Courier | 185 | droitier | deux | 23 | 13 | 4 | vainqueur |
Edberg | 187 | droitier | une | 41 | 36 | 6 | finale |
Kafelnikov | 190 | droitier | deux | 26 | 20 | 2 | vainqueur |
Kuerten | 190 | droitier | une | 20 | 9 | 3 | vainqueur |
Lendl | 187 | droitier | une | 94 | 50 | 8 | vainqueur |
McEnroe | 180 | gaucher | une | 77 | 31 | 7 | finale |
Nastase | 180 | droitier | une | 58 | 38 | 2 | vainqueur |
Rafter | 185 | droitier | une | 11 | 14 | 2 | demi |
Safin | 193 | droitier | deux | 15 | 12 | 2 | demi |
Sampras | 185 | droitier | une | 64 | 24 | 14 | demi |
Vilas | 180 | gaucher | une | 62 | 40 | 4 | vainqueur |
Wilander | 182 | droitier | deux | 33 | 27 | 7 | vainqueur |
Préparation des données pour l'AFDM¶
Classe pour "scaling" des variables indicatrices (0/1)¶
In [ ]:
#numpy
import numpy as np
#classe standardscaler de sklearn
from sklearn.preprocessing import StandardScaler
#création d'une classe héritière
#de StandardScaler pour traiter les variables 0/1
# - pas de centrage
# - scaling avec la racine de la fréquence (or fréquence = moyenne pour 0/1)
class MyScaling(StandardScaler):
#surcharger le constructeur
def __init__(self, *, copy=True, with_mean=True, with_std=True):
#appel de l'ancêtre avec modification : pas de centrage
StandardScaler.__init__(self,copy=True,with_mean=False,with_std=True)
#surcharger la méthode fit
def fit(self, X, y=None, sample_weight=None):
#appel de la méthode ancêtre
super().fit(X,y,sample_weight)
#moyenne (fréquence) sera utilisé pour le scale
self.var_ = self.mean_
#paramètre scale du coup
self.scale_ = np.sqrt(self.var_)
return self
Pipeline pour codage des indicatrices¶
In [ ]:
#imports
from sklearn.preprocessing import OneHotEncoder
from sklearn.pipeline import Pipeline
#définir un pipeline pour le traitement des qualitatives
# - codage 0/1 d'abord
# - puis le scaling maison ensuite
prepa_quali = Pipeline([
('encodage',OneHotEncoder()),
('scaling',MyScaling())
])
Préparation différenciée selon le type des variables¶
In [ ]:
#imports
from sklearn.compose import ColumnTransformer, make_column_selector
#préparation complète - d'une part les quanti, de l'autre les quali
#cf. le rôle de ColumnTransformer
#https://scikit-learn.org/stable/modules/generated/sklearn.compose.ColumnTransformer.html
prepa_full = ColumnTransformer([
('quanti',StandardScaler(),make_column_selector(dtype_exclude='object')),
('quali',prepa_quali,make_column_selector(dtype_include='object'))
])
ACP sur ces données transformées = AFDM¶
Entraînement de l'algorithme¶
In [ ]:
#imports
from sklearn.decomposition import PCA
#pipeline avec pretraitement + ACP sur variables pré-traitées
# = AFDM (analyse factorielle des données mixtes)
wkf = Pipeline([
('preparation',prepa_full),
('acp',PCA(n_components=2,svd_solver='full'))
])
In [ ]:
#entraînement
#bien regarder les constituants du Pipeline
wkf.fit(DActifs)
Out[ ]:
Pipeline(steps=[('preparation', ColumnTransformer(transformers=[('quanti', StandardScaler(), <sklearn.compose._column_transformer.make_column_selector object at 0x0000029D9D4B9550>), ('quali', Pipeline(steps=[('encodage', OneHotEncoder()), ('scaling', MyScaling(with_mean=False))]), <sklearn.compose._column_transformer.make_column_selector object at 0x0000029D9C0DF650>)])), ('acp', PCA(n_components=2, svd_solver='full'))])In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Pipeline(steps=[('preparation', ColumnTransformer(transformers=[('quanti', StandardScaler(), <sklearn.compose._column_transformer.make_column_selector object at 0x0000029D9D4B9550>), ('quali', Pipeline(steps=[('encodage', OneHotEncoder()), ('scaling', MyScaling(with_mean=False))]), <sklearn.compose._column_transformer.make_column_selector object at 0x0000029D9C0DF650>)])), ('acp', PCA(n_components=2, svd_solver='full'))])
ColumnTransformer(transformers=[('quanti', StandardScaler(), <sklearn.compose._column_transformer.make_column_selector object at 0x0000029D9D4B9550>), ('quali', Pipeline(steps=[('encodage', OneHotEncoder()), ('scaling', MyScaling(with_mean=False))]), <sklearn.compose._column_transformer.make_column_selector object at 0x0000029D9C0DF650>)])
<sklearn.compose._column_transformer.make_column_selector object at 0x0000029D9D4B9550>
StandardScaler()
<sklearn.compose._column_transformer.make_column_selector object at 0x0000029D9C0DF650>
OneHotEncoder()
MyScaling(with_mean=False)
PCA(n_components=2, svd_solver='full')
Coordonnées des individus dans l'espace factoriel¶
In [ ]:
#coordonnées des individus dans l'espace factoriel
dfFact = wkf.transform(DActifs)
dfFact = pandas.DataFrame(dfFact,index=DActifs.index,columns=['F1','F2'])
dfFact
Out[ ]:
F1 | F2 | |
---|---|---|
Joueur | ||
Agassi | 0.554948 | -1.729643 |
Becker | -0.835330 | 1.241366 |
Borg | 0.715837 | -1.875173 |
Connors | 3.520120 | -0.681037 |
Courier | -1.660146 | -1.165409 |
Edberg | 0.230097 | 2.088298 |
Kafelnikov | -1.985064 | -0.769907 |
Kuerten | -2.269361 | 0.519756 |
Lendl | 1.497483 | -0.037865 |
McEnroe | 2.504098 | 1.948994 |
Nastase | 0.437266 | -0.127113 |
Rafter | -1.919760 | 1.128111 |
Safin | -2.839067 | 0.287683 |
Sampras | 0.604335 | 0.476228 |
Vilas | 1.805785 | 0.197367 |
Wilander | -0.361243 | -1.501657 |
In [ ]:
#affichage dans le premier plan factoriel
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(7,7))
ax.axis([-3.5,+3.5,-3.5,+3.5])
ax.plot([-3.5,+3.5],[0,0],color='silver',linestyle='--')
ax.plot([0,0],[-3.5,+3.5],color='silver',linestyle='--')
ax.set_xlabel("Dim.1")
ax.set_ylabel("Dim.2")
plt.title("Carte des individus")
for x,y,lbl in zip(dfFact.iloc[:,0],dfFact.iloc[:,1],dfFact.index):
ax.text(x,y,lbl,horizontalalignment='center',verticalalignment='center',fontsize=7)
plt.show()
Application du Pipeline sur un individu supplémentaire¶
In [ ]:
#le cas Féderer
dfFed = D.loc[['Federer']].iloc[:,:7]
dfFed
Out[ ]:
Taille | Lateralite | MainsRevers | Titres | Finales | TitresGC | RolandGarros | |
---|---|---|---|---|---|---|---|
Joueur | |||||||
Federer | 185 | droitier | une | 103 | 54 | 20 | vainqueur |
In [ ]:
#calcul de sa coordonnée factorielle
coord_Fed = wkf.transform(dfFed)
coord_Fed
Out[ ]:
array([[ 3.1306293 , -0.71729545]])
In [ ]:
coord_Fed[0][1]
Out[ ]:
-0.717295454130518
In [ ]:
#position
fig, ax = plt.subplots(figsize=(7,7))
ax.axis([-3.5,+3.5,-3.5,+3.5])
ax.plot([-3.5,+3.5],[0,0],color='silver',linestyle='--')
ax.plot([0,0],[-3.5,+3.5],color='silver',linestyle='--')
ax.set_xlabel("Dim.1")
ax.set_ylabel("Dim.2")
plt.title("Carte des individus")
for x,y,lbl in zip(dfFact.iloc[:,0],dfFact.iloc[:,1],dfFact.index):
ax.text(x,y,lbl,horizontalalignment='center',verticalalignment='center',fontsize=7)
#position de Federer
ax.text(coord_Fed[0][0],coord_Fed[0][1],'Federer',horizontalalignment='center',verticalalignment='center',fontsize=7,fontweight='bold',c='red')
plt.show()
Sauvegarde du Pipeline dans un fichier binaire¶
In [ ]:
#sauvegarde dans un fichier binaire
#https://pypi.org/project/dill/
import dill
#créer le fichier en écriture binaire
f = open("workflow.sav","wb")
#sérialisation
dill.dump(wkf,f)
#ne pas oublier de fermer
f.close()
Chargement et déploiement avec le Pipeline¶
Chargement et inspection¶
In [ ]:
#chargement dans un nouvel objet
#utilisation de dill : https://pypi.org/project/dill/
import dill
#ouvrir le fichier en lecture binaire
f = open("workflow.sav","rb")
try:
#chargement
modele = dill.load(f)
finally:
#fermeture quoiqu'il en soit
f.close()
#affichage des étapes
modele.named_steps
Out[ ]:
{'preparation': ColumnTransformer(transformers=[('quanti', StandardScaler(), <sklearn.compose._column_transformer.make_column_selector object at 0x0000029DA0A343D0>), ('quali', Pipeline(steps=[('encodage', OneHotEncoder()), ('scaling', MyScaling(with_mean=False))]), <sklearn.compose._column_transformer.make_column_selector object at 0x0000029DA0ABDB10>)]), 'acp': PCA(n_components=2, svd_solver='full')}
Déploiement sur un individu supplémentaire¶
In [ ]:
#le cas Djokovic
dfDjoko = D.loc[['Djokovic']].iloc[:,:7]
dfDjoko
Out[ ]:
Taille | Lateralite | MainsRevers | Titres | Finales | TitresGC | RolandGarros | |
---|---|---|---|---|---|---|---|
Joueur | |||||||
Djokovic | 188 | droitier | deux | 79 | 34 | 17 | vainqueur |
In [ ]:
#coordonnées factorielles de Djoko
coord_Djoko = modele.transform(dfDjoko)
coord_Djoko
Out[ ]:
array([[ 1.16198817, -1.67811469]])
In [ ]:
#position
fig, ax = plt.subplots(figsize=(7,7))
ax.axis([-3.5,+3.5,-3.5,+3.5])
ax.plot([-3.5,+3.5],[0,0],color='silver',linestyle='--')
ax.plot([0,0],[-3.5,+3.5],color='silver',linestyle='--')
ax.set_xlabel("Dim.1")
ax.set_ylabel("Dim.2")
plt.title("Carte des individus")
for x,y,lbl in zip(dfFact.iloc[:,0],dfFact.iloc[:,1],dfFact.index):
ax.text(x,y,lbl,horizontalalignment='center',verticalalignment='center',fontsize=7)
#position de Federer
ax.text(coord_Fed[0][0],coord_Fed[0][1],'Federer',horizontalalignment='center',verticalalignment='center',fontsize=7,fontweight='bold',c='red')
#et de Djokovic
ax.text(coord_Djoko[0][0],coord_Djoko[0][1],'Djokovic',horizontalalignment='center',verticalalignment='center',fontsize=7,fontweight='bold',c='blue')
plt.show()