R Initiation - Master CESSA - Exercices d’application

logo

1 TD n°01 : Création de vecteurs

  • Créer un vecteur v1 qui va de 1 à −1 par pas de −0.25
v1 = seq(1, -1, by = -.25)
v1
## [1]  1.00  0.75  0.50  0.25  0.00 -0.25 -0.50 -0.75 -1.00
  • Créer un vecteur v2 qui va de 0 à 100 d’une longueur de 23 (autrement dit qui contient 23 éléments)
V2 <- seq (0,100, length = 23)
V2
##  [1]   0.000000   4.545455   9.090909  13.636364  18.181818  22.727273
##  [7]  27.272727  31.818182  36.363636  40.909091  45.454545  50.000000
## [13]  54.545455  59.090909  63.636364  68.181818  72.727273  77.272727
## [19]  81.818182  86.363636  90.909091  95.454545 100.000000
  • Créer un vecteur v3 qui contient les chaînes : “1ère année”, “2ème année”, “Master”
v3 = c("1ère année", "2ème année", "Master")
v3
## [1] "1ère année" "2ème année" "Master"
  • Créer un vecteur v4 qui répéte trois fois la chaîne “promo”
v4 = rep("promo", 3)
v4
## [1] "promo" "promo" "promo"
  • Regrouper les vecteurs v3 et v4, pour avoir le vecteur v5 suivant : “promo=1ère année” “promo=2ème année” “promo=Master”
v5 = paste (v4, " = ", v3 )
v5
## [1] "promo  =  1ère année" "promo  =  2ème année" "promo  =  Master"
  • Améliorer le code suivant pour avoir le vecteur v6 suivant : “promo=‘1ère année’” “promo=‘2ème année’” “promo=‘Master’”
v6 = paste(v4, paste0("'", v3, "'"), sep= "=")
v6
## [1] "promo='1ère année'" "promo='2ème année'" "promo='Master'"
  • Créer automatiquement le vecteur v7 qui suit : “x1” “y1” “x2” “y2” “x3” “y3” “x4” “y4” à l’aide des fonctions c(), seq(), paste(), rep()
v7 = paste0(c("x", "y"), rep(1:4, each = 2))
v7
## [1] "x1" "y1" "x2" "y2" "x3" "y3" "x4" "y4"
  • Créer un vecteur contenant les codes des départements français
champ_fr = sapply(c(01:19,21:95), function(x) paste0(paste(rep(0,2-nchar(x)), collapse = ""),x))
champ_fr = c(champ_fr,"2A","2B")
champ_fr
##  [1] "01" "02" "03" "04" "05" "06" "07" "08" "09" "10" "11" "12" "13" "14" "15"
## [16] "16" "17" "18" "19" "21" "22" "23" "24" "25" "26" "27" "28" "29" "30" "31"
## [31] "32" "33" "34" "35" "36" "37" "38" "39" "40" "41" "42" "43" "44" "45" "46"
## [46] "47" "48" "49" "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" "60" "61"
## [61] "62" "63" "64" "65" "66" "67" "68" "69" "70" "71" "72" "73" "74" "75" "76"
## [76] "77" "78" "79" "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" "90" "91"
## [91] "92" "93" "94" "95" "2A" "2B"
champ_fr2 = c(paste0("0", 1:9), 10:19, 21:95,"2A","2B")
champ_fr2
##  [1] "01" "02" "03" "04" "05" "06" "07" "08" "09" "10" "11" "12" "13" "14" "15"
## [16] "16" "17" "18" "19" "21" "22" "23" "24" "25" "26" "27" "28" "29" "30" "31"
## [31] "32" "33" "34" "35" "36" "37" "38" "39" "40" "41" "42" "43" "44" "45" "46"
## [46] "47" "48" "49" "50" "51" "52" "53" "54" "55" "56" "57" "58" "59" "60" "61"
## [61] "62" "63" "64" "65" "66" "67" "68" "69" "70" "71" "72" "73" "74" "75" "76"
## [76] "77" "78" "79" "80" "81" "82" "83" "84" "85" "86" "87" "88" "89" "90" "91"
## [91] "92" "93" "94" "95" "2A" "2B"
setdiff(champ_fr, champ_fr2)
## character(0)

