Introduction à l’analyse de données

Economie Ecologique

Simon Jean

AgroParisTech - CIRED - PSAE

Introduction

  • Le but de cette séance est de vous familiariser avec quelques outils modernes que les économistes doivent maîtriser : pour celles et ceux qui auraient besoin d’un rafraichissement sur les structures de données de base (vecteurs, matrices, dataframes) voir le cours de Laurent Rouvière
  • On utilisera le logiciel R, et on va :
    • Apprendre un workflow utile pour tous vos projets
    • Importer et nettoyer des données
    • Faire des statistiques descriptives (plus ou moins compliquées)
    • Un peu de cartographie
  • De façon générale, l’intelligence artificielle (ChatGPT, Claude etc) sont hyper performants : n’hésitez pas vous en aider, à apprendre avec

R

  • Langage de programmation libre et open source interprété, datant des années 1990, développé par R. Ihaka et R. Gentleman
  • Destiné à l’analyse statistique et à la visualisation de données
  • Langage populaire parmi les statisticiens, chercheurs et data-scientists
  • Utilise des packages c’est à dire des collections de fonctions qui permettent de faire une grande variété de choses
  • Pour s’en servir: on installe R, puis un Integrated Development Environment (IDE) comme RStudio par exemple

Projets sur R

  • Premièrement, on crée un projet. Ils permettent de :
    • Regrouper tout le code au sein d’une architecture reproductible
    • Créer des environnements afin d’avoir un code reproductible, gérant les dépendances entre projets et dans le temps
    • Partager rapidement et facilement le code entre différents utilisateurs
    • Suivre dans le temps via le contrôle de version, notamment en utilisant Git (ce sera pour une autre fois)
  • Il faut donc créer un nouveau projet:
    • Créez un dossier facilement identifiable sur votre bureau, nommé Projets
    • Ouvrez R Studio, et cliquez sur File > New Project
    • Puis New Directory > New Project puis nommez le analyse_economie_ecologique ou ce que vous préférez comme titre de projet
    • Vous avez créé un projet!

Reproducibilité

  • On parle de reproducibilité à plusieurs niveaux dans l’analyse scientifique :
    • Reproducibilité computationelle : pouvoir répliquer exactement les résultats
    • Reproducibilité empirique : refaire les tests d’une autre manière
    • Reproducibilité conceptuelle : retrouver les mêmes résultats avec de nouvelles données
  • Celle ci est en crise : beaucoup de résultats en science ne sont pas réplicables
  • Pas simplement la recherche académique, toute production visant à être étayée et partagée doit viser la reproducibilité

Comment rendre la recherche reproductible?

  1. Une architecture claire et organisée par projets:
    • Des fichiers séparés pour les données brutes, traitées et les scripts
  2. Contrôle de version : on ne verra pas ça ensemble
  3. Management des dépendances :
    • Créer des environnements pour vérouiller les bibliothèques
    • Documenter toutes les dépendances
    • Partager les fichiers .lock avec les collaborateurs
  4. Pipeline de données :
    • Ne jamais modifier les fichiers de données brutes
    • Créer des scripts séparés pour le nettoyage et l’analyse
    • Construire des fonctions modulaires et réutilisables - c’est un art
    • Documenter la transformation des données
  1. Documentation :
    • Maintenez des README clairs et exhaustifs
    • Commentez le code
    • Générez des rapports automatisés avec les fichiers markdown
  2. Automatisation :
    • Utilisez des chemins relatifs : le package here()
    • Créez des rapports reproductibles

Architecture

Le but est d’avoir une architecture claire, pour que vos résultats puissent être reproduits, avec :

  • Les scripts : c’est le texte, le code qui sera exécuté
  • Les données : brutes ou nettoyées, ou additionelles de sources extérieures
  • Les résultats: figures, tableaux etc
  • La documentation : les rapports, ou encore un fichier qui s’appelle le Readme.md, qui détaille le projet, les instructions d’utilisation etc.
  • Pour commencer, vous allez aller sur la page Github du cours, pour télécharger les scripts qui vont nous permettre de créer l’architecture, et mener les analyses.
    • Prenez simplement les fichiers en .md et enregistrez les dans scripts/
    • Téléchargez également le fichier baie_somme_2025.xlsx

  • Dans la console de R, vous allez d’abord créer le dossier scripts, afin d’y enregistrer ces scripts que vous aurez téléchargé :
  • On va utiliser le package here qui facilite le traitement des adresses des fichiers au sein de votre ordinateur
  • Puis on va créer les dossiers
if(!("here" %in% installed.packages())){ # Vérifie si le package est déja installé
  install.packages("here") # L'installe si besoin
}

library(here) # Charge la bibliothèque

if(!dir.exists(here("scripts"))){ # Vérifie si le chemin existe déja
  dir.create(here("scripts"))
}

print(paste(here("scripts"), " est créé"))
[1] "C:/Users/jean/OneDrive/Desktop/Teaching/AgroParisTech/Master EEET/M1/Economie écologique/intro_data_analysis/scripts  est créé"
  • Utilisez le script 01_Setup.md
    • On utilise un format spécifique, le markdown, qui mélange le texte, le code Latex si besoin, ainsi que l’analyse de données dans des blocs de code
    • Vous pouvez l’exporter en cliquant sur Render et avoir un document sous format hmtl, ou pdf que vous pouvez partager
  • On veut créer l’architecture suivante

Projet/
├── R/                   # Contient tous les scripts R du projet
│   ├── 01_utilities.rmd # Script d'importation des données
│   ├── 02_cleaning.rmd    # Script de nettoyage des données
│   ├── 03_analysis.rmd    # Script d'analyse des données
│   └── 04_visualisation.rmd # Script pour les graphiques et visualisations
├── data/                # Contient les fichiers de données brutes et traitées
│   ├── raw/             # Données brutes
│   │   └── data1.csv    # Exemple de fichier de données brutes
│   │   └── README.txt   # Texte explicatif des données
│   ├── processed/       # Données nettoyées et traitées
│   │   └── data_clean.csv
│   └── external/        # Données externes ou partagées (ex. bases de données publiques)
│       └── source.csv
├── results/             # Résultats des analyses (sorties, graphiques, tableaux)
│   ├── figures/         # Graphiques générés (png, pdf, etc.)
│   │   └── plot1.png
│   └── tables/          # Tables générées (ex. CSV ou LaTeX)
│       └── table1.csv
├── docs/                 # Documentation du projet (rapports, instructions, etc.)
│   ├── report.pdf       # Rapport principal du projet
│   └── README.md        # Explication du projet, instructions d'utilisation
├── .gitignore           # Fichier pour ignorer certains fichiers dans un dépôt Git (si applicable)
├── renv.lock            # Fichier verrouillant les versions des packages (géré par renv)
├── renv/                # Dossier contenant l'environnement isolé géré par renv
└── README.md            # Fichier d'accueil du projet pour décrire brièvement de quoi il s'agit

On utilise le code suivant :

if(!dir.exists(here("data"))){ # Vérifie si le chemin existe déja
  dir.create(here("data"))
  dir.create(here("data", "raw")) # Crée des dossiers à l'intérieur de \data
  dir.create(here("data", "processed"))
  dir.create(here("data", "external"))
  
  dir.create(here("results"))
  dir.create(here("results", "figure"))
  dir.create(here("results", "tables"))

  dir.create(here("docs"))
}

print("Architecture à jour")
[1] "Architecture à jour"

Packages et environnements

Ensuite, on va télécharger et installer des packages utiles :

  • Le package rmarkdown et knitr pour effectivement avoir des documents en markdown qui soient pris en compte par R
  • La collection de package tidyverse qui contient entre autres :
    • tidyr et dplyr : sont essentiels pour la manipulation de données de base ainsi que le recodage, la création de nouvelles variables, le filtrage etc
    • gpplot2 et ggsci: le package de référence pour la data visualisation et des themes() (styles) de référence dans la littérature scientifique
  • readxl : pour lire des données en format .xlsx
  • magrittr: un package qui permet d’utiliser l’opérateur pipe %>%, qui permet de connecter les opérations entre elles, simplifiant le processus de \(g(h(x))\) à x %>% h() %>% g()
  • stringr et stringdist: pour traiter les données textuelles
  • renv : on va créer un environnement, permettant de travailler avec les bons outils
  • Des packages dont je vous expliquerai ensuite la nécessité pour les analyses statistiques et cartographiques que l’on va mener comme factoextra, factoMineR, sf et sp

