Introduction

Pour ce projet, l’objectif était de mettre en pratique des différentes méthodes d’apprentissage statistique pour l’IA vue en cours. J’ai réutilisé les mêmes données que pour la SAE du semestre précédent. À savoir, un jeu de données sur les statistiques des joueurs de hockey sur glace de la NHL.

Le hockey sur glace est un sport de contact pratiqué sur une patinoire. Il est très populaire dans les pays nordiques tels que le Canada, les États-Unis, la Suède, la Finlande et la Russie, mais il est également joué dans de nombreux autres pays du monde. Ce sport implique deux équipes qui se disputent un palet avec des bâtons de hockey pour marquer des points en envoyant le palet dans le but adverse. Le hockey sur glace est connu pour sa vitesse, son intensité et sa brutalité, ce qui en fait l’un des sports les plus excitants et les plus regardés au monde.

La Ligue nationale de hockey, également connue sous le nom de NHL, est une ligue professionnelle de hockey sur glace en Amérique du Nord. C’est sur cette ligue que va porter notre classification.

La NHL se déroule en deux phases, la phase régulière et les playoffs seulement accessible pour les 16 meilleures équipes de la phase régulières. Lors de cette étude, je vais me concentrer sur la phase régulière de la NHL pour les saison entre 2004 et 2017.

J’ai réparti mon jeu de données en deux catégories les défenseurs,D, et les attaquants où j’ai regroupé les centres, et les ailiers droits et gauches, A. L’objectif, va être de prédire si le poste du joueur. On va commencer par voir la méthode de K plus proches, voisins puis celles des arbres de décision et de random Forest, ensuite, on verra l’ADL et SVD et on finira par la régression logistique.

set.seed(2024)
library(DBI)
con =dbConnect(RPostgreSQL::PostgreSQL(),dbname="iut2202698")
query=' SELECT *
FROM "public"."nhl";'
Y <- dbGetQuery(con,query)
rownames(Y)=Y[,1]
Z=Y[,-1]
dim(Z)
## [1] 13186    31
head(Z)
##   Rk            Player Age Pos  Tm GP  G  A PTS plusminus PIM   PS EV PP SH GW
## 1  1    Connor McDavid  20   C EDM 82 30 70 100        27  26 12.8 26  3  1  6
## 2  2     Sidney Crosby  29   C PIT 75 44 45  89        17  24 12.3 30 14  0  5
## 3  3      Patrick Kane  28  RW CHI 82 34 55  89        11  32 10.8 27  7  0  5
## 4  4 Nicklas Backstrom  29   C WSH 82 23 63  86        17  38  9.9 15  8  0  5
## 5  5   Nikita Kucherov  23  RW TBL 74 40 45  85        13  38 12.0 23 17  0  7
## 6  6     Brad Marchand  28  LW BOS 80 39 46  85        18  81 12.6 27  9  3  8
##   EV.1 PP.1 SH.1   S S_percent  TOI     ATOI BLK HIT FOW FOL FO_percent HART
## 1   45   24    1 251      12.0 1733 21.13333  29  34 348 458       43.2    1
## 2   34   11    0 255      17.3 1491 19.88333  27  80 842 906       48.2    0
## 3   39   16    0 292      11.6 1754 21.40000  15  28   7  44       13.7    0
## 4   36   27    0 162      14.2 1497 18.26667  33  45 685 648       51.4    0
## 5   30   15    0 246      16.3 1438 19.43333  20  30   0   0        0.0    0
## 6   29   15    2 226      17.3 1555 19.43333  35  51  13  23       36.1    0
##   Votes Season
## 1  1604   2017
## 2  1104   2017
## 3   206   2017
## 4    60   2017
## 5   119   2017
## 6   184   2017
Z = Z[c(-2,-5,-31)]
Z=na.omit(Z)
Z$Pos = ifelse(Z$Pos %in% c('C', 'RW','LW'), "A", "D")
X=Z[c(2,4:26)]
Y=as.factor(Z[,3])
dim(X)
## [1] 12768    24
table(Y)
## Y
##    A    D 
## 8552 4216

#Valeur Manquante

table(is.na(X))
## 
##  FALSE 
## 306432

Je n’ai pas de valeur manquante, on va donc pouvoir poursuivre sans modifier le jeu de données.

Knn

La méthode K plus proches voisins (KNN) est une technique simple d’apprentissage automatique utilisée pour la classification et la régression.

