1 Introduction

1.1 Contexte

L’alimentation est la première préoccupation de l’homme d’un point de vue biologique. En effet, elle est synonyme de bon fonctionnement du corps humain et donc de bonne santé. Manger varié et équilibré est essentiel dans le cadre de la prévention des maladies.

Par conséquent, en France, de plus en plus de consommateurs sont attentifs à la provenance des aliments, à leur composition et leur qualité. Cependant, les choix alimentaires dépendent de différents facteurs comme la culture, la société, le revenu, les goûts de chacun etc.

On répartit généralement les aliments en 7 groupes, néanmoins leur consommation doit respecter ce qu’on appelle la pyramide alimentaire.

Figure 1 : Pyramide alimentaire (source : Food in Action)

Figure 1 : Pyramide alimentaire (source : Food in Action)

Cette pyramide se lit de bas en haut, c’est-à-dire de la famille alimentaire qui doit être consommée le plus souvent et en plus grande quantité (sur la journée) à la moins fréquente et en quantité diminuée.

Les aliments sont composés d’éléments qu’on appelle des nutriments. Les besoins nutritionnels nécessaires au bon fonctionnement du corps sont apportés par 3 grandes catégories de nutriments : les macronutriments (protéines, lipides, glucides…), les micronutriments (vitamines, oligo-éléments…) et les fibres.

Malheureusement, aucun aliment ne contient à la fois des lipides, des glucides, des protéines, des fibres, des minéraux et des vitamines. D’où l’utilisation de la pyramide alimentaire afin de choisir des aliments des différents groupes.

Les politiques de santé publique font, aujourd’hui, de l’alimentation une priorité en matière de prévention.

Anses, Agence nationale de sécurité sanitaire de l’alimentation, de l’environnement et du travail, contrôle la chaine alimentaire et évalue les risques sanitaires de la production primaire jusqu’à l’assiette du consommateur. Elle développe également des méthodes de diagnostic, mêne des travaux de recherche et conduit des expertises scientifiques d’evaluation des risques sur les produits. Elle assure enfin des missions de surveillance et de vigilance dans le domaine de la nutrition. L’Agence dispose des connaissances sur les apports nutritionnels de la population française indispensables pour orienter les politiques publiques en matière de nutrition.

Afin d’aider les consommateurs à évaluer la qualité des produits alimentaires qui lui sont proposés, en termes nutritionnels et selon des critères de santé, un label officiel Nutri-score a été mis en place en France en octobre 2017. Ce logo nutritionnel a été conçu par Santé publique France, à la demande de la Direction générale de la santé. Affiché sur la face avant des emballages, il attribue un score aux aliments selon leur qualité nutritionnelle et les classe dans une des 5 catégories identifiées par une lettre (du vert foncé au rouge) et une lettre (de A à E).

Figure 2 : Déclinaison Nutri-Score (source : Sante Publique France)

Figure 2 : Déclinaison Nutri-Score (source : Sante Publique France)

Le calcul du score (pour 100 grammes de produit) est évalué en fonction de la teneur :

  • en nutriments et aliments à favoriser (fibres, protéines, fruits et légumes…) ,
  • et en nutriments à limiter (graisses, acides gras saturés, sucres, sel, calories…).

L’algorithme est public et se base sur les éléments de composition des aliments déclarés de façon obligatoire. Ainsi, le Nutri-Score a déjà été intégré à différentes applications permettant de scanner le code barre des produits, notamment la base publique alimentaire Open Food Facts. Cette table des données répertorie les produits alimentaires dans le monde. Elle est remplie par des particuliers et peut-être consultée par tous.

1.2 Problématique(s) et objectif(s)

L’objectif de cette étude est :

  • d’analyser les nutriments présents dans les aliments ;
  • à partir des méthodes d’apprentissage non supervisé, essayé de retrouver les groupes attribués par l’Anses ;
  • à partir des méthodes d’apprentissage supervisé, prédire la classe d’un nouvel aliment ;
  • consevoir un score nutrionnel comme le Nutri-Score ;
  • observer le comportement des consommateurs en France.

1.3 Plan

Dans un premier temps, nous allons effectuer une analyse descriptive de notre jeu de données. Dans un deuxième temps, en passant par des méthodes d’apprentissage non supervisé, nous allons essayer de retrouver les différentes catégories attribuées par l’Anses. Dans un troixième temps, nous allons tenter de prédire la catégorie d’un nouveau produit issu d’une seconde table. Ensuite, nous réaliserons notre propre score nutritionnel. Pour finir, à partir d’autres tables issues d’une même étude, nous allons analyser le comportement des consommateurs.

2 Statistiques descriptives et représentation graphique

2.1 Présentation du jeu de données

En France, la base de données de référence sur la composition nutritionnelle des aliments est gérée par l’Anses. Nous avons selectioné la table TableCiqual2017_ExcelFR_2017 11 17.xls disponible sur le site data.gouv.fr. Elle fournit la composition nutritionnelle (c’est-à-dire les teneurs en lipides, acides gras, glucides, sucres, protéines, sel, vitamines et minéraux…) ainsi que les valeurs énergétiques de plus de 2800 aliments représentatifs de ceux consommés en France en 2017. Pour chaque aliment, l’Anses estime la composition moyenne en combinant plusieurs produits de marques différentes. Les données sont toujours calculées pour 100 grammes de la partie comestible de l’aliment, c’est-à-dire sans les os, les coquilles, le trognon etc.

#data <- read_excel("~/_M2 Maths Appli et Stat/Semestre 10 - MSS/Projet Données Massives/PBD open data/TableCiqual2017.xls") 
data <- read_excel("C:/Users/matic/Desktop/Projet Open Data/TableCiqual2017_ExcelFR_2017 11 17.xls")
 
data$alim_grp_code <- as.factor(data$alim_grp_code) 
data$alim_ssgrp_code <- as.factor(data$alim_ssgrp_code) 
data$alim_ssssgrp_code <- as.factor(data$alim_ssssgrp_code)
data$alim_code <- as.factor(data$alim_code) 

data$alim_grp_nom_fr <- as.factor(data$alim_grp_nom_fr) 
data$alim_ssgrp_nom_fr <- as.factor(data$alim_ssgrp_nom_fr) 
data$alim_ssssgrp_nom_fr <- as.factor(data$alim_ssssgrp_nom_fr)
data$alim_nom_fr <- as.factor(data$alim_nom_fr) 

alim_ssssgrp_nom_fr_cmp <- rep(NA, 2807)
alim_ssssgrp_code_cmp <- rep(NA, 2807)

for(i in 1:2807){
  if(is.na(data$alim_ssssgrp_nom_fr[i])){
    alim_ssssgrp_nom_fr_cmp[i] <- as.character(data$alim_ssgrp_nom_fr[i]) 
  }else{
    alim_ssssgrp_nom_fr_cmp[i] <- as.character(data$alim_ssssgrp_nom_fr[i]) 
  }

  if(data$alim_ssssgrp_code[i] == 0){
    alim_ssssgrp_code_cmp[i] <- as.character(data$alim_ssgrp_code[i])
  }else{
    alim_ssssgrp_code_cmp[i] <- as.character(data$alim_ssssgrp_code[i])
  }
}

alim_ssssgrp_nom_fr_cmp <- as.factor(alim_ssssgrp_nom_fr_cmp)
alim_ssssgrp_code_cmp <- as.factor(alim_ssssgrp_code_cmp)

# Imputation d'un jeu de donnees avec PCA
df <- as.data.frame(data[, 9:68])
ncomp <- estim_ncpPCA(df)
res_imp <- imputePCA(df, ncp = ncomp$ncp)
data[, 9:68] <- res_imp$completeObs
#paged_table(data) 

dimension <- dim(data)

On dispose d’un jeu de données contenant 2807 aliments et 68 variables :

  • 8 variables qualitatives :
    • Code du groupe d’aliments (alim_grp_code),
    • Code du sous-groupe d’aliments (alim_ssgrp_code),
    • Code du sous-sous-groupe d’aliments (alim_ssssgrp_code),
    • Nom du groupe d’aliments (alim_grp_nom_fr),
    • Nom du sous-groupe d’aliments (alim_ssgrp_nom_fr),
    • Nom du sous-sous-groupe d’aliments (alim_ssssgrp_nom_fr),
    • Code des aliments (alim_code),
    • Nom des aliments (alim_nom_fr) ;
  • 60 variables quantitatives : valeurs énergétiques, eau, protéines, glucides, lipides, sucres, amidon, fibres alimentaires, polyols totaux, cendres, alcool, acides organiques, les différents acides gras, cholestérol, sel chlorure de sodium, calcium, chlorure, cuivre, fer, iode, magnèsium, manganèse, phosphore, potassium, sélénium, sodium, zinc, rétinol, bétaCarotène et les différentes vitamines.

On observe dans cette table que les produits alimentaires sont répartis en 11 groupes d’aliments (alim_grp_nom_fr), qui eux-mêmes, sont séparés en sous-groupe (alim_ssgrp_nom_fr), 58 au total. Pour certains sous-groupes, les produits sont encore divisés dans différents blocs, notés les “sous-sous groupes” (alim_ssssgrp_nom_fr), 75 au total. On remarque donc que certains aliments n’ont pas de sous-groupe ou de sous-sous-groupe.

Les 11 groupes d’aliments sont les suivants :

levels(data$alim_grp_nom_fr)
##  [1] "aides culinaires et ingrédients divers"     
##  [2] "aliments infantiles"                        
##  [3] "boissons"                                   
##  [4] "entrées et plats composés"                  
##  [5] "fruits, légumes, légumineuses et oléagineux"
##  [6] "glaces et sorbets"                          
##  [7] "lait et produits laitiers"                  
##  [8] "matières grasses"                           
##  [9] "produits céréaliers"                        
## [10] "produits sucrés"                            
## [11] "viandes, œufs, poissons"

Pour les variables quantitatives on retrouve bien les 60 constituants comprenant les énergies (au nombre de 3) et les 7 principales familles de nutriments :

  • Les protéines comportant les variables protéines et protéines brutes ,
  • Les glucides (6) comportant les variables glucides, sucre, amidon, fibres alimentaires, polyols et alcool ,
  • Les lipides (20) comportant les différentes variables d’acides gras saturés (au nombre de 17) et les variables lipides, acides organiques et cholestérol,
  • Les minéraux (8) comportant les variables sel, cendres, calcium, magnésium, phosphore, potassium, sodium et chlorure,
  • Les oligoéléments (6) comportant les variables fer, zinc, iode, manganèse, sélénium et cuivre ,
  • L’eau
  • Les vitamines (14) comprenant les 12 variables vitamines et les variables rétinol et bêta-carotène.

Les nutriments ne sont pas tous de la même unité : les énergies sont en kcal/100g ou kJ/100g, les nutriments énergétiques (lipides, glucides et protéines) sont en g/100g excepté le cholestérol qui est en mg/100g, les minéraux sont en mg/100g excepté le sel et les cendres qui sont en g/100g, l’eau est en g/100g, les oligoéléments sont en mg/100g excepté l’iode et le sélénium qui sont en \(\mu\)g/100g et les vitamines sont en \(\mu\)g/100g excepté les vitamines E, C, B1, B2 B3, B5 et B6 qui sont en mg/100g.