Environnement

  • Clé pour rendre le travail reproductible :
    • Les versions des packages sont stockées
    • Quand l’environnement est partagé, tout le monde travail avec les mêmes packages, permettant la reproducibilité entre personnes et dans le temps
  • Chaque projet peut avoir des versions de package différentes sans encombre
# I. Installer le package renv s'il ne l'est pas déja dans l'environnement global
if(!("renv" %in% installed.packages())){
  install.packages("renv")
}

library(renv)

# II. Activation ou création de l'environnement

if(!dir.exists(here("renv"))){ # si l'environnement n'existe pas  déja (ce qui est mon cas)
  renv::init()               # sinon, on l'initialise
}else{
  #renv::activate()
}
NULL
# III. Installer chaque package


packages_to_install = c("rmarkdown", "knitr", "tidyverse", "readxl", "openxlsx",
                        "magrittr", "factoextra", "FactoMineR", "sf", "sp", "here", "ggsci")

install_and_load <- function(packages){
  for (pkg in packages){    
    if(!(pkg %in% installed.packages())){# Pour chaque package de la liste
    renv::install(pkg)       # installe le cas échéant     
    }
    library(pkg, character.only = TRUE)           # Puis le charge
  }
}

install_and_load(packages_to_install) 

renv::snapshot()
- The lockfile is already up to date.

Data cleaning

Chargement des données

  • On va maintenant pouvoir charger les données
    • Enregistrez le fichier baie_somme_2025.xlsx dans data\raw
    • Ouvrez scripts/02_cleaning.Rmd
data_ = readxl::read_xlsx(here("data", "raw", "baie_somme_2025.xlsx"))

head(data_)
# A tibble: 6 × 16
  Horodateur          `Lieu de l'entretien` `Jour de l'entretien`
  <dttm>              <chr>                 <dttm>               
1 2025-01-13 14:38:18 Hourdel               2025-01-13 00:00:00  
2 2025-01-13 14:48:03 Hourdel               2025-01-13 00:00:00  
3 2025-01-13 15:06:42 <NA>                  NA                   
4 2025-01-13 15:13:54 <NA>                  NA                   
5 2025-01-13 15:26:11 <NA>                  NA                   
6 2025-01-13 15:34:16 Pointe                2025-01-13 00:00:00  
# ℹ 13 more variables: `Heure de l'entretien` <dttm>,
#   `Caractéristique de la personne interviewée` <chr>,
#   `Age approximatif` <chr>,
#   `Lieu de résidence de la personne interviewée` <chr>,
#   `Activité de loisir de la personne interrogée :` <chr>,
#   `Activité professionnelle de la personne interrogée :` <chr>,
#   `Q1 : Connaissez-vous le nom des espèces de phoques qui sont présentes dans la baie de Somme ? (Si oui préciser les espèces, Si ne sait pas, donner les deux noms des espèces (phoque gris et phoque veau-marin))` <chr>, …

Diagnostic

  • On va regarder déja la structure des données et le taux de complétude des données
  • On va ensuite regarder comment standardiser les noms de variables, les réponses aux questions (souvent imparfaites), assigner le bon format etc.

Exploration des variables

 POSIXct[1:218], format: "2025-01-13 14:38:18" "2025-01-13 14:48:03" "2025-01-13 15:06:42" ...
 chr [1:218] "Employé" "Employé" "Retraite" "Retraité" "Retraite" ...
 chr [1:218] "Il faut laisser la nature évoluer librement, La surfréquentation peut générer des dérangements pour la nature" ...
 [1] "Horodateur"                                                                                                                                                                                                      
 [2] "Lieu de l'entretien"                                                                                                                                                                                             
 [3] "Jour de l'entretien"                                                                                                                                                                                             
 [4] "Heure de l'entretien"                                                                                                                                                                                            
 [5] "Caractéristique de la personne interviewée"                                                                                                                                                                      
 [6] "Age approximatif"                                                                                                                                                                                                
 [7] "Lieu de résidence de la personne interviewée"                                                                                                                                                                    
 [8] "Activité de loisir de la personne interrogée :"                                                                                                                                                                  
 [9] "Activité professionnelle de la personne interrogée :"                                                                                                                                                            
[10] "Q1 : Connaissez-vous le nom des espèces de phoques qui sont présentes dans la baie de Somme ? (Si oui préciser les espèces, Si ne sait pas, donner les deux noms des espèces (phoque gris et phoque veau-marin))"
[11] "Q2 : Savez-vous combien il y a de phoque gris et de phoque veau-marin dans la baie de Somme ?"                                                                                                                   
[12] "Q3 : Avez-vous déjà vu des phoques dans la baie"                                                                                                                                                                 
[13] "Q4 : Est-ce qu’ils vous dérangent dans vos activités de loisir ou professionnelle ? Pourquoi ?"                                                                                                                  
[14] "Q5 : A quoi assimilez vous le phoque (2 choix max) ?"                                                                                                                                                            
[15] "Q6 :  Pouvez-vous me dire si vous êtes d’accord avec les affirmations suivantes (cocher lorsque accord) ?"                                                                                                       
[16] "Avez-vous des suggestions concernant cette question des phoques dans la Baie de Somme ?"                                                                                                                         
  • On a plusieurs informations sur les formats :
    • POSIT: données temporelles formatées en date
    • chr : données “character” i.e. données textuelles, sans notion d’ordre etc
  • Pour certaines variables on va vouloir passer de données textuelles à des données catégorielles, c’est à dire des données textuelles avec différents niveaux
  • Pour certaines questions à choix multiple, les réponses sont collées entre elles :
    • Il va falloir réfléchir à comment les extraire
    • Et à comment les exploiter
  • Les noms de colonnes ne sont pas pratiques à utiliser : on va les renommer
colnames(data_) = c("date", "lieu", "jour", "heure", "genre",
                    "classe_age", "residence", "loisir", "profession", 
                    "q1_nom_phoques", "q2_pop_phoques", "q3_vue_phoques", 
                    "q4_dérangement", "q5_classe_phoque", "q6_avis", "q7_divers")

Nettoyer les lieux

unique(data_$residence)
 [1] "Hauts de Seine(92)"                   
 [2] "Abbeville"                            
 [3] NA                                     
 [4] "Nancy"                                
 [5] "Beauvais"                             
 [6] "Essonne"                              
 [7] "Drancourt"                            
 [8] "Pas d'ici"                            
 [9] "Crotoy"                               