En classification, KNN cherche les “K” voisins les plus proches d’un point de données inconnu en se basant sur une mesure de similarité, comme la distance euclidienne. Une fois les voisins identifiés, la classe du point inconnu est déterminée en fonction de la classe majoritaire parmi ces voisins.

calcul=function(K=5){
  F_score=function(){
    res=rep(0,nlevels(Y_train))
    w=table(Y_predit,Y_test)
    for (k in 1:nlevels(Y_train)){
      TP=w[k,k]
      FP=sum(w[k,-k])
      FN=sum(w[-k,k])
      res[k]=(2*TP) / (2*TP + FP +FN)
    }
    return(res)
  }
  
  RES_error=NULL
  RES_F_score=NULL
  ech=sample(1:K,nrow(X),replace = T)
  for (iter in 1:K){
    
    res_1_bloc=NULL
    
    ###
    X_train=X[ech != iter,]
    Y_train=Y[ech != iter]
    
    X_test=X[ech == iter,]
    Y_test=Y[ech == iter]
    

    library(class)
    Y_predit =  knn(train=X_train, test=X_test, cl=Y_train) # pas de modèle dans les knn
    w=table(Y_predit,Y_test)
    TauxErreurGlobale = 1-sum(diag(w))/sum(w)
    
    RES_error=c(RES_error,TauxErreurGlobale)
    
    RES_F_score=rbind(RES_F_score,F_score())
    
  }
  return(list(RES_error,RES_F_score))
}


