Exercice 0¶

In [1]:
options(repr.plot.width=15, repr.plot.height=10)
In [2]:
#install.packages("FactoMineR") #à ne faire qu'une fois
library(FactoMineR)
data <- read.csv("../data/decathlon.csv") #ou bien passer l'URL
In [3]:
head(data)
A data.frame: 6 × 14
NameX100mLong.jumpShot.putHigh.jumpX400mX110m.hurdleDiscusPole.vaultJavelineX1500mRankPointsCompetition
<chr><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><dbl><int><int><chr>
1SEBRLE 11.047.5814.832.0749.8114.6943.755.0263.19291.718217Decastar
2CLAY 10.767.4014.261.8649.3714.0550.724.9260.15301.528122Decastar
3KARPOV 11.027.3014.772.0448.3714.0948.954.9250.31300.238099Decastar
4BERNARD11.027.2314.251.9248.9314.9940.875.3262.77280.148067Decastar
5YURKOV 11.347.0915.192.1050.4215.3146.264.7263.44276.458036Decastar
6WARNERS11.117.6014.311.9848.6814.2341.104.9251.77278.168030Decastar
In [4]:
# On écarte la première colonne contenant les noms, après l'avoir affectée (pour un affichage plus joli)
row.names(data) <- data[,1]
data <- data[,-1]
In [5]:
# On veut des variables résumant les performances aux différentes épreuves.
# Les variables Rank, Points et Competition seront donc considérées a posteriori.
# Attention à bien renseigner le type de variable : quanti.sup pour les variables numériques quali.sup sinon.
res.pca <- PCA(data, quanti.sup=11:12, quali.sup=13)

Aide obtenue via ?plot.PCA :

 plot(x, axes = c(1, 2), choix = c("ind","var","varcor"),
     ellipse = NULL, xlim = NULL, ylim = NULL, habillage="none", 
     col.hab = NULL, col.ind="black", col.ind.sup="blue", 
     col.quali="magenta", col.quanti.sup="blue", col.var="black",
     label = c("all","none","ind","ind.sup","quali","var","quanti.sup"),
         invisible = c("none","ind","ind.sup","quali","var","quanti.sup"), 
     lim.cos2.var = 0., title = NULL, palette=NULL,
     autoLab = c("auto","yes","no"), new.plot = FALSE, select = NULL, 
         unselect = 0.7, shadowtext = FALSE, legend = list(bty = "y", x = "topleft"),
         graph.type = c("ggplot","classic"), ggoptions = NULL, ...)

axes : les deux axes à considérer (le premier en abscisse, le second en ordonnée).
choix : les individus ("ind"), ou les variables ("var") sur le cercle des corrélations.
habillage : colorier selon une variable (groupes, ou bien coloriage "continu").
...
select : permet par exemple de limiter le dessin aux individus les mieux représentés.

In [6]:
plot(res.pca, choix="ind", habillage=12, label="ind", select="cos2 10")
# Les 10 individus les mieux représentés sur le plan 1-2 (situés "logiquement" loin de l'origine).
No description has been provided for this image
In [7]:
# Cos2 des individus en question (= contributions)
sort(rowSums(res.pca$ind$cos2), decreasing=TRUE)[1:10]
Warners
0.989753641357896
Casarsa
0.979780173989823
BOURGUIGNON
0.973658883429005
Uldal
0.96667269061345
YURKOV
0.956810465328668
Nool
0.953610043556601
Drews
0.949274816329377
Sebrle
0.945262756203281
Macey
0.937706196548943
Karpov
0.922145532300468
In [8]:
# Affichage des variables dans le plan 3-4
plot(res.pca, choix="var", axes=3:4) #ou axes=c(3,4)
No description has been provided for this image

Une certaine corrélation positive entre les variables X1500m et Pole.Vault semble apparaître. Cela signifierait que "plus un athlète saute haut (avec perche), plus il est lent au 1500m" - attention ce n'est qu'une vague tendance, à vérifier numériquement ! De plus ce plan ne capte que 25% de l'inertie totale.

In [9]:
library(corrplot)
correlations <- cor(data[,-(11:13)])
corrplot(correlations)
# Une légère corrélation positive se confirme, de l'ordre de 0.2 ;
# on peut vérifier numériquement en affichant la matrice partielle :
correlations[8:10,8:10]
corrplot 0.92 loaded