[10] "Saint Valery sur Somme"               
[11] "Dunkersue"                            
[12] "Franciere (somme)"                    
[13] "Saint Valery"                         
[14] "Vers saint valery"                    
[15] "Le crotoy"                            
[16] "Saint-Valerie-sur-somme"              
[17] "Saigneville"                          
[18] "Arrest"                               
[19] "Quesnoy le montant"                   
[20] "Le Treport"                           
[21] "Treport"                              
[22] "Treport depuis 2 ans"                 
[23] "Le treport"                           
[24] "Abeville"                             
[25] "Feuquieres - en - vimeu"              
[26] "Feuquieres en vimeu"                  
[27] "Belgique"                             
[28] "Amiens"                               
[29] "Le Crotoy"                            
[30] "Cayeux"                               
[31] "Cayeux-sur-mer"                       
[32] "Saint-Riquier"                        
[33] "Saint-riquier"                        
[34] "Eu"                                   
[35] "Pau"                                  
[36] "Miramont-sensacq"                     
[37] "Saint-Valery-sur-Somme"               
[38] "Criel-sur-mer"                        
[39] "Boulogne-sur-mer"                     
[40] "Outreau"                              
[41] "Dieppe"                               
[42] "Saint valery sur somme"               
[43] "Arras"                                
[44] "Île de France"                        
[45] "Lancher"                              
[46] "Hourdel"                              
[47] "Cayeux sur mer"                       
[48] "Arlay a 5km du crotoy"                
[49] "Saint Valéry"                         
[50] "Noyelles sur mer"                     
[51] "Lanchere"                             
[52] "Aisne"                                
[53] "Pende"                                
[54] "Noyelle sur mer"                      
[55] "Quesnois"                             
[56] "Pendé"                                
[57] "Boismont"                             
[58] "Saint Raphaël"                        
[59] "St VALERY"                            
[60] "Routhiauville"                        
[61] "Abbevilel"                            
[62] "La Molière"                           
[63] "Lille"                                
[64] "Saimt valery"                         
[65] "Mons-Boubert"                         
[66] "Lancheres"                            
[67] "Fressennville"                        
[68] "St Valery"                            
[69] "Braine-l’Alleup"                      
[70] "VILLERS COTTERETS (02600)"            
[71] "Abbville"                             
[72] "Quend"                                
[73] "Amiens / Berck"                       
[74] "Berck"                                
[75] "Grofflier"                            
[76] "Conchil-le-temple"                    
[77] "Bouches du rhone"                     
[78] "Fort Mahon"                           
[79] "Fauquembergues (résidence secondaire)"
[80] "Merlimont"                            
[81] "ARRAS"                                
[82] "Saint Étienne au- Mont"               
[83] "Berck-sur-mer"                        
[84] "Roye"                                 
[85] "Maubeuge"                             
[86] "Rang du fliers"                       
[87] "Stella"                               
[88] "Estreboeuf"                           
[89] "Friville"                             
unique(data_$lieu)
 [1] "Hourdel"                NA                       "Pointe"                
 [4] "Saint Valéry sur Somme" "Le Crotoy"              "Amiens"                
 [7] "Le Tréport"             "Trépot"                 "Le Treport"            
[10] "Tréport"                "Treport"                "Le treport"            
[13] "Le tréport"             "treport"                "Abbeville"             
[16] "Abeville"               "L'Oise"                 "Cayeux-sur-mer"        
[19] "Cayeux"                 "POINTE DU HOURDEL"      "Berck"                 
[22] "Berck sur mer"          "Berck-sur-mer"         
  • Pas mal de choses qui se ressemblent mais ne sont pas pareilles : Saint Valéry, St Valery, Saint Valery sur Somme etc
    • On va essayer de se débarasser de la ponctuation et des lettres majuscules
    • Corriger les fautes d’orthographe
    • Se débarasser des imprécisions ou choses non nécessaires (depuis 2 ans, (Somme)) etc
  • Tout en conservant toutes les données : on ne remplace jamais la donnée initiale!!
  • On le fait à la main ici, mais des approches plus structurées existent:
    • Distance entre les groupes nominaux : combien faut il changer de caractères (stringdist)
    • Matching avec des bases géographiques : on le fera plus tard pour les coordonnées géographiques

Q1 : Complétez pour St Valery

remplacement_lieux = function(x_){
#' Standardisation des noms de lieux
#'
#' Cette fonction normalise les noms de lieux en les mettant en minuscules, 
#' en supprimant les accents et les articles, puis en les remplaçant par des 
#' noms standardisés selon une liste prédéfinie.
#'
#' @param x_ Une chaîne de caractères représentant un nom de lieu (potentiellement mal orthographié ou mal formaté).
#' @return Une chaîne de caractères avec le nom de lieu standardisé. Si aucun correspondance n'est trouvée, la fonction retourne l'entrée originale.

  x = str_to_lower(x_) # On met tout en minuscule
  x = x %>%
    gsub("\\ble\\b", "", .)%>% # On enlève "le" à l'aide d'une expression régulière (regex)
    gsub("é", "e", .)          # On enlève également les accents aigus et graves
  
  # Après inspection, on fait ce qu'on peut pour remplacer ensemble les zones géographiques
  
  if(x %in% c(' treport', 'trepot', 'treport', "treport depuis 2 ans")){
    return('Le Tréport')
  }else if(x %in% c('abbeville', 'abeville', "abbevilel", "abbville")){
    return('Abbeville')
  }else if(x %in% c(' crotoy', 'le crotoy', 'crotoy')){
    return('Le Crotoy')
  }else if(x %in% c("berck-sur-mer", "berck sur mer", "berck")){
    return('Berck-sur-mer')
  }else if(x %in% c('cayeux-sur-mer', 'cayeux', "cayeux sur mer")){
    return('Cayeux-sur-mer')
  }else if(x %in% c('saint valery sur somme',"saimt valery","saint-valery-sur-somme",
                    "saint valéry", "saint valery", "st valery", "vers saint valery" ,
                    "saint-valerie-sur-somme")){
    return('Saint-Valery-sur-Somme')
  }else if(x %in% c('hourdel', 'pointe', 'pointe du hourdel')){
    return("Pointe du Hourdel")
  }else if(x %in% c("feuquieres en vimeu",  "feuquieres - en - vimeu" )){
    return('Feuquières-en-Vimeu')
  }else if(x %in% c('noyelles sur mer', 'noyelle sur mer')){
    return('Noyelles-sur-mer')
  }else if(x %in% c("amiens",  "amiens / berck"  )){
    return("Amiens")
  }else if(x %in% c('lancheres', 'lanchere', 'lancher', "lanchéres")){
    return('Lanchéres')
  }else if(x %in% c('arlay a 5km du crotoy')){
    return('Arlay')
  }else if(x %in% c("fauquembergues (résidence secondaire)")){
    return('Fauquembergues')
  }else if(x %in% c("villers cotterets(02600)" )){
    return('Villers Cotterets')
  }else if( x %in% c("franciere (somme)"  )){
    return('Francières')
  }else if( x %in% c("hauts de seine(92)")){
    return('Hauts de Seine')
  }else if( x %in% c("Saint-Riquier", "Saint-riquier", "saint-riquier")){
    return('Saint-Riquier')
  }else if( x %in% c("pendé", "pende")){
    return('Pendé')
  }else if( x %in% c("arras")){
    return('Arras')
  }else if( x %in% c("dunkersue")){
    return('Dunkerque')
  }
  
  else{
    return(x_)
  }
}
data_ = data_ %>%
  mutate(clean_lieu = sapply(lieu, remplacement_lieux)) # On applique la fonction remplacement à chaque lieu
                                                  # et sapply renvoie un vecteur qui est assigné à 
                                                  # une nouvelle variable
unique(data_$clean_lieux)
NULL

Q2 : enregistrez la nouvelle data avec dplyr, sapply

 [1] "Hauts de Seine"                       
 [2] "Abbeville"                            
 [3] NA                                     
 [4] "Nancy"                                
 [5] "Beauvais"                             
 [6] "Essonne"                              
 [7] "Drancourt"                            
 [8] "Pas d'ici"                            
 [9] "Le Crotoy"                            
[10] "Saint-Valery-sur-Somme"               
[11] "Dunkerque"                            
[12] "Francières"                           
[13] "Saigneville"                          
[14] "Arrest"                               
[15] "Quesnoy le montant"                   
[16] "Le Tréport"                           
[17] "Feuquières-en-Vimeu"                  
[18] "Belgique"                             
[19] "Amiens"                               
[20] "Cayeux-sur-mer"                       
[21] "Saint-Riquier"                        
[22] "Eu"                                   
[23] "Pau"                                  
[24] "Miramont-sensacq"                     
[25] "Criel-sur-mer"                        
[26] "Boulogne-sur-mer"                     
[27] "Outreau"                              
[28] "Dieppe"                               
[29] "Arras"                                
[30] "Île de France"                        
[31] "Lanchéres"                            
[32] "Pointe du Hourdel"                    
[33] "Arlay"                                
[34] "Noyelles-sur-mer"                     
[35] "Aisne"                                
[36] "Pendé"                                
[37] "Quesnois"                             
[38] "Boismont"                             
[39] "Saint Raphaël"                        
[40] "Routhiauville"                        
[41] "La Molière"                           
[42] "Lille"                                
[43] "Mons-Boubert"                         
[44] "Fressennville"                        
[45] "Braine-l’Alleup"                      
[46] "VILLERS COTTERETS (02600)"            
[47] "Quend"                                
[48] "Berck-sur-mer"                        
[49] "Grofflier"                            
[50] "Conchil-le-temple"                    
[51] "Bouches du rhone"                     
[52] "Fort Mahon"                           
[53] "Fauquembergues (résidence secondaire)"
[54] "Merlimont"                            
[55] "Saint Étienne au- Mont"               
[56] "Roye"                                 
[57] "Maubeuge"                             
[58] "Rang du fliers"                       
[59] "Stella"                               
[60] "Estreboeuf"                           
[61] "Friville"                             