graph_erreur=function(){
  library(ggplot2)
  erreur=c(RES[[1]])
  methode=rep("1.arbre",each=length(RES[[1]]))  
  df=data.frame(methode, erreur)
  ggplot(df, aes(x=methode, y=erreur)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip()+ 
    ggtitle("Taux d'erreur")
}
graph_F_score=function(){
  library(ggplot2)
  F_score=c(RES[[2]])
  modalite=rep(levels(Y),each=nrow(RES[[2]]))
  
  df=data.frame(modalite, F_score)
  ggplot(df, aes(x=modalite, y=F_score)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip() +
    ggtitle("F-score par modalité")
  
}


RES=calcul(10)
graph_erreur()

graph_F_score()

Avec la méthode des arbres de décision, on a un taux d’erreur de seulement 6%, ce qui signifie que le modele a une performance élevé dans la predictions des joueurs de hockey. Un F-score de 0,91 Pour les défenseurs indique une très bonne capacité du modèle à les identifier correctement. Pour les attaquants, un F-score de 0,95 indique une performance encore meilleure dans leur identification. En résumé, le modèle KNN semble être performant dans la classification des joueurs de hockey en attaquants et en défenseurs, avec un taux d’erreur bas et des F-scores élevés pour les deux classes.

Arbres de décision

Les arbres de décision sont une méthode d’analyse et de prise de décision utilisée en apprentissage automatique et en sciences de données. Ils sont basés sur une représentation arborescente des décisions et de leurs conséquences.

calcul=function(K=5){
  F_score=function(){
    res=rep(0,nlevels(Y_train))
    w=table(Y_predit,Y_test)
    for (k in 1:nlevels(Y_train)){
      TP=w[k,k]
      FP=sum(w[k,-k])
      FN=sum(w[-k,k])
      res[k]=(2*TP) / (2*TP + FP +FN)
    }
    return(res)
  }
  
  RES_error=NULL
  RES_F_score=NULL
  ech=sample(1:K,nrow(X),replace = T)
  for (iter in 1:K){
    
    res_1_bloc=NULL
    
    ###
    X_train=X[ech != iter,]
    Y_train=Y[ech != iter]
    
    X_test=X[ech == iter,]
    Y_test=Y[ech == iter]
    

    library(rpart)
    model = rpart(Y_train~.,data=data.frame(X_train),method="class")
    predictions=predict(model,data.frame(X_test),type="class")
    Y_predit=predictions
    w=table(Y_predit,Y_test)
    TauxErreurGlobale = 1-sum(diag(w))/sum(w)
    
    RES_error=c(RES_error,TauxErreurGlobale)
    
    RES_F_score=rbind(RES_F_score,F_score())
    
  }
  return(list(RES_error,RES_F_score))
}


graph_erreur=function(){
  library(ggplot2)
  erreur=c(RES[[1]])
  methode=rep("1.arbre",each=length(RES[[1]]))  
  df=data.frame(methode, erreur)
  ggplot(df, aes(x=methode, y=erreur)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip() + 
    ggtitle("Taux d'erreur")
}
graph_F_score=function(){
  library(ggplot2)
  F_score=c(RES[[2]])
  modalite=rep(levels(Y),each=nrow(RES[[2]]))
  
  df=data.frame(modalite, F_score)
  ggplot(df, aes(x=modalite, y=F_score)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip() +
    ggtitle("F-score par modalité")
  
}


RES=calcul(10)
RES_arbre = mean(RES[[1]])
graph_erreur()

graph_F_score()

Avec la méthode des arbres de décision, on a un taux d’erreur de seulement 8,5%, ce qui signifie que le modele est plutot précis dans ces prédictions. Un F-score de 0,87 Pour les défenseurs indique une bonne capacité du modèle à les identifier correctement. Pour les attaquants, un F-score de 0,93 indique une performance encore meilleure dans leur identification. En résumé, ces résultats suggèrent que le modèle d’arbre de décision est assez fiable pour prédire si un joueur de hockey est un attaquant ou un défenseur. Il est légèrement plus performant dans la prédiction des attaquants par rapport aux défenseurs, mais dans l’ensemble, il offre une bonne précision avec un faible taux d’erreur.

Random Forest

La méthode Random Forest est une technique d’apprentissage automatique utilisée pour résoudre des problèmes de classification et de régression. Elle fonctionne en combinant les prédictions de plusieurs arbres de décision individuels pour obtenir une prédiction plus robuste et précise.

calcul=function(K=4){
  F_score=function(){
    res=rep(0,nlevels(Y_train))
    w=table(Y_predit,Y_test)
    for (k in 1:nlevels(Y_train)){
      TP=w[k,k]
      FP=sum(w[k,-k])
      FN=sum(w[-k,k])
      res[k]=(2*TP) / (2*TP + FP +FN)
    }
    return(res)
  }
  
  RES_error=NULL
  RES_F_score=NULL
  ech=sample(1:K,nrow(X),replace = T)
  for (iter in 1:K){
    
    res_1_bloc=NULL
    
    ###
    X_train=X[ech != iter,]
    Y_train=Y[ech != iter]
    
    X_test=X[ech == iter,]
    Y_test=Y[ech == iter]
    

    library(randomForest)
    model=randomForest(X_train, Y_train)
    Y_predit=predict(model, X_test)
    w=table(Y_predit,Y_test)
    TauxErreurGlobale = 1-sum(diag(w))/sum(w)
    
    RES_error=c(RES_error,TauxErreurGlobale)
    
    RES_F_score=rbind(RES_F_score,F_score())
    
  }
  return(list(RES_error,RES_F_score))
}


graph_erreur=function(){
  library(ggplot2)
  erreur=c(RES[[1]])
  methode=rep("1.arbre",each=length(RES[[1]]))  
  df=data.frame(methode, erreur)
  ggplot(df, aes(x=methode, y=erreur)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip() + 
    ggtitle("Taux d'erreur")
}
graph_F_score=function(){
  library(ggplot2)
  F_score=c(RES[[2]])
  modalite=rep(levels(Y),each=nrow(RES[[2]]))
  
  df=data.frame(modalite, F_score)
  ggplot(df, aes(x=modalite, y=F_score)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip() +
    ggtitle("F-score par modalité")
  
}


RES=calcul(10)
graph_erreur()

graph_F_score()

Avec la méthode des arbres de décision, on a un taux d’erreur de seulement 2,25%, ce qui signifie que le modele a une performance élevé dans la predictions des joueurs de hockey. Un F-score de 0,965 Pour les défenseurs et 0,98 pour les attaquants ce qui indique une très bonne performance du modèle. En conclusion, ces résultats suggèrent que le modèle Random Forest est capable de distinguer avec une grande précision les attaquants des défenseurs dans une équipe de hockey, avec une faible marge d’erreur.

ADL

La méthode ADL, ou Analyse Discriminante Linéaire, est une technique statistique utilisée pour trouver une combinaison linéaire des caractéristiques (ou variables) qui permet de mieux discriminer entre deux ou plusieurs groupes ou classes prédéfinis. L’objectif principal de l’ADL est de maximiser la séparation entre ces groupes.

calcul=function(K=5){
  F_score=function(){
    res=rep(0,nlevels(Y_train))
    w=table(Y_predit,Y_test)
    for (k in 1:nlevels(Y_train)){
      TP=w[k,k]
      FP=sum(w[k,-k])
      FN=sum(w[-k,k])
      res[k]=(2*TP) / (2*TP + FP +FN)
    }
    return(res)
  }
  
  RES_error=NULL
  RES_F_score=NULL
  ech=sample(1:K,nrow(X),replace = T)
  for (iter in 1:K){
    
    res_1_bloc=NULL
    
    ###
    X_train=X[ech != iter,]
    Y_train=Y[ech != iter]
    
    X_test=X[ech == iter,]
    Y_test=Y[ech == iter]
    

    library(MASS)
    model=lda(X_train,Y_train)
    predictions=predict(model,data.frame(X_test),type="class")
    Y_predit=predictions$class
    w=table(Y_predit,Y_test)
    TauxErreurGlobale = 1-sum(diag(w))/sum(w)
    
    RES_error=c(RES_error,TauxErreurGlobale)
    
    RES_F_score=rbind(RES_F_score,F_score())
    
  }
  return(list(RES_error,RES_F_score))
}


graph_erreur=function(){
  library(ggplot2)
  erreur=c(RES[[1]])
  methode=rep("1.arbre",each=length(RES[[1]]))  
  df=data.frame(methode, erreur)
  ggplot(df, aes(x=methode, y=erreur)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip()+ 
    ggtitle("Taux d'erreur")
}
graph_F_score=function(){
  library(ggplot2)
  F_score=c(RES[[2]])
  modalite=rep(levels(Y),each=nrow(RES[[2]]))
  
  df=data.frame(modalite, F_score)
  ggplot(df, aes(x=modalite, y=F_score)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip() +
    ggtitle("F-score par modalité")
  
}


RES=calcul(10)
graph_erreur()

graph_F_score()

Avec la méthode des arbres de décision, on a un taux d’erreur de seulement 5%, ce qui signifie que le modele arrive généralement à prédire le poste du joueur. Un F-score de 0,91 Pour les défenseurs indique une très bonne capacité du modèle à les identifier correctement. Pour les attaquants, un F-score de 0,965 indique une performance encore meilleure dans leur identification. En résumé, ces résultats suggèrent que le modèle ADL est efficace pour prédire si un joueur de hockey est un attaquant ou un défenseur. Il est légèrement plus performant dans la prédiction des attaquants par rapport aux défenseurs,cependant il offre une bonne précision avec un F-score superieur à 0,9 pour les attaquants et les defenseurs ainsi qu’un faible taux d’erreur.

SVD

La méthode de décomposition en valeurs singulières (SVD) est une technique mathématique utilisée pour analyser et représenter des données sous forme de combinaison de motifs significatifs. En gros, SVD prend une grande matrice de données et la décompose en matrices plus petites.

calcul=function(K=5){
  F_score=function(){
    res=rep(0,nlevels(Y_train))
    w=table(Y_predit,Y_test)
    for (k in 1:nlevels(Y_train)){
      TP=w[k,k]
      FP=sum(w[k,-k])
      FN=sum(w[-k,k])
      res[k]=(2*TP) / (2*TP + FP +FN)
    }
    return(res)
  }
  
  RES_error=NULL
  RES_F_score=NULL
  ech=sample(1:K,nrow(X),replace = T)
  for (iter in 1:K){
    
    res_1_bloc=NULL
    
    ###
    X_train=X[ech != iter,]
    Y_train=Y[ech != iter]
    
    X_test=X[ech == iter,]
    Y_test=Y[ech == iter]
    

     library(e1071)
    model = svm(X_train, Y_train,probability = TRUE)
    Y_predit = predict(model, X_test, probability = FALSE)
    w=table(Y_predit,Y_test)
    TauxErreurGlobale = 1-sum(diag(w))/sum(w)
    
    RES_error=c(RES_error,TauxErreurGlobale)
    
    RES_F_score=rbind(RES_F_score,F_score())
    
  }
  return(list(RES_error,RES_F_score))
}


graph_erreur=function(){
  library(ggplot2)
  erreur=c(RES[[1]])
  methode=rep("1.arbre",each=length(RES[[1]]))  
  df=data.frame(methode, erreur)
  ggplot(df, aes(x=methode, y=erreur)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip()+ 
    ggtitle("Taux d'erreur")
}
graph_F_score=function(){
  library(ggplot2)
  F_score=c(RES[[2]])
  modalite=rep(levels(Y),each=nrow(RES[[2]]))
  
  df=data.frame(modalite, F_score)
  ggplot(df, aes(x=modalite, y=F_score)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip() +
    ggtitle("F-score par modalité")
  
}


RES=calcul(10)
graph_erreur()

graph_F_score()

Avec la méthode des arbres de décision, on a un taux d’erreur de seulement 3,5%, ce qui signifie que le modele a une performance élevé dans la predictions des joueurs de hockey. Un F-score de 0,945 Pour les défenseurs indique une très bonne capacité du modèle à les identifier correctement. Pour les attaquants, un F-score de 0,975 indique une performance encore meilleure dans leur identification. Cela suggère que votre modèle est généralement très bon pour distinguer les attaquants et les défenseurs, mais il est peut-être légèrement plus précis dans la prédiction des attaquants. Cela pourrait être dû à plusieurs facteurs, tels que des différences dans les caractéristiques des deux types de joueurs ou des déséquilibres dans les données d’entraînement.

Regression logistique

La régression logistique est une méthode statistique utilisée pour prédire une variable binaire en fonction de variables indépendantes. Elle est souvent utilisée lorsque la variable à prédire ne peut prendre que deux valeurs possibles

calcul=function(K=5){
  F_score=function(){
    res=rep(0,nlevels(Y_train))
    w=table(Y_predit,Y_test)
    for (k in 1:nlevels(Y_train)){
      TP=w[k,k]
      FP=sum(w[k,-k])
      FN=sum(w[-k,k])
      res[k]=(2*TP) / (2*TP + FP +FN)
    }
    return(res)
  }
  
  RES_error=NULL
  RES_F_score=NULL
  ech=sample(1:K,nrow(X),replace = T)
  for (iter in 1:K){
    
    res_1_bloc=NULL
    
    ###
    X_train=X[ech != iter,]
    Y_train=Y[ech != iter]
    
    X_test=X[ech == iter,]
    Y_test=Y[ech == iter]
    

     if (nlevels(Y_train)==2){
      model = glm(Y_train~., family=binomial(link="logit"),data=as.data.frame(X_train))
      p=predict.glm(model,X_test,type="response")
      Y_predit=as.factor(p>0.5)
      levels(Y_predit)=levels(Y_test)
      w=table(Y_predit,Y_test)
    TauxErreurGlobale = 1-sum(diag(w))/sum(w)
    
    RES_error=c(RES_error,TauxErreurGlobale)
    
    RES_F_score=rbind(RES_F_score,F_score())
     }
  }
  return(list(RES_error,RES_F_score))
}


graph_erreur=function(){
  library(ggplot2)
  erreur=c(RES[[1]])
  methode=rep("1.arbre",each=length(RES[[1]]))  
  df=data.frame(methode, erreur)
  ggplot(df, aes(x=methode, y=erreur)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip()+ 
    ggtitle("Taux d'erreur")
}
graph_F_score=function(){
  library(ggplot2)
  F_score=c(RES[[2]])
  modalite=rep(levels(Y),each=nrow(RES[[2]]))
  
  df=data.frame(modalite, F_score)
  ggplot(df, aes(x=modalite, y=F_score)) + 
    stat_boxplot(geom = "errorbar",
                 width = 0.25) + 
    geom_boxplot() +
    coord_flip() +
    ggtitle("F-score par modalité")
  
}


RES=calcul(10)
graph_erreur()

graph_F_score()

Avec la méthode des arbres de décision, on a un taux d’erreur de seulement 2%, ce qui signifie que le modele a une performance élevé dans la predictions des joueurs de hockey. Un F-score de 0,97 Pour les défenseurs et 0,985 pour les attaquants ce qui indique une très bonne performance du modèle. En interprétant ces résultats, on peut conclure que le modèle de régression logistique est très efficace pour distinguer les attaquants des défenseurs dans le hockey. La faible erreur médiane et les F-scores élevés indiquent que le modèle est robuste et précis dans ses prédictions.

Conclusion

En conclusion, toutes les différentes méthodes d’apprentissage statistique pour l’IA ont donné de bons résultats, avec un taux d’erreur de 8,5 % et un F-score inferieur à 0,9, la méthode des arbres de décisions était la moins performante. Au contraire, avec 2 % de taux d’erreur et des F-scores superieur à 0,95 la méthode de régression logistique est la meilleure méthode tester dans le projet. Vu la qualité de mes résultats, je peux aussi conclure qu’il est facile de faire la différence entre le poste d’attaquant et celui de défenseur en NHL. En mettant en lumière les variables clés qui les sépare cela pourrait permettre de faciliter le recrutement des différents entraîneurs et des différentes franchises.