Les deux blocs de code produisent le même résultat, qui est la création d’un vecteur contenant les codes des départements français. Cependant, la différence principale réside dans la manière dont ces codes sont générés.

Dans le premier bloc de code, une fonction anonyme est utilisée avec la fonction sapply pour itérer à travers une séquence de nombres de département allant de 1 à 95, à l’exception des départements 20 et 96. À chaque itération, le numéro du département est converti en chaîne de caractères, puis préfixé de zéros si nécessaire pour garantir que chaque numéro de département est représenté par deux chiffres. Les départements 2A et 2B sont ensuite ajoutés à la fin du vecteur. Ce code utilise donc une approche fonctionnelle pour générer les codes de département.

Dans le deuxième bloc de code, les codes de département sont générés directement en utilisant une combinaison de vecteurs, de nombres et de chaînes de caractères. Les nombres de 1 à 9 sont d’abord convertis en chaînes de caractères et préfixés de zéros. Ensuite, les nombres de 10 à 19 et de 21 à 95 sont ajoutés au vecteur, suivis des codes spéciaux 2A et 2B. Cette approche est plus directe et utilise simplement la concaténation de vecteurs pour créer le vecteur final des codes de département.

2 Manipulation de data.frame

2.0.1 L’exemple des naissances en France

Les données sur les naissances en France sont un choix intéressant à plusieurs égards, d’une part car elles offrent un aperçu précieux des enjeux contemporains liés à la fécondité et aux politiques natalistes. En tant que l’un des rares pays occidentaux à maintenir une politique nataliste active, la France représente un cas d’étude singulier pour comprendre les dynamiques de la fécondité et les politiques publiques associées. D’autre part, les données sur les naissances offrent la possibilité d’explorer les questions de genre dans le contexte de la reproduction, ainsi que d’évaluer l’efficacité des politiques publiques en matière de famille et de fécondité. En résumé, l’utilisation de ces données nous permettra d’approfondir notre compréhension des défis démographiques contemporains et de leurs ramifications sociétales, politiques et économiques.

2.1 Importation d’un jeu de données externe

library(foreign) |> suppressMessages()

naiss <- read.dbf("D:/TD_R_MasterCESSA/TD_R/nais2017.dbf", 
                  as.is = T)

La fonction rio::import Le package rio est un package “chapeau” qui présente une interface unifiée et permet d’importer et d’exporter différents types de fichiers. Il englobe tout une série de packages spécialisés chacun dans certains type de fichiers (ex. haven pour les fichiers SAS, readODS pour les .ods, etc.) et il se guide sur le suffixe pour choisir le meilleur package et la fonction de lecture ad hoc.

Il n’y a qu’une seule fonction à retenir: import. c’est le couteau suisse des outils de lecture de fichier.

Il peut y avoir des paramètre spécifique à ajouter, lesquels dépendent du type de fichier à importer, par exemple : - encoding = ‘UTF8’ : pour passer l’encodage au standard d’europe occidentale, - which : le nom d’une feuille Excel, - sep : le séparateur de champs d’un csv, - strings_as_factors = F pour un .csv et as.is = T pour un .dbf, - col_select : les colonnes à lire d’une table SAS, - n_max : nombre de lignes à lire, - skip : nombre de lignes à ignorer dans une feuille excel, etc.

library(DT) |> suppressWarnings()
library(htmltools) |> suppressWarnings()
datatable(head(naiss, 10),
          options = list(
              initComplete = JS(
                    "function(settings, json) {",
                    "$(this.api().table().header()).css({'font-size': '11px', 'background-color': '#94214D', 'color': '#fff'});",
                    "$(this.api().tables().body()).css({'font-size': '13px'});",
                    "}"
                  )
              ))