On observe 3 valeurs énergétiques différentes. En effet, l’énergie des aliments peut-être calculée de plusieurs méthodes :

  • Les valeurs d’énergie, Règlement UE N° 1169/2011 prend en compte la teneur en protéines brutes, c’est-à-dire la teneur en azote total multipliée par le facteur 6,25, quel que soit l’aliment.
  • Les valeurs d’énergie, N x facteur Jones avec fibres, en kJ/100g ou en kcal/100g, sont calculées en prenant en compte les teneurs en protéines qui sont elles-mêmes estimées sur la base de la teneur en azote total et de facteurs spécifiques (dits facteurs de Jones), qui peuvent différer d’une famille d’aliments à une autre.

2.2 Analyse des variables qualitatives

2.2.1 Diagrammes en Bâtons du nombre d’aliments …

Cliquer sur un onglet et passer la souris sur le diagramme pour lire les données.

2.2.1.1 … par groupe

plot_ly(as.data.frame(table(data$alim_grp_nom_fr)), 
        x = ~Var1,
        y = ~Freq,
        marker = list(color = "rgb(93,71,139)")) %>%
  layout( title = "Histogramme correspondant au \nnombre d'aliments dans chaque groupe")%>%
  layout(xaxis = list(title = "Nom des groupes d'aliments"), yaxis = list(title = "Nombre d'aliments"))

2.2.1.2 … par sous-groupe

plot_ly(as.data.frame(table(data$alim_ssgrp_nom_fr)), 
        x = ~Var1, 
        y = ~Freq, 
        marker = list(color = "rgb(60,179,113)")) %>%
  layout( title = "Histogramme correspondant au \nnombre d'aliments dans chaque sous-groupe") %>%
  layout(xaxis = list(title = "Nom des sous-groupes d'aliments"), yaxis = list(title = "Nombre d'aliments"))

2.2.1.3 … par sous-sous-groupe

plot_ly(as.data.frame(table(data$alim_ssssgrp_nom_fr)), 
        x = ~Var1, 
        y = ~Freq, 
        marker = list(color = "rgb(123,104,238)")) %>%
  layout( title = "Histogramme correspondant au \nnombre d'aliments dans chaque sous-sous-groupe")%>%
  layout(xaxis = list(title = "Nom des sous-sous-groupes d'aliments"), yaxis = list(title = "Nombre d'aliments"))

2.2.2 Commentaires

Pour la variable catégorielle alim_grp_nom_fr, on observe que le groupe contenant le plus grand nombre d’aliments est viandes, oeufs, poissons avec 739 aliments et le plus bas est le groupe glaces et sorbets avec 26 aliments. De plus, pour la variable catégorielle alim_ssgrp_nom_fr, on constate que le groupe contenant le plus grand nombre d’aliments est legumes avec 208 aliments et le plus bas est le groupe sorbets avec 3 aliments. De même, pour la variable catégorielle alim_ssssgrp_nom_fr_cmp, on remarque que le groupe contenant le plus grand nombre d’aliments est legumes crus avec 113 aliments et le plus bas est le groupe sauces sucrees avec 2 aliments.

On en déduit alors, qu’importe la variable catégorielle, les aliments ne sont pas répartis de façon homogène dans les différents groupes.

On remarque que dans notre table il manque le nom de groupe de certains aliments car ils sont considérés comme aliments moyens. On détient tout de même leur code de groupe d’aliments (alim_grp_code). Nous traitons les données sans utiliser ces aliments moyens.

On observe que le sous-groupe contenant le plus d’aliments, legumes, n’appartient pas au groupe possédant le plus grand nombre d’aliments viandes, oeufs, poissons.

table(data$alim_ssgrp_nom_fr, data$alim_grp_nom_fr)[c(10,24,25,34,35,45,46,57,58), c(5, 11)] 
##                                         
##                                          fruits, légumes, légumineuses et oléagineux
##   charcuteries                                                                     0
##   fruits                                                                          93
##   fruits à coque et graines oléagineuses                                          49
##   légumes                                                                        208
##   légumineuses                                                                    37
##   poissons crus                                                                    0
##   poissons cuits                                                                   0
##   viandes crues                                                                    0
##   viandes cuites                                                                   0
##                                         
##                                          viandes, œufs, poissons
##   charcuteries                                               159
##   fruits                                                       0
##   fruits à coque et graines oléagineuses                       0
##   légumes                                                      0
##   légumineuses                                                 0
##   poissons crus                                              108
##   poissons cuits                                              61
##   viandes crues                                              153
##   viandes cuites                                             125

Lorsque l’on regarde, de plus près, la répartition des produits dans chaque sous-groupe pour les groupes viandes, oeufs, poissons et fruits, legumes, legumineuses et oleagineux, on a :

  • le groupe viandes, oeufs, poissons est divisé en 10 sous-groupe :
    • 25 aliments dans la catégorie mollusques et crustaces crus,
    • 15 aliments dans la catégorie mollusques et crustaces cuits,
    • 23 aliments dans la catégorie oeufs,
    • 108 aliments dans la catégorie poissons crus,
    • 61 aliments dans la catégorie poissons cuits,
    • 16 aliments dans la catégorie autres produits a base de viande,
    • 54 aliments dans la catégorie produits a base de poissons et produits de la mer,
    • 159 aliments dans la catégorie charcuteries
    • 153 aliments dans la catégorie viandes crues et
    • 125 aliments dans la catégorie viandes cuites ;
  • De même, le groupe fruits, legumes, legumineuses et oleagineux contient 5 sous-groupes :
    • 49 aliments dans la catégorie fruits à coque et graines oleagineuses,
    • 208 aliments dans la catégorie legumes,
    • 37 aliments dans la catégorie legumineuses
    • 93 aliments dans la catégorie fruits et
    • 50 aliments dans la catégorie pommes de terre et autres tubercules.

On remarque alors que la répartition des aliments dans les sous-groupe de ces 2 groupes est totalement différente. En effet, le sous-groupe legumes contient bien le plus d’aliments avec 208. Cependant, les autres sous-groupes du groupe fruits, legumes, legumineuses et oleagineux contiennent peu d’aliments (inférieurs à 100). A contrario, le groupe viandes, oeufs, poissons n’a pas de sous-groupe avec plus de 200 aliments mais plusieurs avec plus de 100 aliments. Ceci explique donc que le groupe viandes, oeufs, poissons est bien le groupe ayant le plus d’aliments.

plot_ly(as.data.frame(prop.table(table(data$alim_grp_nom_fr))), labels = ~Var1, values = ~Freq, type = "pie" ) %>%
  layout(title = "Diagramme circulaire correspondant à la proportion d'aliments dans chaque groupe (en %)")

Pour la variable catégorielle alim_grp_nom_fr, la catégorie viandes, oeufs, poissons contient 26.4% des aliments, la catégorie lait et produits laitiers contient 8.8% des aliments ou encore la catégorie glaces et sorbets ne contient que 0.9% des aliments.

On en conclut qu’il n’y a pas de répartition uniforme des produits dans chaque groupe. En effet, comme on a pu le voir précédemment, nous avons des groupes qui ont plus de sous-groupe que d’autres mais avec peu de produits. D’autre part, nous avons des groupes qui ont moins de sous-groupe que d’autres mais contenant beaucoup de produits.

2.3 Analyse des variables nutritionnelles

2.3.1 Etude de la variable Energie, Règlement UE N 1169/2011 (kcal/100g)

Comme nous l’avons décrit auparavant, nous avons 60 variables quantitatives : eau, protéines, glucides, etc. Nous allons tout d’abord nous concentrer sur la variable Energie, Règlement UE N 1169/2011 (kcal/100g) car elle est determinée par d’autres variables de la table. Cette valeur permet de savoir aussi l’apport nutritionnelle des aliments.

D’après les informations fournies par Anses sur la table, pour l’ensemble des aliments, la valeur énergétique a été calculée en utilisant les coefficients suivants :

  • pour les lipides : 37 kJ/g (9 kcal/g) ;
  • pour l’alcool (ethanol) : 29 kJ/g (7 kcal/g) ;
  • pour les protéines : 17 kJ/g (4 kcal/g) ;
  • pour les glucides (a l’exception des polyols) : 17 kJ/g (4 kcal/g) ;
  • pour les acides organiques : 13 kJ/g (3 kcal/g) ;
  • pour les polyols : 10 kJ/g (2,4 kcal/g) ;
  • pour les fibres alimentaires : 8 kJ/g (2 kcal/g).
energie <- data$`Energie, Règlement UE N° 1169/2011 (kcal/100g)`
boxplot(energie, horizontal = TRUE, col = "mediumspringgreen", ylim = c(0, 1000), 
        main = "Boite à moustache de la valeur énergétique", 
        xlab = "Valeur énergétique (en kcal/100g)")

##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     0.0    89.4   195.0   217.2   315.5   900.0

Pour la valeur énergétique, nous avons une teneur moyenne de 217.22 kcal/100g. On a 25% des aliments ayant une valeur énergétique qui ont une teneur inférieure ou égale à 89.4 kcal/100g. On a 75% des aliments ayant une valeur énergétique qui ont une teneur inférieure ou égale à 315.5 kcal/100g. On a 50% des aliments ayant une valeur énergétique qui ont une teneur inférieure ou égale à 195 kcal/100g. On a une teneur maximale de 900 et minimale de 0.

data %>% plot_ly(x = ~alim_grp_nom_fr,y = ~energie,split = ~alim_grp_nom_fr,type = 'box') %>% 
  layout(xaxis = list(title = "Groupe d'aliments"),yaxis = list(title = "Valeur énergetique (en kcal/100g)",zeroline = F))  %>%
  layout( title = "Boîtes à moustache de la valeur énergétique par groupe d'aliments")

Lorsque nous observons la distribution de la variable numérique Energie, Règlement UE N° 1169/2011 (kcal/100g) en fonction des différents groupes, on s’aperçoit qu’elles sont différentes. En effet, pour le groupe glaces et sorbets, il n’y a pas de grande dispertion entre les valeurs énergétiques. Elles se situent entre 120 kcal/100g et 359 kcal/100g, soit une différence de 239 kcal/100g. Or, pour le groupe matiere grasse, on a une très grande distribution des valeurs énergétiques, comprises entre 0 et 900 kcal/100g. On peut donc dire que dans ce groupe que 50% des aliments ont un très grand apport énergétique, entre 518 et 900 kcal/100g, par rapport aux autres aliments des autres groupes. Le groupe produits sucres est le second groupe ayant des aliments qui ont un apport énergétique entre 216 et 611 kcal/100g, soit une différence de 365 kcal/100g. On remarque aussi que les groupes boissons, fruit, legumes, legumineuses et oleagineux et viande, oeufs, poissons ont de nombreuses valeurs extrêmes. De plus, les aliments du groupe boissons sont les derniers en apport énergétique, 50% de ces aliments ont une valeur énergétique comprise entre 0 et 76.5 kcal/100g.