Variable catégorielle ordonnée : l’âge

On a classé la population par groupes d’âge. L’usage de variables factor permet d’avoir des variables catégorielles ordonnées, que l’on peut représenter sur un axe ordonné en précisant les niveaux (levels)

Q3: transformez l’âge en variable facteur

Si vous ne savez pas comment vous y prendre, utilisez ?nom_de_la_fonction pour trouver de l’aide

data_$classe_age = factor(data_$classe_age, 
                          levels = c("<20 ans","20-35 ans","35-50 ans",
                                     "50-65 ans", ">65 ans"))

Variables continues tronquées : population des phoques

  • Variable continue : on va surement devoir discretiser
  • Qui est limitée à 0
  • Avec beaucoup de non réponse

Challenges techniques :

  • Transformer la variable chr en variable int
  • On va utiliser la population totale

Q4 : Tabulez la population des phoques!


                 100 - 200                      100.0 
                         1                          5 
                    1000.0                      120.0 
                         4                          1 
                    1300.0                  1500-3000 
                         1                          1 
                      20.0                    200-300 
                         1                          1 
200 gris et 400 veau marin                      200.0 
                         1                          2 
                    2000.0                    20000.0 
                         2                          1 
                     300.0                     3000.0 
                         4                          1 
                     400.0                      450.0 
                         3                          2 
           50 000- 100 000                      500.0 
                         1                          5 
                   50000.0                      600.0 
                         1                          2 
                   600/700                800 environ 
                         1                          1 
                     800.0                        Bcp 
                         1                          2 
                  Beaucoup              Beaucoup trop 
                         1                          1 
          Entre 500 et 800                        Nln 
                         1                          1 
                        Nn                         No 
                         2                          1 
                        Nô                        non 
                         1                          1 
                       Non                   Non - 50 
                       112                          1 
                 Non (100)                   Non (20) 
                         2                          1 
                 Non (200)                 Non (2000) 
                         2                          1 
                  Non (30)                  Non (300) 
                         1                          1 
                 Non (450)                  Non (500) 
                         1                          2 
                 Non (650)                  Non (800) 
                         1                          1 
                Non (8000)                    Non 100 
                         1                          1 
                  Non 1000               Non beaucoup 
                         1                          1 
                       Oui                  Oui - 100 
                        16                          1 
              Oui - 100000                  Oui - 200 
                         1                          1 
             Oui - 300-400                   Oui - 50 
                         1                          1 
                Oui (3000)                  Oui (800) 
                         1                          1 
                  Oui 1000                  Oui(1000) 
                         1                          1 
           OUI, 500 et 200     Pas du tout (beaucoup) 
                         1                          1 
                   Pas mal               Plus de 1500 
                         1                          1 
        Quelques centaines          Quelques milliers 
                         1                          1 
          Quelques uns, 25               Une centaine 
                         1                          1 

On a beaucoup de réponses mixtes, il faudrait extraire les données chiffrées, les ordres de grandeur, et les réponses comme Non et Oui

remplacement_phoques_1 = function(x_){
#' Cette fonction donne une indication sur la connaissance (oui ou non) de la population de phoque
#' @param x_ une chaine de caractères

  
  return(grepl("non", str_to_lower(x_)))
}


remplacement_phoques_population = function(x_, option = "max"){
  
#' Extraction et classification des nombres liés aux populations de phoques
#'
#' Cette fonction analyse une chaîne de caractères pour extraire des nombres 
#' (représentant des populations de phoques) et les classifier en différentes catégories 
#' (`Beaucoup`, `Centaines`, `Milliers`, `Quelques uns`, etc.).
#'
#' @param x_ Une chaîne de caractères contenant une estimation de population (peut inclure des chiffres, des mots ou des expressions variées).
#' @param option Une option définissant si l'on souhaite récupérer la valeur minimale ou maximale 
#' dans le cas de plages numériques (`"min"` ou `"max"`). Par défaut `"max"`.
#' @return Une valeur numérique si un chiffre est détecté, ou une catégorie (`"Beaucoup"`, `"Centaines"`, `"Milliers"`, `"Quelques uns"`, `"Non"`, etc.).

    

  # Approche par regex : 
  potential_number = x_ %>%
    gsub(" ", "", .)%>%
    str_extract_all(.,"\\d+\\.?\\d*")%>%
    unlist()%>%
    as.numeric()
  # On repère les chiffres : 
  # \\d+ repère un chiffre ou plusieurs
  # \\.? repère un point décimal
  # \\d* prent les 0
  
  # Ensuite on vérifie s'il y a des "-" ou des "/"
  
  tiret_barre = grepl("[-/]", x_)

  # Message d'erreur si l'option n'est pas bien spécifiée  
  if(!(str_to_lower(option) %in% c("min", "max"))){
    stop("Error : option is ill picked")
  }
  
    # Enfin, on prend les "beaucoup" en compte : 

  if(x_ %in% c(startsWith(str_to_lower(x_), "beauco"), 
              'Bcp', 'Non beaucoup', 'Pas du tout (beaucoup)')){
    return("Beaucoup")
  }else if(is_empty(potential_number)){
    if(startsWith(str_to_lower(x_), 'oui')){
      return('Oui')
    }else if(grepl("centai", str_to_lower(x_))){
      return('Centaines')
    }else if(grepl('milli', str_to_lower(x_))){
      return('Milliers')
    }else if(grepl('un', str_to_lower(x_))){
      return('Quelques uns')
    }else{
      return('Non')
    }
  }else{
    if(tiret_barre){
      if(length(potential_number)>1){
        if(option=='min'){
          return(min(potential_number))
        }else if(option=="max"){
          return(max(potential_number))
        }
      }else{
        return(as.numeric(potential_number))
      }
    }else{
      return(sum(as.numeric(potential_number)))
    }
  }
}
< table of extent 0 >
data_ = data_ %>%
  mutate(clean_pop_yes_no = sapply(q2_pop_phoques, remplacement_phoques_1),
         clean_pop_max = sapply(q2_pop_phoques, remplacement_phoques_population))

# On récupère également la distinction entre chiffrée et ordre de grandeur
chiffre_ou_non = data_%>%
  select(clean_pop_max)%>%
  pull()%>%
  as.numeric()


data_ = data_ %>%
  mutate(
    # Créer la variable numérique en convertissant les valeurs en numérique
    chiffre_num = as.numeric(clean_pop_max),
    
    # Remplacer les valeurs non numériques par NA
    clean_pop_phoque_chiffre = if_else(is.na(chiffre_num), NA_real_, chiffre_num),
    
    # Créer la variable non numérique pour les réponses textuelles
    clean_pop_phoque_non_chiffrée = if_else(is.na(clean_pop_phoque_chiffre), clean_pop_max, NA_character_)
  )

Variables catégorielles ordinales : professions