str(naiss)
## 'data.frame':    769553 obs. of  32 variables:
##  $ sexe    : chr  "2" "1" "2" "2" ...
##  $ anais   : chr  "2017" "2017" "2017" "2017" ...
##  $ mnais   : chr  "01" "03" "12" "09" ...
##  $ depnais : chr  "01" "01" "02" "02" ...
##  $ jrecp   : chr  "00" "00" "00" "00" ...
##  $ mrecp   : chr  "00" "00" "00" "00" ...
##  $ arecp   : chr  "0000" "0000" "0000" "0000" ...
##  $ jrecm   : chr  "00" "00" "00" "00" ...
##  $ mrecm   : chr  "00" "00" "00" "00" ...
##  $ arecm   : chr  "0000" "0000" "0000" "0000" ...
##  $ jrecc   : chr  "00" "14" "00" "00" ...
##  $ mrecc   : chr  "00" "02" "00" "00" ...
##  $ arecc   : chr  "0000" "2017" "0000" "0000" ...
##  $ agemere : chr  "34" "35" "34" "33" ...
##  $ agexactm: chr  "33" "34" "34" "33" ...
##  $ indlnm  : chr  "4" "4" "4" "4" ...
##  $ situatmr: chr  "S" "S" "S" "S" ...
##  $ indnatm : chr  "2" "2" "2" "2" ...
##  $ depdom  : chr  "99" "99" "99" "99" ...
##  $ tudom   : chr  "9" "9" "9" "9" ...
##  $ tucom   : chr  NA NA NA NA ...
##  $ agepere : chr  "41" "45" "33" "33" ...
##  $ agexactp: chr  "40" "44" "33" "32" ...
##  $ indlnp  : chr  "4" "1" "4" "4" ...
##  $ situatpr: chr  "S" "S" "S" "S" ...
##  $ indnatp : chr  "2" "1" "2" "2" ...
##  $ amar    : chr  "2014" "0000" "2011" "2014" ...
##  $ dmarnais: chr  "1" NA "2" "1" ...
##  $ accouchr: chr  "ES" "ES" "ES" "ES" ...
##  $ nbenf   : chr  "1" "1" "1" "1" ...
##  $ durecevp: chr  "02" NA "02" "03" ...
##  $ originom: chr  "1" "1" "1" "1" ...
##  - attr(*, "data_types")= chr [1:32] "C" "C" "C" "C" ...

2.2 Explorer le jeu de données

L’onglet “Environnement” donne de nombreuses informations sur les dataframes chargées en mémoire, dont la plupart peuvent également être récupérées via des fonctions : class() ; str() ; attributes() ; summary() ; dim() ; nrow() ; ncol() ; length() ; names() ; colnames() ; rownames()

Les tables de contingence sur des variables : count() ; table() ; prop.table()

table(naiss$agemere)

Visualiser tout ou partie des données : View() ; head() ; tail() ; slice() ; pull()

Modifier les données avec la fonction mutate(). La fonction mutate() permet de créer ou de modifier une variable (colonne) dans la table.

library(dplyr) |> suppressWarnings()
## 
## Attachement du package : 'dplyr'
## Les objets suivants sont masqués depuis 'package:stats':
## 
##     filter, lag
## Les objets suivants sont masqués depuis 'package:base':
## 
##     intersect, setdiff, setequal, union
#Créer une nouvelle colonne à partir d’une formule de calcul pouvant mobiliser les autres colonnes de la table
naiss <- naiss |> 
  mutate(agemere2=as.integer(agemere))
#Modifier une variable en réutilisant le même nom
naiss <- naiss |> 
  mutate(sexe=factor(sexe, levels=c("2","1"), labels=c('Féminin','Masculin')))

Recoder une variable : if_else() ou case_when(). Le recodage d’une colonne en deux modalités se fera par création d’une nouvelle colonne dont les valeurs pourront être construites avec la fonction ifelse(). Pour recoder vers plus de deux modalités on pourra utiliser case_when().

naiss |> 
  mutate(sexe = ifelse(sexe=='1', "garçon", "fille"))


naiss |> 
  mutate(sexe = case_when((sexe == '1') & (nbenf > '1') ~ "jumeau",
                          (sexe == '2') & (nbenf > '1') ~ "jumelle",
                          sexe == '1' ~ "garçon",
                          TRUE ~ "fille"))