c <- ggplot(data, aes(x = energie)) 
c <- c + geom_density(fill="mediumturquoise") 
c <- c + labs(title = "Répartition des aliments en fonction de la valeur énergétique", 
              x = "Valeur énergetique (en kcal/100g)", 
              y = "Densité") 
d <- ggplot(data, aes(x = energie)) 
d <- d + geom_histogram(fill="mediumturquoise", bins = floor(sqrt(max(energie)))) 
d <- d + labs(x = "Valeur énergetique (en kcal/100g)", 
              y = "Nombre")
grid.arrange(c, d, nrow = 2)

Lorsque nous regardons la densité des aliments en fonction de la valeur énergétique, on observe une décroissance, donc plus la valeur énergétique augmente, plus le nombre d’aliments diminue. Ainsi, sur l’histogramme, on oberve un pic avec plus de 325 aliments qui ont une valeur énergétique comprise entre 210 et 240 kcal/100g alors qu’entre 840 et 870 kcal/100g on peut voir qu’aucun n’aliment ne possède de valeur énergétique. A partir de 330 kcal/100g on voit une nette décroissance du nombre d’aliments en fonction de l’énergie.

2.3.2 Etude de la corrélation entre les variables nutritionnelles

Nous allons regarder s’il y a un lien entre les variables numériques. On recupère les variables les plus corrélées entre elles (\(|corr(var_i,var_j)| > 0.75\)).

M <- as.matrix(data[, 9:68]) 
R<-cor(M)
rownames(R) <- as.character(seq(9, 68, 1))
colnames(R) <- as.character(seq(9, 68, 1))
N <- matrix(NA, 60, 60)
for(i in 1:60){
  for(j in 1:60){
    if(abs(R[i, j]) > 0.75 & (i != j)){
      N[, j] = R[, j]
    }
  }
}
col <- colorRampPalette(c("darkorange3", "white", "darkorchid4"))(20)
corrplot(R[-c(2, 3, 5, 6, 7, 9, 10, 11, 12, 14, 15, 24, 29, 30, 33, 35, 37:44, 46:60), 
           -c(2, 3, 5, 6, 7, 9, 10, 11, 12, 14, 15, 24, 29, 30, 33, 35, 37:44, 46:60)], col = col, tl.col = "black", tl.cex = 1, tl.srt = 45,type = "upper", order = "hclust")

On constate que : La variable Energie, Règlement UE N° 1169/2011 (n°9) est corrélée négativement avec l’Eau (n°12) et positivement avec les Lipides (n°16). Les Lipides sont corrélés aussi avec AG satures (n°24) et AG monoinsatures (n°25). La Cendre (n°21) est corrélée avec le sel (n°42), le chlorure (n°44) et le sodium (n°53). Les AG satures sont corrélés aussi aux AG 16:0, palmitique (n°33). Les AG monoinsatures sont corrélés aussi aux AG 18:1 9c (n9), oléique (n°35). Les AG polyinsaturés (n°26) sont corrélés avec AG 18:2 9c,12c (n6) linoléique (n°36). Les AG 4:0, butyrique (n°27) sont corrélés avec les AG 6:0, caproïque (n°28) et les AG 10:0, caprique (n°30). Les AG 8:0, caprylique (n°29) sont corrélés avec les AG 10:0, caprique et les AG 12:0, laurique (n°31). Les AG 10:0, caprique sont corrélés aussi aux AG 6:0, caproïque et aux AG 4:0, butyrique. Les AG 16:0, palmitique sont corrélés aussi aux AG 18:0, stéarique (n°34). Les AG 20:5 5c,8c,11c,14c,17c (n3) EPA (n°39) sont corrélés avec les AG 22:6 4c,7c,10c,13c,16c,19c (n3) DHA (n°40). Le sel est corrélé aussi au chlorure et au sodium. Le chlorure est corrélé aussi au sodium. Les autres variables ont une corrélation modérée ou proche de 0.

Nous allons nous intéresser de plus près aux corrélations entre les variables Sel,Sodium, et Cendres car ce sont les plus corrélées postivement. Et aux variables Energie, Règlement UE N° 1169/2011 et Eau car ce sont les plus corrélées négativements. En effet, ceci est évident car il n’y a pas d’Energie dans l’Eau.

Nous savons déjà que les variables Sel,Sodium et Cendres appartiennent à la même famille de nutriments, celle des minéraux. De plus, le Sodium est contenu dans le Sel (NAcl) et n’existe pas tout seul, il n’est donc pas pertinent d’étudier leur corrélation car c’est évident qu’elle doit être extrêmement forte.

2.3.2.1 Test de Spearman

On cherche à estimer l’association entre deux variables. Nous utilisons le test de Spearman car nos données ne proviennent pas d’une distribution normale. La statistique rhô de Spearman est basée sur un test de rang.

cor.test(data$`Energie, Règlement UE N° 1169/2011 (kcal/100g)`, data$`Eau (g/100g)`, method = "spearman") 
## 
##  Spearman's rank correlation rho
## 
## data:  data$`Energie, Règlement UE N° 1169/2011 (kcal/100g)` and data$`Eau (g/100g)`
## S = 6812300000, p-value < 2.2e-16
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##        rho 
## -0.8480762
cor.test(data$`Sel chlorure de sodium (g/100g)`, data$`Cendres (g/100g)`, method = "spearman") 
## 
##  Spearman's rank correlation rho
## 
## data:  data$`Sel chlorure de sodium (g/100g)` and data$`Cendres (g/100g)`
## S = 1014500000, p-value < 2.2e-16
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##       rho 
## 0.7247783
cor.test(data$`Cendres (g/100g)`, data$`Sodium (mg/100g)`, method = "spearman") 
## 
##  Spearman's rank correlation rho
## 
## data:  data$`Cendres (g/100g)` and data$`Sodium (mg/100g)`
## S = 1.097e+09, p-value < 2.2e-16
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##       rho 
## 0.7024058

Le coefficient de Spearman permet de tester la significativité de la corrélation entre deux variables. Pour les variables, Energie, Règlement UE N° 1169/2011 et Eau on obtient un rhô de -0.85, ce qui est proche de -1, cela signifie qu’il y a bien une forte corrélation négative entre ces deux variables. En effet, comme rhô est négatif leur liaison est donc décroissante, lorsque l’Energie augmente l’Eau diminue et inversement.

En revanche, pour les variables Sel et Cendres et les variables Cendres et Sodium on obtient un rhô respectivement de 0.72 et 0.70. Il y a bien une corrélation croissante (car rhô positf) entre ces variables mais elle n’est pas très forte.

2.3.2.2 Régression linéaire

cendre <- data$`Cendres (g/100g)`
sel <- data$`Sel chlorure de sodium (g/100g)`
sodium <- data$`Sodium (mg/100g)`
eau <- data$`Eau (g/100g)`

mod1 <- lm(energie ~ eau) 
mod2 <- lm(sodium ~ cendre)
mod3 <- lm(sel ~ cendre)

coef1 <- coefficients(mod1)
coef2 <- coefficients(mod2)
coef3 <- coefficients(mod3)

eq1 <- paste0("y = ", round(coef1[2], 2), "*x + ", round(coef1[1], 2))
eq2 <- paste0("y = ", round(coef2[2], 2), "*x + ", round(coef2[1], 2))
eq3 <- paste0("y = ", round(coef3[2], 2), "*x + ", round(coef3[1], 2))

e <- ggplot(data, aes(eau, energie, colour=alim_grp_nom_fr))
e <- e + geom_point()
e <- e + geom_abline(intercept = coef1[1], slope = coef1[2],  
                     color = "mediumblue", linetype = "dashed") + 
  ggtitle("Nuages de points entre deux variables avec droite de régression linéaire") 
e <- e + labs(subtitle = eq1) 
                     

f <- ggplot(data, aes(cendre, sodium, colour=alim_grp_nom_fr))
f <- f + geom_point() + theme(legend.position="none")
f <- f + geom_abline(intercept = coef2[1], slope = coef2[2], 
                     color = "mediumblue", linetype = "dashed") + labs(subtitle = eq2) 

g <- ggplot(data, aes(cendre, sel, colour=alim_grp_nom_fr))
g <- g + geom_point() + theme(legend.position="none")
g <- g + geom_abline(intercept = coef3[1], slope = coef3[2], 
                     color = "mediumblue", linetype = "dashed") + labs(subtitle = eq3)

grid.arrange(e, arrangeGrob(f, g, ncol = 2), nrow = 2)

Sur la graphique représentant les nuages de points les variables Sel et Cendre, et, Sodium et Cendre sont modérément positives. En effet, on observe que certains points sont proches de la ligne, mais d’autres en sont éloignés. Les relations sont positives car lorsqu’une variable augmente l’autre augmente aussi.

A contrario, la relation linéaire entre Energie et Eau est modérément négative. En effet, il y a des points qui qui suivent la droite de régréssion linéaire, mais d’autres en sont très éloignés. On confirme bien que la relation est négative car lorsqu’une variable augmente l’autre diminue.

On obtient bien la même conclusion qu’avec le test de Spearman.

3 Classification des aliments par rapport aux nutritions

3.1 Analyse en composante principale

Nous allons donc pratiquer une ACP. Nous devons tout d’abord choisir le nombre de composantes principales que nous allons utiliser.

df <- as.data.frame(data[, 9:68])
res <- PCA(df, graph = FALSE) 

fviz_eig(res,choice = "eigenvalue", addlabels = TRUE, ylim = c(0, 10), barfill = "mediumvioletred") + labs(title = "Ebouli des valeurs propres",
         x = "Composantes principales", y = "Valeurs Propres")

Si l’on utilise la règle de Kaiser (choisir les valeurs propres plus grandes que 1) alors nous aurons le choix entre 16 premières valeurs propres, ce qui conduit à un taux d’inertie expliquée de 73.43%. Nous aurons une bonne projection des variables et des individus. Mais pour la représentation graphique des données, on choisira les deux premières composantes principales. Or cela va nous conduire à un taux d’inertie expliquée de 25.16%, infèrieur à 50%. Nous aurons une grande perte de la qualité de projection des données.

fviz_pca_var(res, col.var = "cos2", select.var = list(cos2 = 0.5), 
             gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
             repel = TRUE, # Évite le chevauchement de texte
             title = "Projection des variables sur les 2 premières \ncomposantes principales (ayant un cos2 > 0.5)")

Les flèches des variables les plus proches du cercle sont les mieux projetées. Les flèches entre la teneur en Eau et l’Energie sont de sens opposé. Donc ces variables sont corrélées négativement. On peut donc dire que les produits ayant des valeurs énergétiques élevées, ont une teneur en eau faible. De plus, les flèches entre les valeurs énergétiques et les teneurs en acide gras sont dans le même sens et sont proches. Donc ces variables sont corrélées positivement. On peut donc dire que les produits ayant des valeurs énergétiques élevées, ont des teneurs en acides gras élevées aussi. De même, les flèches des vitamines sont plus ou moins perpendiculaires à celles des valeurs énergétiques. Donc ces variables ont une corrélation nulle. On peut donc dire qu’il n’y a pas de relation linéaire entre les vitamines et la valeur énergétique.

3.1.1 Projection des produits …

Cliquer sur un onglet.

3.1.1.1 … par groupe