t(table(data_$profession))# Pour voir les fréquences
      
       Agence immobilier Agent public Agriculteur (à préciser) Aide à domicile
  [1,]                 1           20                        3               1
      
       Aide soignante Archéologue Assistante à domicile Cadre Cadre supérieur
  [1,]              1           1                     1     1               3
      
       Chômage Collégien Commerçant (préciser) Cuisine
  [1,]       1         1                    13       1
      
       Dessinateur en bureau d'étude Élève Élèves Employé Employee medical
  [1,]                             1     1      2      26                1
      
       Enseignante Entrepreneur indépendant Entreprise/tourisme (à préciser)
  [1,]           1                        1                                1
      
       Etudiant Étudiant Étudiant college Etudiante Étudiante Étudiantes
  [1,]        1        5                1         2         2          1
      
       Famille d'accueil Femme au foyer Foyer d’enfants Gérant de restaurant
  [1,]                 1              1               1                    1
      
       Infirmiere Infirmière
  [1,]          1          1
      
       Intermittente du spectacle + boutique d'ésortérisme Mairie
  [1,]                                                   1      1
      
       Maîtresse d'école Mécanicien Medical Non Ouvrier Ouvrier agricole
  [1,]                 1          1       1   1       6                1
      
       Pas demandé Pêcheur (préciser) Photographe Prof Professeur
  [1,]           1                  2           1    4          1
      
       Réceptionniste en hôtellerie Recruteur de donateurs pour association
  [1,]                            1                                       1
      
       Restauration/hôtellerie Retraite Retraité Retraité militaire
  [1,]                      12       37       32                  1
      
       Retraité pêcheurs Retraité/ avant aide soignante Retraitée Retraites
  [1,]                 1                              1         1         1
      
       Rien Sans Sans emploi Tabac Traducteur
  [1,]    1    1           1     1          1
      
       Travail avec des enfants handicapés Usine
  [1,]                                   1     2

On a encore des problèmes d’encodage : je vous laisse y penser

  • Trouvez les dénominations en CSP les plus pertinentes
  • Regroupez les personnes dans la bonne CSP en :
    • Modifiant la fonction remplacement()
    • Et en attribuant les CSP à une nouvelle variable

On peut utiliser un prétraitement des données et la fonction startsWith(x, prefix) pour rassembler tout ce qui commencerait par la même racine

Q5 : utilisez startsWith() pour compléter la fonction suivante et rédigez la documentation

csp_questionnaire = c("Entreprise/tourisme", "Restauration/hôtellerie" , "Agriculteur (à préciser)", 
                      "Pêcheur (préciser)","Commerçant (préciser)", "Agent public",
                      "Cadre supérieur", "Employé", "Ouvrier")


remplacement_professions = function(x_){
  
#' Classification des professions à partir d'un questionnaire
#'


  x = x_%>%
    str_to_lower()%>%
    gsub("é", "e", .)%>%
    gsub("è", "e", .)   
  
  if(x %in% c('agriculteur (à preciser)')){
    return("Agriculteur exploitant")
  }else if(startsWith(x, "employ") | x %in% c('aide à domicile', 
                                              'aide soignante', 'assistante à domicile', 
                                              "famille d'accueil", "tabac", "medical",
                                              "recruteur de donateurs pour association")){
    return("Employé.e")
  }else if(x %in% c('sans', 'rien', 'sans emploi', 'chômeur','chômage', 'non', "femme au foyer")){ 
  
  # COMPLETER L'EXPRESSION
  
    return('Sans activité professionelle (hors retraite)')
  }else if(startsWith(x, 'retrait')){
    return('Retraité.e') 
  }else if(startsWith(x, 'ouvrier') | x %in% c('mecanicien', 'usine')){
    return("Ouvrier")
  }else if(startsWith(x, 'foyer') | x %in% c("infirmiere", "maîtresse d'ecole", 'enseignante', 'professeur', 'prof', "foyer d'enfants", "travail avec des enfants handicapes")){
    return('Professions intermédiaires')
  }else if(startsWith(x, 'cadr') | startsWith(x, 'intermittente') | x %in% c('traducteur', "archeologue", "dessinateur en bureau d'etude", 'photographe', "agence immobilier") ){
    return("Cadres et professions intellectuelles supérieures")
  # Catégories du questionnaire
  }else if(x %in% c('gerant de restaurant', 'receptionniste en hôtellerie', 'restauration/hôtellerie', "cuisine")){
    return("Restauration/hôtellerie")
  }else if(startsWith(x, "entrepr") | startsWith(x, "commerçant")){
    return("Artisans, commerçants, chefs d'entreprise")
  }else if(x %in% c('agent public', 'mairie')){
    return('Agent public')
  }else{
    return(x_)
  }
    
}
csp_questionnaire = c("Entreprise/tourisme", "Restauration/hôtellerie" , "Agriculteur (à préciser)", "Pêcheur (préciser)","Commerçant (préciser)", "Agent public",
                      "Cadre supérieur", "Employé", "Ouvrier")


remplacement_professions = function(x_){

#' Classification des professions à partir d'un questionnaire
#'
#' Cette fonction standardise les réponses d'un questionnaire concernant la profession des répondants 
#' en les classant dans des catégories socio-professionnelles.
#'
#' @param x_ Une chaîne de caractères représentant une profession, telle qu'indiquée par un répondant.
#' @return Une chaîne de caractères correspondant à une catégorie professionnelle standardisée.  

  x = x_%>%
    str_to_lower()%>%
    gsub("é", "e", .)%>%
    gsub("è", "e", .)   
  
  if(x %in% c('agriculteur (à preciser)')){
    return("Agriculteur exploitant")
  }else if(startsWith(x, "employ") | x %in% c('aide à domicile', 
                                              'aide soignante', 'assistante à domicile', 
                                              "famille d'accueil", "tabac", "medical",
                                              "recruteur de donateurs pour association")){
    return("Employé.e")
  }else if((startsWith(x, "etudian") | startsWith(x, "eleve") | startsWith(x, "colleg") | x %in% c('sans', 'rien', 
                                                                                                   'sans emploi', 'chômeur',
                                                                                                   'chômage', 'non', "femme au foyer"))){
    return('Sans activité professionelle (hors retraite)')
  }else if(startsWith(x, 'retrait')){
    return('Retraité.e') 
  }else if(startsWith(x, 'ouvrier') | x %in% c('mecanicien', 'usine')){
    return("Ouvrier")
  }else if(startsWith(x, 'foyer') | x %in% c("infirmiere", "maîtresse d'ecole", 'enseignante', 'professeur', 'prof', "foyer d'enfants", "travail avec des enfants handicapes")){
    return('Professions intermédiaires')
  }else if(startsWith(x, 'cadr') | startsWith(x, 'intermittente') | x %in% c('traducteur', "archeologue", "dessinateur en bureau d'etude", 'photographe', "agence immobilier") ){
    return("Cadres et professions intellectuelles supérieures")
  # Catégories du questionnaire
  }else if(x %in% c('gerant de restaurant', 'receptionniste en hôtellerie', 'restauration/hôtellerie', "cuisine")){
    return("Restauration/hôtellerie")
  }else if(startsWith(x, "entrepr") | startsWith(x, "commerçant")){
    return("Artisans, commerçants, chefs d'entreprise")
  }else if(x %in% c('agent public', 'mairie')){
    return('Agent public')
  }else{
    return(x_)
  }
    
}

Variables catégorielles nominales : nom des phoques

Ici, on va adopter la même approche par fonction de remplacement()

table(data_$q1_nom_phoques)

                                Gris                   Gris et veau marin 
                                   1                                    1 
                Gris et veaux marins                             Les deux 
                                   1                                    1 
                                 Nln                                   Nn 
                                   1                                    2 
                                 Nob                                  non 
                                   1                                    1 
                                 Non                                  Oui 
                                 136                                   47 
       Oui (manque les phoques gris)                         Oui les deux 
                                   1                                    2 
                     Oui phoque gris OUI phoque gris et phoque veau-marin 
                                   1                                    1 
                   Oui plus ou moins                       Oui veau-marin 
                                   1                                    1 
                         Pas de tout                          Phoque gris 
                                   1                                    1 
                Phoque gris et blanc                Phoque veau-marin oui 
                                   1                                    1 
                        Phoques gris                             Touriste 
                                   1                                    1 
                            Vaumarin                           Veau-marin 
                                   1                                    2 
         Veau-marin et phoque commun                           Veau marin 
                                   1                                    5 
            Veau marin , phoque gris                 Veau marin et phoque 
                                   1                                    1 
        Veaux-marins et phoques gris 
                                   1 

On va prendre comme réponses possible : oui, non, Partiel: veau marin et Partiel: phoque gris

Q6 - Faites une fonction de remplacement pour les phoques