Les conditions peuvent être complexes et utiliser plusieurs colonnes de la table. Les conditions sont explorées successivement jusqu’à ce qu’une d’entre elles donne TRUE, elles ne sont donc pas nécessairement exclusives l’une de l’autre. La dernière ligne « voiture balai » (qui n’est pas obligatoire) exploite cette fonctionnalité avec une condition toujours vraie qui piège tous les cas non explorés.

naiss <- naiss  |> 
  mutate(tagemere=case_when(as.numeric(agemere)<20 ~ "1. moins de 20 ans",
                            as.numeric(agemere)<25 ~ "2. de 20 à moins de 25 ans",
                            as.numeric(agemere)<35 ~ "3. de 25 à moins de 35 ans",
                            TRUE ~ "4. 35 ans et plus"))

naiss  |> count(tagemere)
##                     tagemere      n
## 1         1. moins de 20 ans  12077
## 2 2. de 20 à moins de 25 ans  82432
## 3 3. de 25 à moins de 35 ans 484062
## 4          4. 35 ans et plus 190982

En outre :

naiss |> 
  mutate(agemere2 = cut(as.numeric(agemere),c(15,20,25,35,45)))

2.3 Les fonctions statistiques de base

Puisque la fécondité d’une année est présentée sous la forme d’une distribution dépendante de l’âge, ce qui signifie que le nombre de naissances varie selon l’âge des femmes. On peut alors la caractériser avec des statistiques appropriées à des distributions : la moyenne, l’écart type (ou la variance), le mode, la médiane et les quartiles et avec des indicateurs de la dissymétrie et d’aplatissement.

naiss$agemere = as.numeric(naiss$agemere)
naiss |> mutate(agemere = as.numeric(naiss$agemere)) |>
  summarize(nb_naiss = n(),
                        q1_mere = quantile(as.numeric(agemere), 0.25),
                        med_mere = median(as.numeric(agemere)),
                        q3_mere = quantile(as.numeric(agemere), 0.75),
                        min_mere = min(as.numeric(agemere)),
                        max_mere = max(as.numeric(agemere))) 
##   nb_naiss q1_mere med_mere q3_mere min_mere max_mere
## 1   769553      27       31      34       17       46

Ces sorties R proviennent d’une opération de résumé effectuée sur un jeu de données nommé “naissances”. Voici comment interpréter chaque mesure résumée :

  • nb_naiss: Ce nombre représente le nombre total de naissances dans le jeu de données.

  • q1_mere: Cette valeur correspond au premier quartile de l’âge des mères dans le jeu de données. En d’autres termes, 25% des âges des mères sont inférieurs ou égaux à cette valeur.

  • med_mere: C’est la médiane de l’âge des mères dans le jeu de données. La médiane divise les données en deux parties égales, de sorte que la moitié des âges des mères sont inférieurs ou égaux à cette valeur et l’autre moitié sont supérieurs ou égaux.

  • q3_mere: Il s’agit du troisième quartile de l’âge des mères dans le jeu de données. Ainsi, 75% des âges des mères sont inférieurs ou égaux à cette valeur.

  • min_mere: C’est l’âge minimal des mères dans le jeu de données.

  • max_mere: C’est l’âge maximal des mères dans le jeu de données.

En résumé, ces sorties fournissent des informations statistiques sur l’âge des mères au moment des naissances dans le jeu de données “naissances”, notamment en indiquant des mesures de tendance centrale (médiane), de dispersion (quartiles, minimum et maximum) et le nombre total de naissances.

Avec le package tidyverse, conçu pour permettre le travail harmonieux entre différents packages en suivant les principes de la philosophie “tidy data” (données bien rangées), on dispose d’un ensemble d’outils de référence. Grâce à l’opérateur pipe %>%, il est possible d’enchainer facilement les fonctions et de tirer parti des fonctionnalités clés du package dplyr, inclus dans le tidyverse (une fois le package tidyverse chargé, les packages dplyr et ggplot2 sont automatiquement chargés). La fonction across() permet d’appliquer une opération à plusieurs colonnes d’un data frame de manière simultanée, ce qui est extrêmement utile lors de la manipulation de données. Voici quelques points saillants concernant la fonction across() :

  • Opérations sur plusieurs colonnes : avec across(), vous pouvez spécifier plusieurs colonnes sur lesquelles vous souhaitez appliquer une opération. Cela vous évite de répéter la même opération pour chaque colonne individuellement, ce qui rend votre code plus concis et plus facile à maintenir.