A matrix: 3 × 3 of type dbl
Pole.vaultJavelineX1500m
Pole.vault 1.0000000-0.0300006 0.2474478
Javeline-0.0300006 1.0000000-0.1803931
X1500m 0.2474478-0.1803931 1.0000000
No description has been provided for this image

Exercice 1¶

In [10]:
data <- read.csv("../data/Pizza.csv")
head(data)
A data.frame: 6 × 9
brandidmoisprotfatashsodiumcarbcal
<chr><int><dbl><dbl><dbl><dbl><dbl><dbl><dbl>
1A1406927.8221.4344.875.111.770.774.93
2A1405328.4921.2643.895.341.791.024.84
3A1402528.3519.9945.785.081.630.804.95
4A1401630.5520.1543.134.791.611.384.74
5A1400530.4921.2841.654.821.641.764.67
6A1407531.1420.2342.314.921.651.404.67

La première colonne ("brand") indique la marque de pizza : elle ne peut pas contribuer à l'ACP car elle n'est pas numérique. Elle sera utile plus tard tout de même, pour visualiser les caractériqtiques des marques.

La seconde colonne est un identifiant, sans aucun intérêt en ce qui nous concerne.

In [11]:
data <- data[,-2]
res.pca <- PCA(data, quali.sup=1)
In [12]:
plot(res.pca, choix="ind", habillage=1)
# Anticipant un peu sur la séance "clustering", on voit clairement plusieurs groupes :
# les pizzas d'une même marque "se ressemblent" et sont à quelques exceptions près très différentes des autres.
No description has been provided for this image
In [13]:
plot(res.pca, choix="var")
No description has been provided for this image

https://www.zumub.com/blog/fr/quels-sont-vos-carbohydrates/ : "Les carbohydrates [...] peuvent être trouvés dans différents aliments tels que le miel, les biscuits, le pain [...]" => on comprend donc qu'un taux élevé de "carb" signifie "pizza à pâte épaisse" (généralement peu garnie. Chacun ses goûts hein mais autant se faire un sandwich '^^). L'opposition quasi parfaite avec la teneur en protéines "prot" s'en trouve expliquée. Beaucoup de viande ou beaucoup de pâte, il faut choisir.

Ensuite, on remarque que les pizzas plus grasses sont souvent plus caloriques, ce qui n'est pas très étonnant non plus. La très forte corrélation "sodium" / "fat" semble indiquer qu'il s'agit de ce type d'ingrédient, ce qui ne donne a priori pas très envie de goûter les pizzas de la marque "A"...