remplacement_phoques_noms = function(x){
  
#' Standardisation des réponses sur les espèces de phoques observées
#'
#' Cette fonction catégorise les réponses concernant la présence des espèces de phoques 
#' (veau-marin et phoque gris) en les regroupant dans des classes standardisées.
#'
#' @param x Une chaîne de caractères correspondant à une réponse du questionnaire sur les phoques observés.
#' @return Une chaîne de caractères standardisée : `"Oui"`, `"Non"`, `"Partiel : Veau-marin"`, `"Partiel : Phoque gris"`, ou la réponse initiale si elle ne correspond à aucune catégorie.
    
  if(x %in% c('Veaux-marins et phoques gris', 'Veau marin , phoque gris', 'OUI phoque gris et phoque veau-marin',
              'Gris et veaux marins', 'Veau marin et phoque', 'Les deux', 'Oui', 'Gris et veau marin', 
              'Gris et veaux marins', "Les deux ", 'Oui les deux')){
    return('Oui')
  }else if(x %in% c('Non', 'nn', 'Nob', 'Nln', 'Non', 'Nn', 
                    'non', 'Pas de tout', 'Touriste', 'Oui plus ou moins')){
    return('Non')
  }else if(x %in% c('Oui (manque les phoques gris)', 'Veau-marin', 
                    'Phoque veau-marin oui', 'Vaumarin', 'Veau marin',
                    'Veau-marin et phoque commun', 'Veau-marin', 'Oui veau-marin')){
    return("partiel : Veau-marin")
  }else if(x %in% c('Gris', 'Oui phoque gris', 'Phoque gris et blanc', 'Phoques gris', 'Phoque gris')){
    return('Partiel : phoque gris')
  }else{
    return(x)
  }
}

data_ = data_ %>%
  mutate(clean_noms_phoques = sapply(q1_nom_phoques, remplacement_phoques_noms))

Variables catégorielles nominales multiples: avis sur les phoques

La différence ici, c’est que plusieurs choix sont possibles

# Trouver toutes les phrases possibles

phrases_ = c() # Initier un stock de phrases vide

for(row_ in 1:nrow(data_)){ # Pour chaque individu
  candidate_ =  unlist(# On enlève la structure de liste 
    strsplit(as.character(data_[row_, "q6_avis"]), # qui résulte de la séparation de chaque réponse
             ", ") # autour de la virgule
    ) 
  for(element_ in candidate_){ # On regarde pour chaque phrase cnadidate
    if(!(element_ %in% phrases_)){ # Si elle a déja été dite
      phrases_ = c(phrases_, element_) # Sinon, on l'ajoute
    }
  }
}
# Imprimer les 15 premières phrases
phrases_[1:15]
 [1] "Il faut laisser la nature évoluer librement"                                 
 [2] "La surfréquentation peut générer des dérangements pour la nature"            
 [3] "Les conflits entre les humains et les phoques augmentent"                    
 [4] "Les phoques font partie du patrimoine du territoire"                         
 [5] "Il y a trop de phoques veau-marin"                                           
 [6] "Il y a trop de phoques gris"                                                 
 [7] "Il ne faut pas que le nombre de phoque augmente plus"                        
 [8] "Les phoques mangent trop de poissons"                                        
 [9] "Les phoques représentent une opportunité économique pour le territoire"      
[10] "Les phoques représentent une menace économique pour le territoire"           
[11] "Il faudrait un groupe de travail local sur les interactions avec les phoques"
[12] "Il faudrait plus de panneaux d’informations sur les phoques"                 
[13] "Il faudrait des barrières pour empêcher de déranger les phoques"             
[14] "Il faudrait un centre d’information sur les phoques"                         
[15] "Visites guide nature m"                                                      

On a des problèmes de remplissage du questionnaire!

On va récupérer les choix du questionnaire

  • Ensuite, on fait ce qu’on appelle du one hot encoding, c’est à dire que l’on:
    • Transforme chaque affirmation en une variable
    • Qui prend la valeur 0 si absente, 1 si présente
    • Permet de numériser les variables catégorielles sans imposer d’ordre
data_ = data_ %>%
  mutate(ID = seq(1:nrow(data_))) # On donne un identifiant à chaque individu statistique

# On crée une table d'occurence vide
table_occurrences <- matrix(0,
                            nrow = nrow(data_), 
                            ncol = length(phrases_possibles),
                            dimnames = list(seq(1:nrow(data_)),
                                             phrases_possibles)) # Nomme les dimensions

# Remplir le tableau avec les occurrences
for (i in 1:nrow(data_)) {
  phrases_ = unlist(                            # On enlève la structure de liste 
    strsplit(as.character(data_[i, "q6_avis"]), # qui résulte de la séparation de chaque réponse
             ", ")                              # autour de la virgule
    ) 
  for (phrase in phrases_) {                    # Pour chaque phrase exprimée, 
    if (phrase %in% phrases_possibles) {        # si elle est dite, 
      table_occurrences[i, phrase] <- table_occurrences[i, phrase] + 1 # on encode
    }
  }
}

Q7 : Convertir la matrice en dataframe pour une meilleure lisibilité

# Convertir la matrice en dataframe pour une meilleure lisibilité

table_occurrences_df <- as.data.frame(table_occurrences)

colnames(table_occurrences_df) = paste0("q_clean_", c('nature_libre','surfréquentation', 'conflits', 
                                   'patrimoine','trop_veau', 'trop_gris','pas_plus_phoques',
                                   'trop_poisson','opportunité_économique', 'menace_économique',
                                   'groupe_travail', 'panneaux',
                                   'barrières', 'centre_information', 'menace_tradition'))

table_occurrences_df = table_occurrences_df%>%
  mutate('ID' = seq(1, nrow(table_occurrences_df)))

Variables catégorielles nominales multiples : questions sur les phoques

On fait la même chose pour la question sur les phoques

phrases_ = c() # Initier un stock de phrases vide

for(row_ in 1:nrow(data_)){ # Pour chaque individu
  candidate_ =  unlist(# On enlève la structure de liste 
    strsplit(as.character(data_[row_, "q5_classe_phoque"]), # qui résulte de la séparation de chaque réponse
             ", ") # autour de la virgule
    ) 
  for(element_ in candidate_){ # On regarde pour chaque phrase cnadidate
    if(!(element_ %in% phrases_)){ # Si elle a déja été dite
      phrases_ = c(phrases_, element_) # Sinon, on l'ajoute
    }
  }
}

phrases_possibles = c("Un animal sympathique et paisible", "Une espèce invasive et nuisible",
                      "Un concurrent pour les activités humaines", "Une espèce menacée",
                      "Un prédateur supérieur"
                      )

data_ = data_ %>%
  mutate(identifier = seq(1:nrow(data_))) # On donne un identifiant à chaque individu statistique


# On crée une table d'occurence vide
table_occurrences <- matrix(0,
                            nrow = nrow(data_), 
                            ncol = length(phrases_possibles),
                             dimnames = list(seq(1:nrow(data_)),
                                             phrases_possibles)) # Pour stocker un identifiant

# Remplir le tableau avec les occurrences
for (i in 1:nrow(data_)) {
  phrases_ = unlist(# On enlève la structure de liste 
    strsplit(as.character(data_[i, "q5_classe_phoque"]), # qui résulte de la séparation de chaque réponse
             ", ") # autour de la virgule
    ) 
  for (phrase in phrases_) {
    if (phrase %in% phrases_possibles) { # si elle est dite, 
      table_occurrences[i, phrase] <- table_occurrences[i, phrase] + 1
    }
  }
}

Variables catégorielles nominales multiples : loisirs

  • Il y a beaucoup trop de réponses uniques (64)
  • Il faut revenir aux résultats du questionnaire, et traiter notamment les réponses collées (variables catégorielles multiples)
  • On refait du one hot encoding
# I. On doit cleaner les données pour bien pouvoir les isoler : on supprime ce qu'il se passe entre parenthèse pour pouvoir séparer les entités par l'usage de la virgule
data_loc = data_ %>%
  select(loisir)

data_loc= data_loc %>%
  mutate(loisir_easy = gsub("\\s*\\(.*?\\)", '', loisir))%>%
  mutate(loisir_easy = gsub(" ", "", loisir_easy))%>%
  mutate(loisir_easy = str_to_lower(loisir_easy))