# Groupe 

df <- as.data.frame(data[, c(1, 9:68)])
res <- PCA(df, quali.sup = 1, graph = FALSE) 

fviz_pca_ind(res, geom.ind = "point", habillage = 1, palette = rainbow(11), legend.title = "Groupes alimentaires",
             title = " Projection des individus sur les 2 premieres composantes principales")

3.1.1.2 … par sous-groupe

# Sous Groupe 

df <- as.data.frame(data[, c(2, 9:68)])
res <- PCA(df, quali.sup = 1, graph = FALSE)

plot(res, choix = "ind", label = "none", invisible = "quali", habillage = 1, 
     title = "Projection des individus sur les 2 premieres composantes principales")

3.1.1.3 … par sous-sous-groupe

# Sous sous Groupe 

df <- as.data.frame(cbind(alim_ssssgrp_code_cmp, data[, 9:68]))
res <- PCA(df, quali.sup = 1, graph = FALSE)

plot(res, choix = "ind", label = "none", invisible = "quali", habillage = 1, 
     title = "Projection des individus sur les 2 premieres composantes principales") 

3.1.2 Commentaires

On remarque que les points les plus éloignés du centre sont ceux qui contributs le moins à l’inertie expliquée et peuvent être enlevés. Comme on l’a vu, le premier axe oppose la teneur énergétique avec la teneur en eau. En effet, l’aliment tout à droite de cet axe, Beurre à 82% MG, doux fait parti des matières grasses. Ainsi, on peut donc dire que le Beurre à 82% MG, doux a des valeurs énergétiques élevées et des teneurs en acides gras élevées aussi. Pour le second axe, les vitamines sont corrélées positivement. En effet, l’aliment le plus haut de l’axe 2, Levure de boulanger, compressee fait partie des ingrédients divers. Ainsi, on peut donc dire que la Levure de boulanger, compressee contient un nombre élevé de vitamines, plus précisement, la Vitamine B3 ou PP ou Niacine (mg/100g), la Vitamine B5 ou Acide pantotheque (mg/100g) ou encore la Vitamine B6 (mg/100g).

Avant d’utiliser le clustering, on regarde graphiquement si nous pouvons délimiter des frontières entre chaque catégorie. Si on regarde par groupe, on a pu voir qu’il n’y a pas de frontière entre les groupes. Ils sont superposés les uns sur les autres. Par sous-groupe, il n’y a pas d’amélioration au niveau des frontières. Les points sont assez hétérogènes. Par sous-sous-groupe, nous ne voyons toujours pas de délimitation entre chaque groupe.

Il est fort probable que le clustering ne fonctionne pas a première vu. Soit il faut regarder en fonction des autres composantes principales si on peut détérminer une délimitation. Soit il faudrait séparer certaine catégorie, comme les liquides et les solides qui sont assez différent sur le plan nutritionnel.

Nous utilisons le clustering uniquement pour les aliments par groupe.

3.2 Regroupement des aliments les plus proches nutritionnellements

3.2.1 Projection des produits avec 11 clusters

# Groupe

df <- as.data.frame(data[, 9:68])
res <- PCA(df, graph = FALSE, ncp = 16)
n <- nrow(df)

C <- res$ind$coord
delta <- dist(C[, 1:16])^2/(2*n)
tree <- hclust(delta, method = 'ward.D') 

memb <- cutree(tree, k = 11) 
memb <- as.factor(memb)

# Projection
df <- as.data.frame(cbind(memb, data[, 9:68]))
res <- PCA(df, quali.sup = 1, graph = FALSE)

fviz_pca_ind(res,
             geom.ind = "point",
             habillage = 1,
             palette = rainbow(11),
             legend.title = "Groupes alimentaires",
             title = " Projection des individus sur les 2 premieres composantes principales")

Pour un regroupement à 11 clusters avec 2 composantes principales, on distincte bien les différents groupes mais elles ne reflètent pas visuellement les catégories de notre ensemble. Pour un regroupement à 11 clusters avec 16 composantes principales, on peut distinguer les groupes mais certains groupes se chevauchent les uns sur les autres. Plus on augmente le nombre de cluster et plus les groupes sont de moins en moins distinguables.

Nous utilisons la fonction catdes() pour voir si il y a un lien entre les catégories de notre ensemble et les clusters déterminés par la fonction cutree().

3.2.2 Test de liaison entre les clusters créé par la fonction et les catégories de l’ensemble de départ

# Groupe 

gp <- as.factor(cutree(tree, k = length(levels(data$alim_grp_code)))) 
test <- catdes(data.frame(gp, data[, -c(2, 3, 5, 6, 7, 8)]), num.var = 1) 

#table(gp, data$alim_grp_code) 

#cluster1
print(test$category$`1`[1:2, ], digits = 2)
print(test$quanti$`1`[1:5, ], digits = 2)

#...#

#cluster11
print(test$category$`11`[1:2, ], digits = 2) 
print(test$quanti$`11`[1:5, ], digits = 2)

Cluster 1 :

La catégorie alim_grp_nom_fr=viandes, oeufs, poissons (resp. alim_grp_code=4) est surreprésentée ({v.test = Inf} > 0) chez les individus (les aliments) du cluster 1.

Pour le cluster 1 :

  • 93.60% (resp. 93.70%) des produits qui possèdent “alim_grp_nom_fr=viandes, oeufs, poissons”, possèdent le “cluster 1” ;
  • 68.04% (resp. 68.73%) des produits possédant “cluster 1”, possèdent “alim_grp_nom_fr=viandes, oeufs, poissons” ;
  • 26.30% (resp. 26.60%) de la population totale possède un “alim_grp_nom_fr=viandes, oeufs, poissons”.

De même, on regarde les variables quantitatives qui sont significatives pour le cluster.

On peut donc dire que les variables Protéines..g.100g, Protéines.brutes..N.x.6.25..g.100g, Vitamine.B3.ou.PP.ou.Niacine..mg.100g, Zinc..mg.100g, et AG.20.4.5c.8c.11c.14c..n6…arachidonique..g.100g sont surreprésentées (v.test > 0) et sont liées de manière significative au cluster 1 (p.value < 0.01).

Or la catégorie alim_grp_nom_fr=viandes, oeufs, poissons (resp. alim_grp_code=4) est surreprésentée aussi ({v.test = 5.3} > 0) chez les individus (les aliments) du cluster 8. Mais, pour le cluster 8 :

  • 1.6% (resp. 1.6%) des produits qui possèdent “alim_grp_nom_fr=viandes, oeufs, poissons”, possèdent le “cluster 8” ;
  • 100% (resp. 100%) des produits possédant “cluster 8”, possèdent “alim_grp_nom_fr=viandes, oeufs, poissons” ;
  • 26% (resp. 27%)de la population totale possède un “alim_grp_nom_fr=viandes, oeufs, poissons”.

De plus, la variable Rétinol..µg.100g, Cuivre..mg.100g, Vitamine.B12..µg.100g, Vitamine.B9.ou.Folates.totaux..µg.100g et Vitamine.B2.ou.Riboflavine..mg.100g sont surreprésentées (v.test > 0) et sont liées de manière significative au cluster 1 (p.value < 0.01).

Tableau recapitulatif :

Cluster Groupe P-valeur V-test
1 4 \(0\) \(Inf\)
2 6 \(1.3 \times 10^{-98}\) \(21\)
3 5 \(7.2 \times 10^{-60}\) \(16\)
4 3 \(3.8 \times 10^{-148}\) \(26\)
5 3 \(1.6 \times 10^{-25}\) \(10\)
4 9 \(2.5 \times 10^{-49}\) \(15\)
7 10 \(1.7 \times 10^{-41}\) \(13\)
8 4 \(1.2 \times 10^{-07}\) \(5.3\)
9 9 \(5.5 \times 10^{-06}\) \(4.5\)
10 10 \(4.5 \times 10^{-16}\) \(8.1\)
11 10 \(1.4 \times 10^{-06}\) \(4.8\)

En fin de compte, le clustering n’est pas une bonne méthode d’apprentissage non supervisé sur nos données.

3.3 Synthèse de notre classification d’aliment

Nous avons également essayé la méthode des k-means, cela n’a pas été plus informatif. Les groupes ne sont jamais bien délimités, ils se chevauchent. Selon les méthodes, ACP, Clustering, K-means certains aliments ne sont pas dans le même groupe à chaque fois.

De plus, ayant un taux d’inertie expliquée de 25.16%, on suppose alors que la grande pertie de la qualité de projection des données doit contribuer au chevauchement des groupes.

Finalement, les méthodes d’apprentissage supervisé ne nous ont pas permises de retrouver les catégories des aliments attribuées par l’Anses.

4 Classification de nouveaux aliments dans notre base

Notre objectif suivant est d’essayé de classer un produit qui n’est pas dans notre table avec de l’apprentissage supervisé. Pour cela, nous allons utiliser plusieurs méthodes. Nous allons tout d’abord séparer notre jeu de données en un échantillon d’apprentissage (soit 80% de notre data) et un échantillon de validation (soit 20% de notre data).

4.1 Choix du modèle

data_train <- data[, c(1, 12:13, 17:19, 21:22, 24:26, 41, 43:56, 58, 61)]
n <- 2807
p <- 27
y <- as.data.frame(data_train[, 1])
x <- as.data.frame(data_train[, -1])

B = 10
tbc = matrix(NA, B, 5)

TBC <- function(y_pred, y_test){
  sum(y_pred == y_test)/length(y_test)
}

for (b in 1:B){
  tr = sample(1:n, floor(n*80/100))
  train = as.data.frame(data_train[tr, ])
  test = as.data.frame(data_train[-tr, ])
  
  # Methodes
  g_lda = lda(alim_grp_code ~ ., data = train)
  mod = svm(alim_grp_code ~ ., data = train, type = "C-classification") 
    tree <- rpart(alim_grp_code ~ ., data = train, cp = 0, method = "class", minsplit = 2)
  tree1 = prune(tree, cp = tree$cptable[which.min(tree$cptable[ ,"xerror"]), 1])
  rf = randomForest(train[, -1], train[, 1], mtry = floor(sqrt(p)), nodesize = 1, ntree = 400) 
    rfdef <- randomForest(train[, -1], train[, 1], important = TRUE)
    rfdefimp <- rfdef$importance[, 1]  
    rfdefimpsort <- sort(rfdefimp, index.return = TRUE, decreasing = TRUE) 
    varsel <- rfdefimpsort$ix[1:27]
    xs <- train[, varsel+1]
  rfs = randomForest(xs, train[, 1])
  
  # prediction 
  pred_lda = predict(g_lda, test[, -1])$class
  pred_svm = predict(mod, test[, -1], type = "class")
  pred_tree = predict(tree1, test[, -1], type = "class")
  pred_rf = predict(rf, test[, -1])
  pred_rfs = predict(rfs, test[, -1])
  
  #Taux de bon classement
  tbc[b, 1] = TBC(pred_lda, test$alim_grp_code)
  tbc[b, 2] = TBC(pred_svm, test$alim_grp_code)
  tbc[b, 3] = TBC(pred_tree, test$alim_grp_code)
  tbc[b, 4] = TBC(pred_rf, test$alim_grp_code)
  tbc[b, 5] = TBC(pred_rfs, test$alim_grp_code)
}