naiss |> 
  mutate(across(c(agemere,agepere),as.numeric)) |>   
  summarize(across(where(is.numeric),list(Minimum=min,Maximum=max,Moyenne=mean)))
##   agemere_Minimum agemere_Maximum agemere_Moyenne agepere_Minimum
## 1              17              46         30.7817              17
##   agepere_Maximum agepere_Moyenne
## 1              46         33.6721
  • Flexibilité dans l’application des opérations : across() peut être utilisé avec une variété d’opérations, telles que la transformation de données, le filtrage, ou le calcul de statistiques. Vous pouvez utiliser des fonctions existantes ou définir vos propres fonctions personnalisées.

  • Compatibilité avec les verbes mutate(), summarize(), filter(), etc. : across() peut être utilisé avec les verbes mutate(), summarize(), filter(), et d’autres verbes de manipulation de données de dplyr. Cela permet une intégration transparente dans votre flux de travail d’analyse de données.

  • Contrôle précis des colonnes : Vous pouvez spécifier quelles colonnes vous souhaitez inclure ou exclure de l’opération en utilisant des sélecteurs de colonnes familiers de dplyr, tels que starts_with(), ends_with(), ou contains().

En résumé, la fonction across() est un outil puissant pour travailler efficacement avec plusieurs colonnes de données simultanément dans le tidyverse, offrant à la fois flexibilité et contrôle précis sur les opérations appliquées.

2.4 Restriction et projection

La fonction de base subset() permet d’effectuer les deux opérations courantes en manipulation de données : la restriction et la projection. Dans le paramètre subset, on spécifie la condition (simple ou combinée) qui définit la restriction des observations. Quant au paramètre select, il permet de spécifier les variables à inclure dans la projection. Cependant, malgré sa polyvalence, cette fonction présente des limites en termes de lisibilité et de convivialité syntaxique.

Une alternative plus moderne et plus expressive se trouve dans l’utilisation de la syntaxe du tidyverse, où l’opérateur pipe %>% permet de chaîner facilement les opérations. Par exemple, pour filtrer les données de naissances en ne conservant que les observations où l’âge est inférieur à 18 ans, puis compter le nombre d’observations restantes, on peut utiliser la syntaxe suivante :

naiss |> 
  filter(if_all(starts_with("age"),~.>15))|>    
  count()
##        n
## 1 769553

Dans cet exemple, la fonction filter() est utilisée pour restreindre les données selon une condition spécifique, tandis que count() permet de compter les observations restantes. L’utilisation de if_all() permet d’appliquer la condition à toutes les variables commençant par “age”, offrant ainsi une approche plus concise et lisible pour réaliser cette tâche.

En extrayant les observations correspondantes de la table naissances :

Combien y-a-t-il de naissances ?

– d’un département de votre choix (‘depnais’), ET – dont la mère a plus de 40 ans (‘agemere’ - qui est codé en caractères), ET – hors mariage (année de mariage ‘amar’ = “0000”), ET – de filles (‘sexe’ = “2”)?

Sous les trois dernières mêmes conditions, combien y-a-t-il de naissances sur l’ensemble des deux départements de Charente (16) et du Cher (18)?

naiss |>  
  filter(depnais=='75',
         as.numeric(agemere)>40,
         amar=='0000',
         sexe=='2') |>  
  summarize(nb_naissances=n())
##   nb_naissances
## 1           672
naiss |>  
  filter(depnais %in% c('16','18'),
         as.numeric(agemere)>40,
         amar=='0000',
         sexe=='2') |>  
  summarize(nb_naissances=n())