Enfin, la quantité d'eau présente dans l'échantillon n'est pas vraiment (positivement) corrélée aux autres variables (sauf "prot" : fruits de mer ?!), Les pizzas protéinées semblent aussi plus cendrées, mais j'ai du mal à interpréter ça (quelqu'un a une idée ?).

Note : 92% expliqués avec les deux premiers axes => pas besoin d'aller plus loin.

In [14]:
# Vérification numérique (visuelle)
correlations <- cor(data[,-1])
corrplot(correlations)
No description has been provided for this image

Les corrélations semblent plus marquées sur ce dernier graphe. Il resterait à en expliquer certaines, n'hésitez pas si vous avez des idées !

Exercice 2¶

In [15]:
data <- read.csv("../data/video_games.csv")
head(data)
A data.frame: 6 × 36
TitleFeatures.Handheld.Features.Max.PlayersFeatures.Multiplatform.Features.Online.Metadata.GenresMetadata.Licensed.Metadata.PublishersMetadata.Sequel.Metrics.Review.Score⋯Length.Main...Extras.AverageLength.Main...Extras.LeisureLength.Main...Extras.MedianLength.Main...Extras.PolledLength.Main...Extras.RushedLength.Main.Story.AverageLength.Main.Story.LeisureLength.Main.Story.MedianLength.Main.Story.PolledLength.Main.Story.Rushed
<chr><chr><int><chr><chr><chr><chr><chr><chr><int>⋯<dbl><dbl><dbl><int><dbl><dbl><dbl><dbl><int><dbl>
1Super Mario 64 DS True1TrueTrueAction TrueNintendo True85⋯24.9166729.96666725.0000001618.33333314.33333318.31666714.50000021 9.700000
2Lumines: Puzzle Fusion True1TrueTrueStrategy TrueUbisoft True89⋯ 9.75000 9.866667 9.750000 2 9.61666710.33333311.08333310.000000 3 9.583333
3WarioWare Touched! True2TrueTrueAction,Racing / Driving,SportsTrueNintendo True81⋯ 3.85000 5.666667 3.33333311 2.783333 1.916667 2.933333 1.83333330 1.433333
4Hot Shots Golf: Open Tee True1TrueTrueSports TrueSony True81⋯ 0.00000 0.000000 0.000000 0 0.000000 0.000000 0.000000 0.000000 0 0.000000
5Spider-Man 2 True1TrueTrueAction TrueActivisionTrue61⋯12.7666717.31666712.5000001210.483333 8.35000011.083333 8.00000023 5.333333
6The Urbz: Sims in the CityTrue1TrueTrueSimulation TrueEA True67⋯20.8333325.20000020.000000 316.45000015.50000015.75000015.500000 215.250000
In [16]:
# Beaucoup de variables ! Lesquelles sont numériques ?
num_vars <- as.logical(sapply(data[1,], is.numeric))
colnames(data)[num_vars]
colnames(data)[!num_vars]
  1. 'Features.Max.Players'
  2. 'Metrics.Review.Score'
  3. 'Metrics.Sales'
  4. 'Metrics.Used.Price'
  5. 'Release.Year'
  6. 'Length.All.PlayStyles.Average'
  7. 'Length.All.PlayStyles.Leisure'
  8. 'Length.All.PlayStyles.Median'
  9. 'Length.All.PlayStyles.Polled'
  10. 'Length.All.PlayStyles.Rushed'
  11. 'Length.Completionists.Average'
  12. 'Length.Completionists.Leisure'
  13. 'Length.Completionists.Median'
  14. 'Length.Completionists.Polled'
  15. 'Length.Completionists.Rushed'
  16. 'Length.Main...Extras.Average'
  17. 'Length.Main...Extras.Leisure'
  18. 'Length.Main...Extras.Median'
  19. 'Length.Main...Extras.Polled'
  20. 'Length.Main...Extras.Rushed'
  21. 'Length.Main.Story.Average'
  22. 'Length.Main.Story.Leisure'
  23. 'Length.Main.Story.Median'
  24. 'Length.Main.Story.Polled'
  25. 'Length.Main.Story.Rushed'
  1. 'Title'
  2. 'Features.Handheld.'
  3. 'Features.Multiplatform.'
  4. 'Features.Online.'
  5. 'Metadata.Genres'
  6. 'Metadata.Licensed.'
  7. 'Metadata.Publishers'
  8. 'Metadata.Sequel.'
  9. 'Release.Console'
  10. 'Release.Rating'
  11. 'Release.Re.release.'

Parmi ces variables, j'ai envie de considérer "Features.Max.Players" comme qualitative : c'est a priori plutôt un indicateur du type de jeu.

In [17]:
indices <- (1:ncol(data))[num_vars]
quali.sup_indices <- c(indices[1], (1:ncol(data))[!num_vars])
indices <- indices[-1]

# Enfin prêt pour l'ACP :
res.pca <- PCA(data, quali.sup=quali.sup_indices)
Warning message:
“ggrepel: 10 unlabeled data points (too many overlaps). Consider increasing max.overlaps”
In [18]:
plot(res.pca, choix="ind", label="none")
No description has been provided for this image

Quelques individus extrêmes se démarquent, mais globalement on n'y voit pas grand chose et beaucoup de jeux sont confondus vers l'origine. Environ 64% d'inertie expliquée dans ce plan, ce qui est assez important même si l'analyse des axes suivants reste intéressante.

In [19]:
plot(res.pca, choix="var")
Warning message:
“ggrepel: 10 unlabeled data points (too many overlaps). Consider increasing max.overlaps”
No description has been provided for this image

Beaucoup de variables très corrélées : on va relancer l'analyse en supprimant certaines variables (quasi) redondantes, pour tenter d'y voir plus clair.

In [20]:
correlations <- cor(data[,indices])
correlations[correlations < 0.8] <- 0
corrplot(correlations)
# Visualisation des potentiels regroupements à effectuer :
No description has been provided for this image

Regroupements :

'Metrics.Review.Score'

'Metrics.Sales'

'Metrics.Used.Price'

'Release.Year'

'Length.All.PlayStyles.Polled' (disons qu'on garde celle-là)
'Length.Completionists.Polled'
'Length.Main.Story.Polled'
'Length.Main...Extras.Polled'

'Length.Main.Story.Leisure'
'Length.Main.Story.Average'
'Length.Main.Story.Median' (gardée)
'Length.Main.Story.Rushed'

'Length.All.PlayStyles.Rushed'
'Length.All.PlayStyles.Median' (gardée)
'Length.All.PlayStyles.Average'

'Length.Completionists.Average' (gardée)
'Length.Completionists.Leisure'
'Length.Completionists.Median'
'Length.Completionists.Rushed'

'Length.Main...Extras.Average'
'Length.Main...Extras.Median' (gardée)
'Length.Main...Extras.Rushed'

'Length.Main...Extras.Leisure'

'Length.All.PlayStyles.Leisure'

=> 11 variables "seulement" au lieu de 24.

In [21]:
keep_vars <- c('Metrics.Review.Score','Metrics.Sales','Metrics.Used.Price','Release.Year',
               'Length.All.PlayStyles.Polled','Length.Main.Story.Median','Length.All.PlayStyles.Median',
               'Length.Completionists.Average','Length.Main...Extras.Median','Length.Main...Extras.Leisure',
               'Length.All.PlayStyles.Leisure')
indices <- (1:ncol(data))[colnames(data) %in% keep_vars]
data <- data[,c(indices, quali.sup_indices)]
In [22]:
qual_vars <- !as.logical(sapply(data[1,], is.numeric))
quali.sup_indices <- (1:ncol(data))[qual_vars]

# Enfin prêt pour l'ACP "v2" ^^
res.pca <- PCA(data, quali.sup=quali.sup_indices)
In [23]:
plot(res.pca, choix="ind", label="none")
# Le nuage semble déjà un peu plus dispersé (même si ça reste moyen).
# Seulement 50% de l'inertie dans le plan 1-2 cette fois : il faudra regarder plus loin.
No description has been provided for this image
In [24]:
plot(res.pca, choix="var")
No description has been provided for this image

Le cercle des corrélations semble indiquer, en gros, que les jeux plus longs sont situés vers le bas à droite, tandis que les jeux les plus vendus sont plutôt vers le haut légèrement à droite. Vérification rapide :

In [25]:
rightmost <- which.max(res.pca$ind$coord[,1])
upmost <- which.max(res.pca$ind$coord[,2])
data[c(rightmost,upmost),]
# Le premier a effectivement une durée de jeu plus longue, et le second s'est bien mieux vendu :
A data.frame: 2 × 23
Metrics.Review.ScoreMetrics.SalesMetrics.Used.PriceRelease.YearLength.All.PlayStyles.LeisureLength.All.PlayStyles.MedianLength.All.PlayStyles.PolledLength.Completionists.AverageLength.Main...Extras.LeisureLength.Main...Extras.Median⋯Features.Handheld.Features.Multiplatform.Features.Online.Metadata.GenresMetadata.Licensed.Metadata.PublishersMetadata.Sequel.Release.ConsoleRelease.RatingRelease.Re.release.
<int><dbl><dbl><int><dbl><dbl><int><dbl><dbl><dbl>⋯<chr><chr><chr><chr><chr><chr><chr><chr><chr><chr>
85573 1.7829.952008431.4833365.000000 15617.41667308.3833200⋯TrueTrueTrueSimulation TrueNintendoTrueNintendo WiiTTrue
8348212.3929.952008 30.13333 9.116667108 19.11667 30.8000 10⋯TrueTrueTrueRacing / DrivingTrueNintendoTrueNintendo WiiTTrue
In [26]:
# Mais qui sont-ils ?!
data[c(rightmost,upmost),"Title"]
  1. 'Animal Crossing: City Folk'
  2. 'Mario Kart Wii'
In [27]:
# Dans le plan 3-4 :
plot(res.pca, axes=3:4, choix="var")
No description has been provided for this image

Année de sortie complètement décorrélée du nombre de joueurs maximal : on ne voyait en effet pas très bien le rapport !