boxplot(tbc, main = sprintf("Taux de bon classement, B = %s fois", B), 
        names = c("LDA", "SVM", "CART", "Random Forest", "VI"), ylim = c(0.65, 0.95), 
        col = 2:6) 
abline(h = 0.90, col = "black", lty = 3, lwd = 1) 
abline(h = 0.85, col = "black", lty = 3, lwd = 1) 
abline(h = 0.80, col = "black", lty = 3, lwd = 1) 
abline(h = 0.75, col = "black", lty = 3, lwd = 1) 
abline(h = 0.70, col = "black", lty = 3, lwd = 1)

summary(tbc) 
##        V1               V2               V3               V4        
##  Min.   :0.6726   Min.   :0.7811   Min.   :0.7865   Min.   :0.8737  
##  1st Qu.:0.6851   1st Qu.:0.8087   1st Qu.:0.7954   1st Qu.:0.8919  
##  Median :0.6975   Median :0.8158   Median :0.8007   Median :0.8977  
##  Mean   :0.6954   Mean   :0.8130   Mean   :0.8036   Mean   :0.8963  
##  3rd Qu.:0.7011   3rd Qu.:0.8221   3rd Qu.:0.8132   3rd Qu.:0.9035  
##  Max.   :0.7171   Max.   :0.8274   Max.   :0.8203   Max.   :0.9110  
##        V5        
##  Min.   :0.8737  
##  1st Qu.:0.8923  
##  Median :0.9004  
##  Mean   :0.8984  
##  3rd Qu.:0.9052  
##  Max.   :0.9146

La méthode Ramdom Forest est celle qui obtient le meilleur taux de bon classement sur notre ensemble de validation. Passons à la prédiction.

4.2 Prédiction de nouveaux aliments

Comme ensemble test nous avons choisi une base de données australienne nommée Food_nutrient_database.xlsx disponible sur le site Food Standards Australia New Zealand publiée en janvier 2019. Food Standards Australia New Zealand (FSANZ) est un organisme gouvernemental de la santé publique australienne. FSANZ élabore des normes alimentaires pour l’Australie et la Nouvelle-Zélande.

La base de données fournit la composition nutritionnelle ainsi que les valeurs énergétiques de 1535 aliments représentatifs de ceux qui sont le plus souvent consommés par des australiens ou utilisés en tant qu’ingrédients dans d’autres aliments.
Nous avons choisi la feuille Excel donnant tous les aliments et toutes les boissons pour 100g de portion comestible.

#data_test <- read_excel("~/_M2 Maths Appli et Stat/Semestre 10 - MSS/Projet Données Massives/PBD open data/Food_nutrient_database.xlsx",sheet = "All solids & liquids per 100g")  
data_test <- read_excel("C:/Users/matic/Desktop/Projet Open Data/Data_Test_Food_nutrient_database.xlsx",sheet = "All solids & liquids per 100g") 

data_test <- na.omit(data_test)
data_test <- data_test[, -1]
dimen <- dim(data_test)

Parmi les 253 variables correspondantes aux différents nutriments des aliments on a retrouvé 27 variables semblables à celles qui sont dans notre base de données initiale. Ces variables sont les suivantes :

colnames(data_test[, c(4:30)])
##  [1] "Eau (g/100g)"                 "Protéines (g/100g)"          
##  [3] "Sucres (g/100g)"              "Amidon (g/100g)"             
##  [5] "Fibres alimentaires (g/100g)" "Cendres (g/100g)"            
##  [7] "Alcool (g/100g)"              "AG saturés (g/100g)"         
##  [9] "AG monoinsaturés (g/100g)"    "AG polyinsaturés (g/100g)"   
## [11] "Cholestérol (mg/100g)"        "Calcium (mg/100g)"           
## [13] "Chlorure (mg/100g)"           "Cuivre (mg/100g)"            
## [15] "Fer (mg/100g)"                "Iode (µg/100g)"              
## [17] "Magnésium (mg/100g)"          "Manganèse (mg/100g)"         
## [19] "Phosphore (mg/100g)"          "Potassium (mg/100g)"         
## [21] "Sélénium (µg/100g)"           "Sodium (mg/100g)"            
## [23] "Zinc (mg/100g)"               "Rétinol (µg/100g)"           
## [25] "BetaCarotène (µg/100g)"       "Vitamine E (mg/100g)"        
## [27] "Vitamine C (mg/100g)"
head(data_test) 

En retirant les aliments ayant des valeurs non saisies on obtient donc un échantillon test contenant 594 aliments et 30 variables. Notre modèle Random Forest a appris uniquement sur ces 27 variables présentes dans notre échantillon d’apprentissage.

y_pred <- predict(rf, data_test[, -c(1:3)]) 
y_pred = as.factor(y_pred)
levels(y_pred) = c("entrées et plats composés", "fruits, légumes, légumineuses et oléagineux", 
                  "produits céréaliers", "viandes, oeufs, poissons", "lait et produits laitiers",
                  "boissons", "produits sucrés", "glaces et sorbets", "matières grasses", 
                  "aides culinaires et ingrédients divers", "aliments infantiles")
data_pred <- cbind(y_pred, data_test) 

data_pred[c(3, 10, 30, 80, 200), c(1, 2)]

On affiche au hasard 5 aliments de la nouvelle table avec le groupe qui leur a été attribué. Ces 5 aliments là sont bien prédits. Mais regardons plus en détail.

plot_ly(as.data.frame(y_pred), 
        y = ~y_pred, 
        marker = list(color = "rgb(102,205,170)")) %>%
  layout( title = "Histogramme correspondant au nombre d'aliments dans chaque groupe") %>%
  layout(xaxis = list(title = "Nombre d'aliments"), yaxis = list(title = "Nom des groupes d'aliments"))
ind <- which(data_pred == "matières grasses") 
data_pred[ind, 2]
##  [1] "Mayonnaise, traditional (greater than 70% fat), commercial"                                  
##  [2] "Dairy blend, butter & edible oil spread (approximately 80% fat), sodium 600 mg/100 g"        
##  [3] "Margarine spread, polyunsaturated (70% fat), reduced salt (sodium 280 mg/100 g)"             
##  [4] "Margarine spread, monounsaturated (greater than 65% fat), reduced salt (sodium 360 mg/100 g)"
##  [5] "Margarine spread, monounsaturated, reduced fat (55% fat) & salt (sodium 380 mg/100 g)"       
##  [6] "Margarine spread, monounsaturated, reduced fat (less than 65% fat)"                          
##  [7] "Margarine spread, olive oil blend (65% fat), reduced salt (sodium 360mg/100 g)"              
##  [8] "Oil, blend of polyunsaturated vegetable oils"                                                
##  [9] "Oil, canola"                                                                                 
## [10] "Oil, copha"                                                                                  
## [11] "Oil, olive"                                                                                  
## [12] "Oil, peanut"                                                                                 
## [13] "Oil, soybean"                                                                                
## [14] "Oil, sunflower"                                                                              
## [15] "Dripping, beef"                                                                              
## [16] "Fat, solid, vegetable oil based"                                                             
## [17] "Pork, loin chop, separable fat, raw"                                                         
## [18] "Pork, loin roast, separable fat, raw"                                                        
## [19] "Chicken, separable fat, composite, raw"                                                      
## [20] "Chicken, separable fat, composite, cooked, no added fat"                                     
## [21] "Duck, skin & fat, raw"

On remarque qu’il n’y a pas d’aliments infantiles et de glaces et sorbets dans la nouvelle base de données. En effet, lorsque l’on regarde les aliments du groupe aliments infantiles, il contient des produits similaires à d’autres groupes, surtout comme les groupes laits et produits laitiers et produits céréaliers. De même, pour les produits du groupe glaces et sorbets qui sont souvent à base de lait et sucrés, ils vont plutôt se retrouver bien évidemment dans le groupe laits et produits laitiers ou celui de produits sucrés.

On remarque que le groupe contenant le plus d’aliments est celui de viandes, oeufs, poissons avec 223 suivi du groupe fruits, légumes, légumineuses et oléagineux avec 142. On retrouve les mêmes groupes les plus nombreux que dans notre base de données d’apprentissage.

D’autre part, en regardant les produits dans chaque groupe prédit, on voit que certains ne sont pas à leur place comme le produit “Céréales de petit-déjeuner, son de blé, pellets, vitamines B1, B2 et folate ajoutées” qui se retrouve dans le groupe “aides culinaires et ingrédients divers” au lieu d’être dans le groupe produits céréaliers. Ou encore, dans le groupe des matière grasse (affiché ci-dessus), on a “Porc, steak sur patte, graisse séparable, cru”, “Porc, côtelette, graisse séparable, crue”, “Porc, rôti de longe, graisse séparable, cru”, “Steak de porc, médaillon ou longe, gras séparable, cru”, “Poulet, graisse dissociable, composite, cru”, “Poulet, graisse séparable, composite, cuit, sans matière grasse ajoutée” et “Canard, peau et graisse, cru” or ils contiennent tous de la graisse séparable alors, d’une part, ces produits sont bien placés par rapport à leurs nutriments, d’autre part d’après leur nom ils devraient être dans le groupe viandes, poissons, oeuf. Il faudra soit modifier d’autres paramètres ou soit le faire manuellement pour que par la suite, l’algorithme apprend sur une nouvelle base un peu plus complète.

4.3 Conclusion

Pour conclure, on est plutôt satisfaites de notre modèle de prédiction. En effet, malgrès quelques erreurs, qui restent compréhensibles, notre modèle arrive à classer des aliments provenant d’une autre table.

5 Elaboration d’un score nutritionnel

Pour mettre en place notre score nutritionnel, nous avons décidé de nous inspirer du Nutri-Score élaboré par Santé Publique France.

Le calcul du Nutri-score est basé sur plusieurs nutriments (pour 100g de produit). Ceux dont la consommation excessive nuit à la santé , 0 à 10 points sont attribués à chacun :

  • La valeur énergétique (Kcal/KJ)
  • La quantité d’acides gras saturés (g)
  • La quantité de sucres (g)
  • La quantité de Sodium (mg)

Et ceux favorisant une bonne santé, 0 à 5 points sont attribués :

  • La quantité de protéines (g)
  • Les fibres (g)
  • Les fruits et légumes (%)

Il est calculé de façon identique pour tous les aliments, sauf pour les fromages, les matières grasses végétales ou animales, les boissons. Le score se compose de deux dimensions : les points positifs (nutriments bons) et les points négatifs (nutriments défavorables). On soustrait les bons points des mauvais et on obtient une note. La note finale du Nutri-score est donc comprise entre une valeur théorique de - 15 (le plus favorable sur le plan nutritionnel) et une valeur théorique de + 40 (le plus défavorable). Enfin, on attribue une lettre selon si c’est un solide ou un liquide grâce au tableau suivant :