phrases_possibles = c("Pêche):", "Chasse", "Voile", "vélo", "natation",
                      "Planceàvoile", "Paddle",  "Kayak/canoë", "Kitesurf", "Equitation", "Wingfoil",
                      "Promenade", "Marche", "Naturaliste")
phrases_possibles = str_to_lower(phrases_possibles)

# II. On crée une table d'occurence vide
table_occurrences <- matrix(0,
                            nrow = nrow(data_), 
                            ncol = length(phrases_possibles) +1, # On ajoute une case pour prendre en compte les cas "Autres"
                            dimnames = list(seq(1:nrow(data_)),
                                             c(phrases_possibles, "Autre"))) # Pour stocker un identifiant




#III. Remplir le tableau avec les occurrences
for (i in 1:nrow(data_)) {
  phrases_ = 
    unlist(# On enlève la structure de liste 
    strsplit(as.character(data_loc[i, "loisir_easy"]), # qui résulte de la séparation de chaque réponse
             ",") # autour de la virgule
    ) 
  for (phrase in phrases_) {
    if (phrase %in% phrases_possibles) { # si elle est dite, 
      table_occurrences[i, phrase] <- table_occurrences[i, phrase] + 1
    }else{
      table_occurrences[i, "Autre"] = table_occurrences[i, "Autre"] +1
    }
  }
}

Synthèse et nettoyage final

On merge tout et on sélectionne les données propres :

Q8 : utilisez le pipe operator (%>%) pour sélectionner les colonnes de la full_data et récupérer les colonnes qui ont le nom clean avec grepl

On sauvegarde les données en .xlsx mais les formats de référence sont plutot:

  • .csv
  • pour des données plus larges .json
  • .parquet
  • ou encore les data.tables pour les format de matrices avec beaucoup de 0 suivant le one hot encoding de larges données (accélère également leur écriture et leur ouverture)
data_clean = full_data %>%
  select(c('date', 'genre', 'classe_age',    # Les données déja propres
           colnames(full_data)[full_data %>%         # Les colonnes que l'on a nommé clean
                                 colnames() %>%      # car nettoyées!
                                 grepl("clean", .)]
           )
         )

colnames(data_clean) = gsub("clean_", "", colnames(data_clean))

write.xlsx(data_clean, here("data", "processed", "data_cleaned.xlsx"))

Documentation

  • Créez un fichier .txt ou .md qui détaille les données brutes et enregistrez le dans /docs/README_rawdata.txt
  • Créez un fichier .txt ou .md qui détaille les transformations appliquées aux données et les fonctions utilisées, et enregistrez le dans /docs/README_datacleaning.txt
    • Faites un dictionnaire de variables: dansl’idéal, il faut le faire, mais je l’ai fait pour vous (enfin, ClaudeAI l’a fait à ma place)
      • Nom des variables; Descriptions; Type de variables ;Unité de mesure ;Valeurs possibles ;Valeurs manquantes ;Source des données; Commentaires et remarques
  • Idéalement, il faudrait refaire tout cela pour réplication de sorte que :
    • Updatez le script fonctions.R avec les fonctions que l’on a utilisées : elles ne sont pas encore très modulaires ou beaucoup réutilisables, mais c’est un premier pas.
    • Appelez le script fonctions.R avec source(here("scripts", "fonctions.R")) en début de script et utilisez les fonctions

Analyse de données

Quelles analyses mener?

  1. Etude des valeurs manquantes
  2. Etude des distributions des variables clés
  3. Analyse de corrélation entre variables
  4. Analyse multivariées
- The lockfile is already up to date.

Analyses simples

Pour cela, on va utiliser la bibliothèque ggplot2 (Grammar of Graphics) qui prend les éléments suivants :

  • Les données (data)
  • Les “aesthetics” (aes()) : comment représenter les variables, qui est en abscisse (x=), en ordonnée (y=), en couleur (color=)
  • La géométrie (geom()): le type de graphique
  • Une variété d’éléments de style :
    • scale_color_, scale_fill pour les couleurs
    • theme() pour les axes, polices, tailles, couleur de fond
  • On peut également représenter sur différents panels avec les fonctions facet_
  • Marche au mieux lorsque les données sont sous le format tidy: une variable par colonne, une observation par ligne
    • On utilise parfois pivot_longer() pour transformer des données “larges” en “longues”, pour tout représenter d’un coup

Les variables démographiques

custom_theme = function(){
  theme_minimal() +  # Base theme
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),  # Rotate x-axis labels by 45 degrees
    legend.position = "bottom"
  )
}

p = data_ %>%
  select(c('genre', 'classe_age','date', 'professions', 'lieu')) %>%
  pivot_longer( - date, 
               values_to = 'values',
               names_to = 'names')%>%
    ggplot(aes(x=values))+
    geom_histogram(stat="count")+
    facet_grid(~names,  scales = "free")+
  custom_theme()

Q1 : Ecrivez le code pour plotter la variable classe_age avec ggplot

Indices :

  • les datas sont elles tidy avec une seule variable?
  • Pensez à utiliser la caractéristique stat = count
p = data_ %>%
  ggplot(aes(x = classe_age))+
  geom_bar(stat= 'count')

Ce n’est pas très joli, ni modulaire : on va écrire une fonction

plot_distribution("classe_age")

ggsave(here('results','figure', paste0("classe_age",'.jpg')), width = 25, height = 18, units = 'cm')
plot_distribution("professions")

ggsave(here('results','figure', paste0("professions",'.jpg')), width = 25, height = 18, units = 'cm')
plot_distribution("lieu")

ggsave(here('results','figure', paste0("lieu",'.jpg')), width = 25, height = 18, units = 'cm')

Population de phoques : noms

plot_distribution("noms_phoques")
ggsave(here('results','figure', paste0("noms_phoques",'.jpg')), width = 25, height = 18, units = 'cm')

Population de phoques

plot_distribution("pop_phoque_chiffre")
ggsave(here('results','figure', paste0("pop_phoque_chiffre",'.jpg')), width = 25, height = 18, units = 'cm')
plot_distribution("phoque_paisible")

ggsave(here('results','figure', paste0("phoque_paisible",'.jpg')), width = 25, height = 18, units = 'cm')
plot_distribution("phoque_invasive")

ggsave(here('results','figure', paste0("phoque_invasive",'.jpg')), width = 25, height = 18, units = 'cm')
plot_distribution("phoque_concurrent")

ggsave(here('results','figure', paste0("phoque_concurrent",'.jpg')), width = 25, height = 18, units = 'cm')
plot_distribution("phoque_menacee")

ggsave(here('results','figure', paste0("phoque_menacee",'.jpg')), width = 25, height = 18, units = 'cm')
plot_distribution("phoque_prédateur")

ggsave(here('results','figure', paste0("phoque_prédateur",'.jpg')), width = 25, height = 18, units = 'cm')

Analyse bivariée

On cherche la répartition de notre échantillon entre classes d’âge et différente caractéristiques :

p = data_ %>%
ggplot(aes(x = classe_age, fill = professions)) +
  geom_bar(position = "fill") +  # Normalized proportions
  theme_minimal()+
  scale_fill_brewer(palette = "Paired")

Analyse bivariée : phoques paisible et nature sauvage

Q2 : plottez la correlation entre phoque_paisible et q_nature_libre

data_ %>%
  ggplot(aes(x = phoque_paisible, fill = q_nature_libre)) +
  geom_bar(position = "fill") +  # Normalized proportions
  theme_minimal()+
  scale_fill_brewer(palette = "Paired")

Analyse bivariée : phoques paisible et connaissance des phoques

Q3 : plottez la correlation entre phoque_paisible et pop_yes_no

data_ %>%
  ggplot(aes(x = phoque_paisible, fill = pop_yes_no)) +
  geom_bar(position = "fill") +  # Normalized proportions
  theme_minimal()+
  scale_fill_brewer(palette = "Paired")

Analyses multivariées : global

correlation_matrix = cor(data_ %>%
                           select(to_factor_vect)%>%
                           select(- c('wingfoil'))%>%
                           mutate(across(everything(), as.numeric)), 
                         method = 'pearson')