##   nb_naissances
## 1            31
dep.naiss = naiss |>  
  filter(as.numeric(agemere)>40,
         amar=='0000',
         sexe=='2') |>  
  group_by(depnais) |>  
  summarize(nb_naissances=n())

2.5 Les statistiques par modalité

La fonction group_by() de dplyr permet de réaliser des statistiques par modalités, ou sous population, sans avoir à partitionner la table.

naiss |> 
  mutate(agemere=as.integer(agemere)) |> 
  group_by(indnatm) |> 
  summarize(moyenne=mean(agemere), et=sd(agemere))
## # A tibble: 2 × 3
##   indnatm moyenne    et
##   <chr>     <dbl> <dbl>
## 1 1          30.8  5.25
## 2 2          30.9  5.80

La fonction ajoute un « index » à la table initiale indiquant les observations appartenant à chaque modalité. La présence de cet index contraint certaines fonctions de dplyr à travailler par morceau.

On peut utiliser naturellement plusieurs variables dans le group_by(), voire des expressions.

naiss |> 
  group_by(indnatm, indnatp) |>
  summarize(n=n())
## `summarise()` has grouped output by 'indnatm'. You can override using the
## `.groups` argument.
## # A tibble: 4 × 3
## # Groups:   indnatm [2]
##   indnatm indnatp      n
##   <chr>   <chr>    <int>
## 1 1       1       580453
## 2 1       2        53466
## 3 2       1        59712
## 4 2       2        75922
naiss |> 
  group_by(substring(agemere,1,1)) |> 
  summarize(n=n())
## # A tibble: 4 × 2
##   `substring(agemere, 1, 1)`      n
##   <chr>                       <int>
## 1 1                           12077
## 2 2                          302336
## 3 3                          413674
## 4 4                           41466

Les groupes sont conservés avec la plupart des opérations. La présence de l’index de groupe influe donc sur les calculs ultérieurs. La fonction ungroup() permet d’éliminer tout index si la suite des calculs doit concerner la table entière.

À partir de la table “naissances” :

  • Calculer le nombre de naissances et l’âge moyen de la mère par département de naissance depnais.

  • Quel est le département qui a le plus de naissances ?

  • Quel est celui qui a la plus forte proportion de filles ?

dep.nais = naiss |> 
  group_by(depnais) |> 
  summarize(nb_naissances=n(),
            moyenne_agemere=mean(as.numeric(agemere))) 
naiss |> 
  group_by(depnais) |> 
  summarize(nb_naissances=n(),
            moyenne_agemere=mean(as.numeric(agemere))) |> 
  filter(nb_naissances==max(nb_naissances))
## # A tibble: 1 × 3
##   depnais nb_naissances moyenne_agemere
##   <chr>           <int>           <dbl>
## 1 75              42096            32.9
naiss |> 
  group_by(depnais) |> 
  summarize(nb_naissances=n(),
            nb_filles=sum(sexe=='2'),
            moyenne_agemere=mean(as.numeric(agemere))) |> 
  mutate(part_filles=nb_filles/nb_naissances) |> 
  arrange(desc(part_filles)) |> 
  head(1)
## # A tibble: 1 × 5
##   depnais nb_naissances nb_filles moyenne_agemere part_filles
##   <chr>           <int>     <int>           <dbl>       <dbl>
## 1 2B               1666       862            31.0       0.517

2.6 Les graphiques

2.6.1 Qu’est-ce que ggplot2 ?

– ggplot2 est un package R qui permet de créer des graphiques

– ggplot2 est basé sur la grammaire des graphiques, ce qui permet une approche cohérente et intuitive pour la création de graphiques.

– les graphiques ggplot2 sont construits en ajoutant des couches (layers) les unes sur les autres, ce qui permet une personnalisation facile et une grande flexibilité.

Exemple de code :

library(ggplot2)
## Warning: le package 'ggplot2' a été compilé avec la version R 4.2.3
ggplot(data = iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point()

Dans cet exemple, nous utilisons les données iris pour créer un nuage de points où la longueur des sépales est représentée sur l’axe des x, la largeur des sépales sur l’axe des y, et la couleur des points représente les différentes espèces de fleurs.