Figure 3 : Attribution de la couleur du Nutri-Score (source : Ministère de l'Agriculture)

Figure 3 : Attribution de la couleur du Nutri-Score (source : Ministère de l’Agriculture)

Le Nutri-Score n’étant pas adapté aux aliments infantiles destinés aux enfants de 0 à 3 ans, nous avons alors retiré les aliments infantiles de notre base de données.

N’ayant pas la même valeur énergétique et le nutriment pourcentage de fruits et légumes, nous avons décidé de ne pas les utiliser pour élaborer notre score nutritionnel. Notre note finale va donc de -10 à 30 points.

Notre score a été calculé de la même façon pour tous les aliments sauf pour ceux du groupe boissons. Leur score dépend uniquement de la teneur de sucres (de 0 à 10 points).

Nous avons utilisé le tableau d’attribution des lettres du Nutri-score selon si c’est un liquide ou un solide pour notre score nutritionnel.

Ainsi nous obtenons notre base de données avec un score et une lettre pour chaque aliment.

Pour mieux visualiser notre score nutritionnel nous avons mis en place une application intéractive (shiny) disponible dansla Dropbox. En voici une capture.

Figure 4 : Capture de l'application shiny de notre score nutritionnel.

Figure 4 : Capture de l’application shiny de notre score nutritionnel.

6 Consommations et habitudes alimentaires en France Métropolitaine

Grâce à l’étude INCA 2 (Etude Individuelle Nationale des Consommations Alimentaires 2), réalisée en 2006/2007, nous avons à disposition des tables de données disponibles sur le site. data.gouv.fr. Elle nous permet de connaître le comportement et la consommation alimentaire des personnes de 3 à 79 ans vivant en France métropolitaine (hors Corse). Sur les 8 tables, nous avons décidé d’en choisir 2 par rapport à notre thématique : la table de description des individus et la table des apports journaliers en nutriment.

Nous allons analyser la table de description des individus.

6.1 Comportement des individus

Cette table regroupe les informations sur les habitudes de vie, l’état de santé, les attitudes et opinions en alimentation de 4079 individus. Elle contient 4079 observations et 368 variables que l’on peut retrouver en détail sur la Notice utilisation des données INCA 2.

#Table_indiv <- read_delim("~/_M2 Maths Appli et Stat/Semestre 10 - MSS/Projet Données Massives/PBD open data/Table_indiv.csv", ";", escape_double = FALSE, trim_ws = TRUE)
Table_indiv <- read_delim("C:/Users/matic/Desktop/Projet Open Data/Table_indiv.csv", ";", escape_double = FALSE, trim_ws = TRUE)

Table_indiv$nomen <- as.factor(Table_indiv$nomen)

Table_indiv$region <- as.factor(Table_indiv$region)
levels(Table_indiv$region) <- c("Région Parisienne", "Champagne", "Picardie", "Haute-Normandie", "Centre", 
                                "Basse-Normandie", "Bourgogne", "Nord", "Lorraine", "Alsace", "Franche Comté", 
                                "Pays De Loire", "Bretagne", "Poitou Charentes", "Aquitaine", "Midi-Pyrénées", "Limousin",
                                "Rhône-Alpes", "Auvergne", "Languedoc", "Provence Côte D'azur") 
####

Table_indiv_fq <- Table_indiv[, c("nomen", "fqfec", "fqfl", "fqpl", "fqvpo", "region")]

Table_indiv_fq$fqfl <- as.factor(Table_indiv_fq$fqfl)
levels(Table_indiv_fq$fqfl) <- c("jamais", "1-2 fois/sem", "3-4 fois/sem", "5-6 fois/sem", 
                              "1 fois/jour", "2 fois/jour", "3 fois/jour", "4 fois/jour", 
                              "5 fois/jour", "6 fois/jour ou +", "ne sait pas", "[pas de réponse]") 

Table_indiv_fq$fqfec <- as.factor(Table_indiv_fq$fqfec)
levels(Table_indiv_fq$fqfec) <- c("jamais", "1-2 fois/sem", "3-4 fois/sem", "5-6 fois/sem", 
                               "1 fois/jour", "2 fois/jour", "3 fois/jour", "4 fois/jour ou +", 
                               "ne sait pas", "[pas de réponse]") 

Table_indiv_fq$fqpl <- as.factor(Table_indiv_fq$fqpl)
levels(Table_indiv_fq$fqpl) <- c("jamais", "1-2 fois/sem", "3-4 fois/sem", "5-6 fois/sem", 
                              "1 fois/jour", "2 fois/jour", "3 fois/jour", "4 fois/jour ou +", 
                              "ne sait pas", "[pas de réponse]")

Table_indiv_fq$fqvpo <- as.factor(Table_indiv_fq$fqvpo)
levels(Table_indiv_fq$fqvpo) <- c("jamais", "1-2 fois/sem", "3-4 fois/sem", "5-6 fois/sem", 
                               "1 fois/jour", "2 fois/jour", "3 fois/jour", "4 fois/jour ou +", 
                               "ne sait pas", "[pas de réponse]") 

colnames(Table_indiv_fq) <- c("nomen", "Fréquence de consommation de pain, céréales, PDT, lég. secs", 
                           "Fréquence de consommation de fruits, légumes", 
                           "Fréquence de consommation de lait, pdts laitiers", 
                           "Fréquence de consommation de viandes, volailles, poissons, oeufs", "region")

####

Table_indiv_etiq <- Table_indiv[, c("nomen", "etiqclnut", "etiqclsant", "etiqnut", "region")]

Table_indiv_etiq$etiqclnut <- as.factor(Table_indiv_etiq$etiqclnut)
levels(Table_indiv_etiq$etiqclnut) <- c("[plusieurs réponses]", "lit et influence tjrs achat", 
                                   "lit et influence parfois achat", "lit et n'influence jamais achat", 
                                   "ne lit jamais cette partie", "[pas de réponse]")

Table_indiv_etiq$etiqclsant <- as.factor(Table_indiv_etiq$etiqclsant) 
levels(Table_indiv_etiq$etiqclsant) <- c("[plusieurs réponses]", "lit et influence tjrs achat", 
                                   "lit et influence parfois achat", "lit et n'influence jamais achat", 
                                   "ne lit jamais cette partie", "[pas de réponse]")

Table_indiv_etiq$etiqnut <- as.factor(Table_indiv_etiq$etiqnut)
levels(Table_indiv_etiq$etiqnut) <- c("[plusieurs réponses]", "lit et influence tjrs achat", 
                                  "lit et influence parfois achat", "lit et n'influence jamais achat", 
                                  "ne lit jamais cette partie", "[pas de réponse]")

colnames(Table_indiv_etiq) <- c("nomen", "Lit les messages nutritionnels revendiqués", 
                           "Lit les messages décrivants effets sur la santé", 
                           "Lit le contenu nutritionnel", "region") 

Par rapport à notre thématique, nous allons prendre en compte que 9 variables qualitatives que nous avons séparé en deux tables :

  • L’identifiant des 4079 participants à l’étude (nomen) ;
  • Fréquence de consommation de féculants ;
  • Fréquence de consommation de fruits, légumes à noter ;
  • Fréquence de consommation de lait, produits laitiers ;
  • Fréquence de consommation de viandes, volailles, poissons, oeufs ;
  • Région.

et pour savoir s’il est vraiment intérressant de faire un score nutritionnel :

  • L’identifiant des 4079 participants à l’étude (nomen) ;
  • Lit les messages nutritionnels revendiqués ;
  • Lit les messages décrivants effets sur la santé ;
  • Lit le contenu nutritionnel ;
  • Région.
mj_fq <- unlist(lapply(Table_indiv_fq[, -1], function(x){length(levels(x))})) # nombre de modalite de la j-ieme variable 
m_fq <- sum(mj_fq) # total du nombre de modalite 

mj_etiq <- unlist(lapply(Table_indiv_etiq[, -1], function(x){length(levels(x))})) # nombre de modalite de la j-ieme variable 
m_etiq <- sum(mj_etiq) # total du nombre de modalite 

resume_fq <- summary(Table_indiv_fq) 
resume_etiq <- summary(Table_indiv_etiq)

Table_indiv_no_na <- na.omit(Table_indiv_fq)
dimension3 <- dim(Table_indiv_no_na)

Table_indiv_no_na <- na.omit(Table_indiv)
dimension4 <- dim(Table_indiv_no_na) 

Pour notre table contenant la fréquence :

Nous avons les 10 mêmes modalités pour les variables Fréquence de consommation de pain, céréales, PDT, lég. secs, Fréquence de consommation de lait, pdts laitiers et Fréquence de consommation de viandes, volailles, poissons, oeufs : jamais, 1-2 fois/sem, 3-4 fois/sem, 5-6 fois/sem, 1 fois/jour, 2 fois/jour, 3 fois/jour, 4 fois/jour ou +, ne sait pas, [pas de réponse]. Nous avons 2 modalités de plus pour la variable Fréquence de consommation de fruits, légumes : 5 fois/jour, 6 fois/jour ou +.
Nous avons 21 régions françaises dans notre table. On en déduit alors que l’on a en tout 63 modalités.

Sur nos données, sans prendre en considération les valeurs manquantes, on peut dire que la majorité des individus interrogés consomme des féculents 1 fois par jour, des fruits et des légumes 2 fois par jours, des produits laitiers 1 fois par jour et, de la viande, du poissons ou des oeufs 1 fois par jour. Beaucoup d’individus sont originaires de la région Parisienne.

Pour notre table contenant la lecture de l’étiquette :

Nous avons 6 mêmes modalités par variables sauf pour la région qui en à 21 : [plusieurs réponses], lit et influence tjrs achat, lit et influence parfois achat, lit et n’influence jamais achat, ne lit jamais cette partie, [pas de réponse].

Nous pouvons dire que, sans prendre en considération les NA, pour ceux qui lisent les messages nutritionnels revendiqués et font un choix, on en a 1805 sur 2189 individus, soit 82% des volontaires. On a 1917 qui lisent les messages décrivants les effets sur la santé, soit 88% et ils sont 1774 à lire le contenu nutritionnel, soit 81%. On en déduit que les français prennent le temps de lire les étiquettes, surtout les messages en rapport avec la santé. La majorité achètent les produits par rapport aux messages. Entre 10% et 17% ont des réponses multiples. Mais aussi, environ 2% des volontaires, pour chaque question, ne lisent pas les messages. Il n’est pas donc négligeable de faire un score nutritionnelle pour aider les clients à mieux choisir leurs aliments.

On remarque que 585 volontaires n’ont pas répondu à ces 4 questions sur la fréquence de consommation et 1890 n’ont pas répondu à la lecture de l’étiquette. Surement parce qu’ils n’ont pas eu accès à cette partie du questionnaire. Nous avons quand-même essayé de compléter par inputeMCA mais la compilation est trés lente voir infaisable sur nos propres ordinateurs pour déjà juste récupérer l’estimateur du nombre de composantes principales.

6.2 Apport nutritionnel journalier

Nous avons tous besoin d’un apport énergétique suffisant pour être en forme durant la journée. Mais les besoins énergétiques sont différents en fonction des individus. Ils varient principalement par rapport à l’âge, au sexe et au degré d’activité physique.

tranches_age <- c("01-03 ans", "04-06 ans", "07-09 ans", "10-12 ans", "13-19 ans", "20-40 ans", "41-60 ans")
f <- c(4500, 6000, 7500, 8500, 9000, 9000, 9000) * 239 * 10^(-3)
h <- c(4500, 6000, 8000, 9000, 11500, 11500, 11000) * 239 * 10^(-3)
apport <- data.frame(tranches_age, f, h)

p <- plot_ly(apport, x = ~tranches_age, y = ~f, type = 'bar', name = 'femme') %>% 
  add_trace(y = ~h, name = 'homme') %>% 
  layout(yaxis = list(title = "Kcal"), barmode = 'groupe') %>% 
  layout(title = "Apports énergétiques quotidiens conseillés \n(pour un niveau moyen de poids et d'activité physique)")
p 

Figure 5 : manger-bouger.fr (source : AFSSA)

D’après l’AFSSA (Agence française de sécurité sanitaire des aliments), on peut voir durant l’enfance, de 1 ans à 19 ans, que l’apport énergétique doit être croissante et doit se stabiliser à l’âge adulte, femme comme homme. Mais à l’âge de 7 ans, un écart entre les filles et les garçons commencent à se faire, soit à peu près 120 Kcal de différence. A l’âge adulte, entre 20 ans et 60 ans, il est conseillé pour les femmes de rester à environ 2150 Kcal et pour les hommes, à 2750 environ (Attention : ce sont des approximations, nous n’avons pas les données exactes mais juste un graphique).

Ainsi, par rapport à l’analyse que nous avons faite sur la variable énergétique pour chaque catégorie de produits et la fréquence de consommation de ces produits, nous avons voulu savoir si les apports énergétiques journaliers sont respectés, grâce à la table Table_indnut.

#Table_indnut <- read_delim("~/_M2 Maths Appli et Stat/Semestre 10 - MSS/Projet Données Massives/PBD open data/Table_indnut.csv", ";", escape_double = FALSE, trim_ws = TRUE) 
Table_indnut <- read_delim("C:/Users/matic/Desktop/Projet Open Data/Table_indnut.csv", ";", escape_double = FALSE, trim_ws = TRUE)


Table_indnut$nomen <- as.factor(Table_indnut$nomen)

Table_indnut$sexe_ps <- as.factor(Table_indnut$sexe_ps)
levels(Table_indnut$sexe_ps) <- c("homme", "femme") 

Table_indnut$ech <- as.factor(Table_indnut$ech)
levels(Table_indnut$ech) <- c("adultes", "enfants")

colnames(Table_indnut) <- c("nomen", "sexe_ps", "ech", "v2_age", 
                            "Energie calorique totale table table CIQUAL avec polyols et acides orga. (kcal/j)", 
                                    "Protéines (en g/j)", "Glucides disponibles, y compris polyols (en g/j)", 
                                    "Lipides (en g/j) ", "Alcool (en g/j)", "Acides gras mono-insaturés (en g/j)", 
                                    "Acides gras polyinsaturés (en g/j)", "Acides gras saturés (en g/j)", "Amidon (en g/j)",  
                                    "Glucides simples (en g/j)", "Fibres (en g/j)", "Cholestérol (en mg/j)", "Eau (en g/j)", 
                                    "Calcium (en mg/j)", "Fer (en mg/j)", "Sodium (en mg/j)", "Magnésium (en mg/j)", 
                                    "Manganèse (en mg/j)", "Phosphore (en mg/j)", "Potassium (en mg/j)", "Cuivre (en mg/j)", 
                                    "Zinc (en mg/j)", "Sélénium (en µg/j)", "Iode (en µg/j)", "Rétinol (en µg/j)", 
                                    "Béta-carotène (en µg/j)", "Vitamine C (en mg/j)", "Vitamine D (en µg/j)", 
                                    "Vitamine E (en mg/j)", "Vitamine B1 ou thiamine (en mg/j)", 
                                    "Vitamine B2 ou riboflavine (en mg/j)", "Vitamine B3 ou niacine (en mg/j)", 
                                    "Vitamine B5 ou acide pantothénique (en mg/j)", "Vitamine B6 ou pyridoxine (en mg/j)", 
                                    "Vitamine B9 ou folates (en µg/j)", "Vitamine B12 ou colobamine (en µg/j)" )

#paged_table(Table_indnut) 

dimension5 <- dim(Table_indnut) 

On a du dans un premier temps déterminer la moyenne énergétique pour chaque sexe par Région. On a fait la même chose pour enfants/aldultes par région.

indnut <- merge(Table_indnut, Table_indiv[, c("nomen", "region")], by = "nomen") 
moy_genre <- matrix(NA, 42, 38)

indnut$region <- as.factor(indnut$region)
levels(indnut$region) <- 1:21
indnut$region <- as.numeric(indnut$region)
nom_reg <- c("Île-de-France", "Champagne-Ardenne", "Picardie", "Haute-Normandie", "Centre", "Basse-Normandie", 
  "Bourgogne", "NordPas-de-Calais", "Lorraine", "Alsace", "Franche-Comté", "Pays De la Loire", "Bretagne", 
  "Poitou-Charentes", "Aquitaine", "Midi-Pyrénées", "Limousin", "Rhône-Alpes", "Auvergne", 
  "Languedoc-Roussillon", "Provence-Alpes-Côte d'Azur")
for(j in 0:20){
    moy_genre[j*2+1, 3:38] = round(apply(indnut[which(indnut$region == j+1 & indnut$sexe_ps == "femme"), 5:40], 2, function(x){mean(x, na.rm = TRUE)} ), 2)
    moy_genre[j*2+1, 1] = nom_reg[j+1]
    moy_genre[j*2+1, 2] = "femme"
    moy_genre[j*2+2, 3:38] = round(apply(indnut[which(indnut$region == j+1 & indnut$sexe_ps == "homme"), 5:40], 2, function(x){mean(x, na.rm = TRUE)} ), 2) 
    moy_genre[j*2+2, 1] = nom_reg[j+1]
    moy_genre[j*2+2, 2] = "homme"
}
colnames(moy_genre) <- c("Region", "sexe_ps", 
                         "Energie calorique totale table table CIQUAL avec polyols et acides orga. (kcal/j)", 
                         "Protéines (en g/j)", "Glucides disponibles, y compris polyols (en g/j)", 
                         "Lipides (en g/j) ", "Alcool (en g/j)", "Acides gras mono-insaturés (en g/j)", 
                         "Acides gras polyinsaturés (en g/j)", "Acides gras saturés (en g/j)", "Amidon (en g/j)",  
                         "Glucides simples (en g/j)", "Fibres (en g/j)", "Cholestérol (en mg/j)", "Eau (en g/j)", 
                         "Calcium (en mg/j)", "Fer (en mg/j)", "Sodium (en mg/j)", "Magnésium (en mg/j)", 
                         "Manganèse (en mg/j)", "Phosphore (en mg/j)", "Potassium (en mg/j)", "Cuivre (en mg/j)", 
                         "Zinc (en mg/j)", "Sélénium (en µg/j)", "Iode (en µg/j)", "Rétinol (en µg/j)", 
                         "Béta-carotène (en µg/j)", "Vitamine C (en mg/j)", "Vitamine D (en µg/j)", 
                         "Vitamine E (en mg/j)", "Vitamine B1 ou thiamine (en mg/j)", 
                         "Vitamine B2 ou riboflavine (en mg/j)", "Vitamine B3 ou niacine (en mg/j)", 
                         "Vitamine B5 ou acide pantothénique (en mg/j)", "Vitamine B6 ou pyridoxine (en mg/j)", 
                         "Vitamine B9 ou folates (en µg/j)", "Vitamine B12 ou colobamine (en µg/j)")

moy_reg_genre <- as.data.frame(moy_genre)

#### 

moy_ages <- matrix(NA, 42, 38)

nom_reg <- c("Île-de-France", "Champagne-Ardenne", "Picardie", "Haute-Normandie", "Centre", "Basse-Normandie", 
  "Bourgogne", "NordPas-de-Calais", "Lorraine", "Alsace", "Franche-Comté", "Pays De la Loire", "Bretagne", 
  "Poitou-Charentes", "Aquitaine", "Midi-Pyrénées", "Limousin", "Rhône-Alpes", "Auvergne", 
  "Languedoc-Roussillon", "Provence-Alpes-Côte d'Azur")
for(j in 0:20){
    moy_ages[j*2+1, 3:38] = round(apply(indnut[which(indnut$region == j+1 & indnut$ech == "enfants"), 5:40], 2,  function(x){mean(x, na.rm = TRUE)} ), 2)
    moy_ages[j*2+1, 1] = nom_reg[j+1]
    moy_ages[j*2+1, 2] = "enfants"
    moy_ages[j*2+2, 3:38] = round(apply(indnut[which(indnut$region == j+1 & indnut$ech == "adultes"), 5:40], 2, function(x){mean(x, na.rm = TRUE)} ), 2)
    moy_ages[j*2+2, 1] = nom_reg[j+1]
    moy_ages[j*2+2, 2] = "adultes"
}
colnames(moy_ages) <- c("Region", "ech", 
                         "Energie calorique totale table table CIQUAL avec polyols et acides orga. (kcal/j)", 
                         "Protéines (en g/j)", "Glucides disponibles, y compris polyols (en g/j)", 
                         "Lipides (en g/j) ", "Alcool (en g/j)", "Acides gras mono-insaturés (en g/j)", 
                         "Acides gras polyinsaturés (en g/j)", "Acides gras saturés (en g/j)", "Amidon (en g/j)",  
                         "Glucides simples (en g/j)", "Fibres (en g/j)", "Cholestérol (en mg/j)", "Eau (en g/j)", 
                         "Calcium (en mg/j)", "Fer (en mg/j)", "Sodium (en mg/j)", "Magnésium (en mg/j)", 
                         "Manganèse (en mg/j)", "Phosphore (en mg/j)", "Potassium (en mg/j)", "Cuivre (en mg/j)", 
                         "Zinc (en mg/j)", "Sélénium (en µg/j)", "Iode (en µg/j)", "Rétinol (en µg/j)", 
                         "Béta-carotène (en µg/j)", "Vitamine C (en mg/j)", "Vitamine D (en µg/j)", 
                         "Vitamine E (en mg/j)", "Vitamine B1 ou thiamine (en mg/j)", 
                         "Vitamine B2 ou riboflavine (en mg/j)", "Vitamine B3 ou niacine (en mg/j)", 
                         "Vitamine B5 ou acide pantothénique (en mg/j)", "Vitamine B6 ou pyridoxine (en mg/j)", 
                         "Vitamine B9 ou folates (en µg/j)", "Vitamine B12 ou colobamine (en µg/j)")
moy_reg_ages <- as.data.frame(moy_ages)

Pour mieux visualiser les données, nous avons utilisé une cartographie intéractive pour ces 4 modalités :

#data_REGION <- readOGR(dsn = "~/_M2 Maths Appli et Stat/Semestre 10 - MSS/Projet Données Massives/PBD open data/regions-20140306-50m-shp", layer = "regions-20140306-50m", verbose = FALSE)
data_REGION <- readOGR(dsn ="C:/Users/matic/Desktop/Projet Open Data/regions-20140306-50m-shp", layer = "regions-20140306-50m", verbose = FALSE)


proj_REGION <- sp::CRS('+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0')
contourRegion <- sp::spTransform(data_REGION, proj_REGION)
rm(list = c("data_REGION", "proj_REGION"))
#save(contourRegion, file = "~/_M2 Maths Appli et Stat/Semestre 10 - MSS/Projet Données Massives/PBD open data/contourRegion.Rdata")
save(contourRegion, file = "C:/Users/matic/Desktop/Projet Open Data/contourRegion.Rdata")

contourRegion <- contourRegion[which(contourRegion@data$nom != "Corse"
                                    & contourRegion@data$nom != "Guadeloupe"
                                    & contourRegion@data$nom != "Guyane"
                                    & contourRegion@data$nom != "La Réunion"
                                    & contourRegion@data$nom != "Martinique"
                                    & contourRegion@data$nom != "Mayotte"),]
moy_reg_fem <- moy_reg_genre[which(moy_reg_genre$sexe_ps == "femme"), ]

contourRegion@data$femme <- moy_reg_fem$sexe_ps[match(contourRegion@data$nom , moy_reg_fem$Region)]

contourRegion@data$valeur_energetique_femme <- moy_reg_fem$`Energie calorique totale table table CIQUAL avec polyols et acides orga. (kcal/j)`[match(contourRegion@data$nom , moy_reg_fem$Region)]

contourRegion$valeur_energetique_femme <- as.numeric(as.vector(contourRegion$valeur_energetique_femme))

bins <- c(0, seq(1500, 1900, 50), Inf)
pal <- colorBin("Oranges", domain = contourRegion$nom, bins = bins)

label <- sprintf("%s, %g kcal/j", contourRegion$nom, contourRegion@data$valeur_energetique_femme) %>%
  lapply(htmltools::HTML)

f <- leaflet(contourRegion) %>%
  addTiles() %>%
  addPolygons(weight = 2,
              opacity = 1,
              color = "white",
              fillColor = ~pal(valeur_energetique_femme),
              fillOpacity = 0.8,
              highlight = highlightOptions(weight = 5,
                                           color = "#666",
                                           fillOpacity = 0.8,
                                           bringToFront = TRUE),
              label = label
              ) %>%
  addLegend("bottomleft", pal = pal, values = ~valeur_energetique_femme,
            title = "Moy. d'apport Energé. fem.", opacity = 0.8)
f

Cas des femmes :

On observe que la région Limousine a le plus grand apport énergétique avec 1878.54 Kcal/j pour les femmes. La Bourgonne est la plus basse avec 1503.71 Kcal/j. Si nous calculons la moyenne d’apport énergétique des femmes conseillée, alors nous obtenons 1952 Kcal/j. Les régions sont sous la moyenne conseillée. Il faudrait analyser plus en détail pour voir la tranche d’âge par exemple ou d’autres critères qui font qu’ils sont au dessous de la moyenne conseillée.

On remarque que les régions Pay de la Loire et le Nord-Pas-de-Calais n’ont aucune donnée, quelque soit la modalité. On ne pourra donc pas les traiter.

Passons aux hommes.

moy_reg_hom <- moy_reg_genre[which(moy_reg_genre$sexe_ps == "homme"), ]

contourRegion@data$homme <- moy_reg_hom$sexe_ps[match(contourRegion@data$nom , moy_reg_hom$Region)]

contourRegion@data$valeur_energetique_homme <- moy_reg_hom$`Energie calorique totale table table CIQUAL avec polyols et acides orga. (kcal/j)`[match(contourRegion@data$nom , moy_reg_hom$Region)]

contourRegion$valeur_energetique_homme <- as.numeric(as.vector(contourRegion$valeur_energetique_homme))

bins <- c(0, seq(2080, 2380, 50), Inf)
pal <- colorBin("Greens", domain = contourRegion$nom, bins = bins)

label <- sprintf("%s, %g kcal/j", contourRegion$nom, contourRegion@data$valeur_energetique_homme) %>%
  lapply(htmltools::HTML)

h <- leaflet(contourRegion) %>%
  addTiles() %>%
  addPolygons(weight = 2,
              opacity = 1,
              color = "white",
              fillColor = ~pal(valeur_energetique_homme),
              fillOpacity = 0.8,
              highlight = highlightOptions(weight = 5,
                                           color = "#666",
                                           fillOpacity = 0.8,
                                           bringToFront = TRUE),
              label = label) %>%
  addLegend("bottomleft", pal = pal, values = ~valeur_energetique_homme,
            title = "Moy. d'apport Energé. hom.", opacity = 0.8)
h

** Cas des hommes : **

On constate, homme comme femme, que le Limousin a toujours le plus grand apport énergétique avec 2338.72 Kcal/j. Le plus bas, cette fois ci, est l’Auvergne avec 2087.17 Kcal/j. Si nous comparons à la moyenne conseillée pour les hommes, soit 2270 Kcal/j alors l’apport énergétique de la basse-Normandie, l’Alsace et Midi-Pyrénées sont satisfaisantes. On remarque qu’elles sont géographiquement séparées donc il n’y a pas de lien géographique entre ces 3 régions. Les autres apports énergétiques des régions sont en dessous de 2230 Kcal/j ou en dessus de 2280 Kcal/j de cette moyenne.

Nous allons passer aux tranches d’âge : enfants ou adultes. Commençons par les enfants :

moy_reg_enf <- moy_reg_ages[which(moy_reg_ages$ech == "enfants"), ]

contourRegion@data$enfants <- moy_reg_ages$ech[match(contourRegion@data$nom , moy_reg_enf$Region)]

contourRegion@data$valeur_energetique_enfants <- moy_reg_enf$`Energie calorique totale table table CIQUAL avec polyols et acides orga. (kcal/j)`[match(contourRegion@data$nom , moy_reg_enf$Region)]

contourRegion$valeur_energetique_enfants <- as.numeric(as.vector(contourRegion$valeur_energetique_enfants))

bins <- c(0, seq(1680, 2030, 50), Inf)
pal <- colorBin("Blues", domain = contourRegion$nom, bins = bins)

label <- sprintf("%s, %g kcal/j", contourRegion$nom, contourRegion@data$valeur_energetique_enfants) %>%
  lapply(htmltools::HTML)

e <- leaflet(contourRegion) %>%
  addTiles() %>%
  addPolygons(weight = 2,
              opacity = 1,
              color = "white",
              fillColor = ~pal(valeur_energetique_enfants),
              fillOpacity = 0.8,
              highlight = highlightOptions(weight = 5,
                                           color = "#666",
                                           fillOpacity = 0.8,
                                           bringToFront = TRUE),
              label = label
              ) %>%
  addLegend("bottomleft", pal = pal, values = ~valeur_energetique_enfants,
            title = "Moy. d'apport Energé. enf.", opacity = 0.8)
e

** Cas des enfants : **

On remarque que le Limousin a le plus grand apport énergétique avec 2017.27 Kcal/j pour les enfants. L’apport énergétique le plus bas est la région Haut-Normandie avec 1680.53 Kcal/j. Lorsque l’on compare à la moyenne conseillée pour les enfants, soit 1957 Kcal/j, nous avons seulement Midi-Pyrénées proche de la moyenne conseillée. Il faudrait analyser par tranches d’âge pour avoir plus de précision.

Pour finir, nous allons analyser l’apport énergétiques des adultes.

moy_reg_adu <- moy_reg_ages[which(moy_reg_ages$ech == "adultes"), ]

contourRegion@data$adultes <- moy_reg_ages$ech[match(contourRegion@data$nom , moy_reg_adu$Region)]

contourRegion@data$valeur_energetique_adultes <- moy_reg_adu$`Energie calorique totale table table CIQUAL avec polyols et acides orga. (kcal/j)`[match(contourRegion@data$nom , moy_reg_adu$Region)]

contourRegion$valeur_energetique_adultes <- as.numeric(as.vector(contourRegion$valeur_energetique_adultes))

bins <- c(0, seq(1790, 2090, 50), Inf)
pal <- colorBin("Reds", domain = contourRegion$nom, bins = bins)

label <- sprintf("%s, %g kcal/j", contourRegion$nom, contourRegion@data$valeur_energetique_adultes) %>%
  lapply(htmltools::HTML)

a <- leaflet(contourRegion) %>%
  addTiles() %>%
  addPolygons(weight = 2,
              opacity = 1,
              color = "white",
              fillColor = ~pal(valeur_energetique_adultes),
              fillOpacity = 0.8,
              highlight = highlightOptions(weight = 5,
                                           color = "#666",
                                           fillOpacity = 0.8,
                                           bringToFront = TRUE),
              label = label
              ) %>%
  addLegend("bottomleft", pal = pal, values = ~valeur_energetique_adultes,
            title = "Moy. d'apport Energé. adu.", opacity = 0.8)
a

** Cas des adultes : **

Ainsi, on peut voir que le Limousin reste encore une fois premier avec 2075.95 Kcal/j d’apport énergétique pour les adultes. L’Auvergne est, pour la deuxième fois, dernier avec 1792.74 Kcal/j. Si nous comparons avec la moyenne conseillée 2420 Kcal/j, nous avons toutes les régions au-dessous de la moyenne.

7 Conclusion générale

Durant la réalisation de ce rapport nous avons rencontré différents problèmes, en effet certaines tables étaient très mal remplies (comme la table de Open Fast Food que nous n’avons pas étudié finalement). Nous avons dû régler le problème des valeurs manquantes sinon nous pouvions pas réaliser certains objectifs comme le score nutritionnel. Notre second objectif de retrouver les tables crées par l’Anses n’a pas été atteint, les méthodes d’apprentissage non supervisé sont inefficaces sur nos données. En revanche, nous avons une bonne prédiction de nouveaux aliments grâce à notre méthode d’apprentissage supervisé. Pour l’analyse des nutriments, on a pu confirmer que certains nutriments de la même famille sont corrélés.

En ce qui concerne l ’étude INCA2, les tables étaient bien remplies avec des informations supplémentaires très utiles qui nous ont permises une bonne interprétation du jeu de données.

Cependant, il serait possible de pousser davantage l’analyse du comportement alimentaire des individus et ainsi essayer de leur conseiller des produits. Peut-être serait-il intéressant, si l’Anses réalise une nouvelle étude sur la même thématique de comparer l’évolution du comportement des individus ou même les comportements dans différents Pays. Nous avons eu comme idées de questions : est-ce-que les individus ont participé au mois sans alcools ? Est-ce-que il y a eu une diminution de la consommation de viande ou de poisson suite au conseil de l’Etat ou après les affaires sanitaire ? Ou encore, en consomment-t-ils toujours, font-ils attention à l’origine de la viande ? Etc.

Il serait également intéressant de comparer nos produits avec des produits bio pour voir si ils sont vraiment meilleurs pour la santé. On pourrait s’aider de notre score nutritionnel. Par ailleurs, il serait possible d’améliorer notre score en prennant en compte les pesticides, les colorants ou même les conservateurs contenus dans certains produits et perfectionner également notre application shiny en calculant le score d’un produit que l’on saisit sois-même, qui n’est pas présent dans notre jeu de données.

8 Références