# On efface la diagonale (corrélation de 1) et on garde seulement un triangle pour éviter la redondance
diag(correlation_matrix) = NA 

# Convert matrix to long format for ggplot
cor_long = reshape2::melt(correlation_matrix)

p = ggplot(cor_long, aes(Var1, Var2, fill = value)) +
  geom_tile() +
  scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0) +
  labs(title = "Correlation Heatmap of One-Hot Encoded Variables",
       x = " ",
       y = " ",
       fill = "Correlation") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Analyse multivariée : restreinte

On va essayer de restreindre un peu l’analyse : Q4 : prenez un sous ensemble de variables, comme les caractéristiques des phoques et les réponses aux questions et faites une heatmap

correlation_matrix = cor(data_ %>%
                           select(colnames(data_)[startsWith(colnames(data_), "q_")])%>%
                           mutate(across(everything(), as.numeric)), 
                         method = 'pearson')

diag(correlation_matrix) = NA 

# Convert matrix to long format for ggplot
cor_long = reshape2::melt(correlation_matrix)

Analyse par correspondance multiple

Technique d’analyse multivariées pour identifier la structure d’un espace de grande dimension

Origine et développement

  • Jean-Paul Benzécri (années 1960) :
    • Pionnier de l’analyse des correspondances, il développe l’Analyse des Correspondances Simples (ACS) pour représenter graphiquement la structure des tableaux de contingence.
    • Nécessité de visualiser les relations entre catégories dans des enquêtes sociales, marketing, etc.
    • Extension vers l’Analyse des Correspondances Multiples (ACM)
    • Adaptation de l’ACS pour traiter simultanément plusieurs variables qualitatives, en combinant plusieurs tableaux de contingence dans une matrice globale.
  • Harold Hotelling (années 1930-1940) :
    • Pionnier de l’Analyse en Composantes Principales (ACP), il formalise la réduction de dimensionnalité pour des variables quantitatives.
    • Introduit l’idée de décomposition en valeurs propres, qui vise à extraire des axes expliquant la plus grande part de la variance.
  • Théorie de l’information et géométrie
    • L’inertie (ou dispersion) dans les données est mesurée de manière similaire à la variance en ACP, ce qui permet de comparer et de représenter les structures des données.

Intuition mathématique – De la géométrie à la décomposition

  • Principe de base : représentation géométrique
    • Chaque individu est représenté par un point dans un espace de dimension élevée, chaque axe correspondant à une modalité (catégorie).
    • Objectif : trouver un espace de dimension réduite qui capture l’essentiel des relations entre modalités et individus
  • Mesure de la dispersion :l’inertie
    • Inertie totale : analogue à la variance en ACP, elle mesure la dispersion totale des points par rapport au centre.
    • Chaque modalité contribue à cette inertie selon sa fréquence et son écart par rapport à l’attendu en cas d’indépendance.
  • Décomposition en valeurs singulières (SVD)
    • Inspirée par l’ACP de Hotelling, la SVD permet de décomposer la matrice normalisée en axes factoriels orthogonaux.
    • La SVD « fait pivoter » l’espace initial pour révéler les directions (axes) où la dispersion (l’information) est maximale.
    • Les axes sélectionnés expliquent successivement des parts décroissantes de l’inertie totale.

Fonctionnement pas à pas – De la donnée brute à l’interprétation

  • Préparation des données : construction d’un tableau binaire :
    • Chaque individu est décrit par des réponses qualitatives, transformées en indicateurs (1 ou 0) pour chaque modalité.
  • Normalisation et calcul des distances
    • Utilisation de la distance du khi-deux, adaptée aux données qualitatives, pour mesurer la similitude entre profils.
    • Pondération des modalités selon leur fréquence pour équilibrer leur influence.
  • Décomposition via SVD
    • La matrice normalisée est décomposée en composantes grâce à la SVD, qui découle directement des idées de réduction de dimensionnalité portées par l’ACP de Hotelling.
    • Chaque axe factoriel est associé à une valeur propre qui quantifie la part d’inertie expliquée.
  • Projection et interprétation
    • Projection simultanée des individus et des modalités sur les axes factoriels.
    • Les points proches indiquent des profils similaires.
    • Les axes traduisent des tendances ou oppositions sous-jacentes (ex. profils traditionnels vs. profils modernes).

Idée générale

  • On minimise la distance du khi-deux intra-classe : les modalités proches doivent rester proches
  • On maximise la dispersion entre groupes distincts: les modalités et individus différents doivent être bien séparés sur les axes factoriels.

Mise en pratique

  • Il faut préparer les données: on ne va pas tout garder
  • Par ailleurs, la structure de nos questions n’est pas parfaite pour utiliser l’ACM :
    • Fait pour quand plusieurs niveaux d’une même question sont mutuellement exclusifs
    • Nous, on a autorisé 2 ou 5 réponses par question(s)
    • On n’a pas exactement la même structure, donc la distance du khi-deux risque d’être un peu modifiée
    • On pourrait modifier les réponses et les pondérer par le nombre de réponses
  • Malgré ces problèmes, on va illustrer la méthode : on réfléchira ensuite à la meilleure manière
  • Pour cela on utilise les bibliothèques FactoMineR et factoextra
library(FactoMineR)
library(factoextra)

# I. Préparer les données : il faut que des facteurs

data_for_analysis = data_%>%
  select(c(to_factor_vect, 'genre', 'classe_age', 'pop_yes_no', 'professions', 'noms_phoques'))%>%
   mutate(across(everything(), as.factor))%>%
  drop_na()

Réaliser l’ACM

  • La fonction MCA() va :
    • Construire le tableau disjonctif complet.
      • Calculer la distance du khi-deux pour chaque individu et modalité.
      • Réaliser la décomposition en valeurs singulières qui, de manière implicite, minimise la somme des distances (c’est-à-dire l’inertie non expliquée).
  • On enlève les modalités les plus corrélées
                          q_trop_gris                           q_trop_veau 
          "q_trop_gris - q_trop_veau"           "q_trop_veau - q_trop_gris" 
                       q_trop_poisson                    q_pas_plus_phoques 
"q_trop_poisson - q_pas_plus_phoques" "q_pas_plus_phoques - q_trop_poisson" 
correlation_matrix[which(correlation_matrix>.7)]
[1] 0.9642916 0.9642916
res.mca <- MCA(data_for_analysis%>%
                 select(-c('classe_age', 'pop_yes_no', 'professions', 'noms_phoques', 
                           'q_trop_veau', 'q_pas_plus_phoques')), 
               graph = FALSE)

Visualiser les résultats

On va visualiser le scree plot qui montre la part de variance expliquée par chaque axe factoriel

fviz_screeplot(res.mca, addlabels = TRUE, ylim = c(0, 50),
               title = "Inertie expliquée par les axes")

Visualisation des individus et des modalités

Visualisation des modalités

Visualisation des modalités restreintes

Visualisation des modalités restreintes

Visualisation des individus

Visualisation des individus avec classes d’age

palette_5 <- c("#E41A1C", "#377EB8", "#4DAF4A", "#FF7F00", "#984EA3")


fviz_mca_ind(res.mca,
             label = "none", # masquer le texte des individus
             ggtheme = theme_minimal (), 
             palette = palette_5,
             habillage = data_for_analysis$classe_age,
             title=" ")

Annexes

Différences des méthodes géométriques

  • Type de données
    • ACP : Variables quantitatives.
    • ACS : Deux variables qualitatives (tableau de contingence).
    • ACM : Plusieurs variables qualitatives (tableau disjonctif complet).
  • Méthodologie sous-jacente
    • ACP : Basée sur la variance et la covariance (analyse matricielle des données numériques).
    • ACS & ACM : Basées sur les fréquences observées vs. attendues (utilisation de la distance du khi-deux) et la décomposition en valeurs singulières des tableaux normalisés.
  • Objectifs analytiques
    • ACP : Réduction de dimensionnalité et interprétation des relations linéaires entre variables quantitatives.
    • ACS : Étude de l’association entre deux variables catégorielles.
    • ACM : Exploration simultanée et synthèse des relations entre plusieurs variables qualitatives.

Back to main