Chapitre 4 S’exprimer dans R

4.1 Introduction

4.1.1 Objectif

Dans le chapitre précédent, il a été question d’objets dans R. Certains types ont été présentés. Il a surtout été fait état de la différence qui les sépare, de ce en quoi ils se démarquent les uns des autres. Ici, nous allons continuer en explorant l’expression dans R.

Les objets permettent de stocker des données. Celles-ci ne deviennent vivantes et parlantes qu’à travers le dialogue que le data scientist entretient avec elles. Et en quels termes ce dialogue se pose-t-il? Là est le début de notre démarche ici.

Nous allons:

  • revenir sur les questions logiques;

  • introduire déclarations conditionnelles;

  • introduire la notion de boucle et de fonction.

4.1.2 Outils

Que nous faut-il?

  • R (évidemment);

  • RStudio (de préférence);

  • Les données utilisées dans le cadre du présent chapitre.

4.1.3 Données

Dans le présent chapitre, nous allons utiliser des données tirées des Recensements Généraux de la Population et de l’Habitat au Mali en 1976, 1987, 1998 et 2009. Des rapports sont disponibles cette adresse.

Quant aux données extraites et formatées pour le présent cours, elles sont disponibles à cette adresse.

Nous

Balayons du regard les objets qui meublent notre environnement.

ls()
## [1] "pop_groupage_list"

Regardons la structure de cet objet.

str(pop_groupage_list)
## List of 4
##  $ 1976:'data.frame':    18 obs. of  5 variables:
##   ..$ annee   : num [1:18] 1976 1976 1976 1976 1976 ...
##   ..$ groupage: Ord.factor w/ 18 levels "0-4"<"5-9"<"10-14"<..: 1 2 3 4 5 6 7 8 9 10 ...
##   ..$ femme   : num [1:18] 589394 482851 321959 333508 265842 ...
##   ..$ homme   : num [1:18] 587015 492272 342807 308607 218391 ...
##   ..$ total   : num [1:18] 1176409 975123 664766 642115 484233 ...
##  $ 1987:'data.frame':    18 obs. of  5 variables:
##   ..$ annee   : num [1:18] 1987 1987 1987 1987 1987 ...
##   ..$ groupage: Ord.factor w/ 18 levels "0-4"<"5-9"<"10-14"<..: 1 2 3 4 5 6 7 8 9 10 ...
##   ..$ femme   : num [1:18] 713507 611562 414302 379522 315753 ...
##   ..$ homme   : num [1:18] 719804 633206 452166 348200 260215 ...
##   ..$ total   : num [1:18] 1433311 1244768 866468 727722 575968 ...
##  $ 1998:'data.frame':    18 obs. of  5 variables:
##   ..$ annee   : num [1:18] 1998 1998 1998 1998 1998 ...
##   ..$ groupage: Ord.factor w/ 18 levels "0-4"<"5-9"<"10-14"<..: 1 2 3 4 5 6 7 8 9 10 ...
##   ..$ femme   : num [1:18] 824505 797057 589603 529270 409584 ...
##   ..$ homme   : num [1:18] 839795 830211 637495 492480 364333 ...
##   ..$ total   : num [1:18] 1664300 1627268 1227098 1021750 773917 ...
##  $ 2009:'data.frame':    18 obs. of  5 variables:
##   ..$ annee   : num [1:18] 2009 2009 2009 2009 2009 ...
##   ..$ groupage: Ord.factor w/ 18 levels "0-4"<"5-9"<"10-14"<..: 1 2 3 4 5 6 7 8 9 10 ...
##   ..$ femme   : num [1:18] 1321275 1178850 882725 799081 624565 ...
##   ..$ homme   : num [1:18] 1353418 1225145 935796 745757 538927 ...
##   ..$ total   : num [1:18] 2674693 2403995 1818521 1544838 1163492 ...

Il s’agit d’une liste. Les données portent sur la population par groupe d’âge.

4.2 Les déclarations

Nous allons présenter ici la notion de déclaration et l’illustrer à partir de nos données. Qu’est-ce qu’une déclaration? Tout simplement une affirmation que l’on formule et que l’on soumet à la machine… Pour être plus exact, nous soumettons la déclaration aux données et regardons leur réaction!

4.2.1 Formulations simples

Affirmons qu’au Mali, pour les groupes d’âge identifiés, il y a plus de femmes que d’hommes. Est-ce vrai ou faux? Qu’en disent nos données? Pour faire simple, prenons le recensement le plus récent, celui de 2009, pour vérifier la véracité de notre déclaration.

Pour commencer, tirons de la liste les données relative à l’année d’intérêt.

pop_groupage_2009 <- pop_groupage_list[["2009"]]

Maintenant, regardons la structure de ce data frame.

str(pop_groupage_2009)
## 'data.frame':    18 obs. of  5 variables:
##  $ annee   : num  2009 2009 2009 2009 2009 ...
##  $ groupage: Ord.factor w/ 18 levels "0-4"<"5-9"<"10-14"<..: 1 2 3 4 5 6 7 8 9 10 ...
##  $ femme   : num  1321275 1178850 882725 799081 624565 ...
##  $ homme   : num  1353418 1225145 935796 745757 538927 ...
##  $ total   : num  2674693 2403995 1818521 1544838 1163492 ...

Regardons la tête, les 3 premières observations par exemple.

head(x = pop_groupage_2009, n = 3)
##    annee groupage   femme   homme   total
## 55  2009      0-4 1321275 1353418 2674693
## 56  2009      5-9 1178850 1225145 2403995
## 57  2009    10-14  882725  935796 1818521

Regardons la queue, les 3 dernières observations par exemple.

tail(x = pop_groupage_2009, n = 3)
##    annee groupage femme homme total
## 70  2009    75-79 36949 41667 78616
## 71  2009      80+ 44504 42779 87283
## 72  2009       ND     0     0     0

Nous voyons qu’il y a une colonne pour les hommes homme et une autre pour les femmes, femme. Tirons du data frame les vecteurs relatifs à ces deux groupes.

pop_femme_2009 <- pop_groupage_2009$femme
pop_homme_2009 <- pop_groupage_2009$homme

Maintenant posons la condition suivante: pop_femme_2009 > pop_homme_2009.

pop_femme_2009 > pop_homme_2009
##  [1] FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
## [12] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE

Pour une meilleur lisibilité, insérons ce résultat dans le data frame

pop_groupage_2009$femme_sup_homme <- pop_groupage_2009$femme > pop_groupage_2009$homme

En assignant le résultat de l’opération à une nouvelle variable du data frame, R crée lui-même une variable boléenne (TRUE/FALSE). Regardons les groupes d’âge qui répondent au critère posé.

pop_groupage_2009[pop_groupage_2009$femme_sup_homme, ]
##    annee groupage  femme  homme   total femme_sup_homme
## 58  2009    15-19 799081 745757 1544838            TRUE
## 59  2009    20-24 624565 538927 1163492            TRUE
## 60  2009    25-29 557627 457139 1014766            TRUE
## 61  2009    30-34 436501 391919  828420            TRUE
## 62  2009    35-39 333542 330907  664449            TRUE
## 63  2009    40-44 281004 276149  557153            TRUE
## 65  2009    50-54 196356 192875  389231            TRUE
## 71  2009      80+  44504  42779   87283            TRUE

Le même résultat s’obtient avec la fonction subset.

subset(x = pop_groupage_2009, subset = femme_sup_homme == TRUE)
##    annee groupage  femme  homme   total femme_sup_homme
## 58  2009    15-19 799081 745757 1544838            TRUE
## 59  2009    20-24 624565 538927 1163492            TRUE
## 60  2009    25-29 557627 457139 1014766            TRUE
## 61  2009    30-34 436501 391919  828420            TRUE
## 62  2009    35-39 333542 330907  664449            TRUE
## 63  2009    40-44 281004 276149  557153            TRUE
## 65  2009    50-54 196356 192875  389231            TRUE
## 71  2009      80+  44504  42779   87283            TRUE

L’on peut utiliser directement introduire le critère à l’intérieur du data frame

pop_groupage_2009[pop_groupage_2009$femme > pop_groupage_2009$homme, ]
##    annee groupage  femme  homme   total femme_sup_homme
## 58  2009    15-19 799081 745757 1544838            TRUE
## 59  2009    20-24 624565 538927 1163492            TRUE
## 60  2009    25-29 557627 457139 1014766            TRUE
## 61  2009    30-34 436501 391919  828420            TRUE
## 62  2009    35-39 333542 330907  664449            TRUE
## 63  2009    40-44 281004 276149  557153            TRUE
## 65  2009    50-54 196356 192875  389231            TRUE
## 71  2009      80+  44504  42779   87283            TRUE

…ou à l’intérieur de la fonctio subset.

subset(x = pop_groupage_2009, subset = femme > homme)
##    annee groupage  femme  homme   total femme_sup_homme
## 58  2009    15-19 799081 745757 1544838            TRUE
## 59  2009    20-24 624565 538927 1163492            TRUE
## 60  2009    25-29 557627 457139 1014766            TRUE
## 61  2009    30-34 436501 391919  828420            TRUE
## 62  2009    35-39 333542 330907  664449            TRUE
## 63  2009    40-44 281004 276149  557153            TRUE
## 65  2009    50-54 196356 192875  389231            TRUE
## 71  2009      80+  44504  42779   87283            TRUE

Cette dernière approche se révèle simple. A partir de maintenant, nous allons privilégier la fonction subset.

Sur la base de ces résultats, on voit clairement que R sait comparer des valeurs numériques. Juste pour confirmer, reprenons sur le groupe d’âge 0-4 ans.

1353418 < 1321275
## [1] FALSE

Qu’en est-il des rééls?

1.000002 > 1 
## [1] TRUE
# (Notez que l'assignation ses fait avec "=", mais le test d'égalité se fait avec "==")

De toute évidence, ça marche avec les nombres. Qu’en est-il des caractères? Testons!

"MALI" == "Mali"
## [1] FALSE

Cette égalité est rejetée par que R est sensible à la taille des lettres (majuscule/minuscule). Maintenant regardons la logique.

TRUE == 1
## [1] TRUE

Vous rappelez-vous quand, dans le cours précédent, nous avons coercé une vecteur de valeurs logiques en y ajoutant un réel comment TRUE est devenu 1 et FALSE 0? Et bien, c’est la preuve que pour R, TRUE == 1.

4.2.2 Critères additifs: et = &

Il est souvent possible que l’on souhaite combiner plusieurs critères dans la même déclaration. Supposons que l’on veuille connaître les groupes d’âge pour lesquels:

  • les femmes sont plus nombreuses que les hommes; et

  • la population totale (hommes + femmes) est en dessous de 1 millions de personnes.

Nous commençons par définir nos critères.

# femme > homme
pop_groupage_2009$femme_sup_homme <- pop_groupage_2009$femme > pop_groupage_2009$homme
# total > 1000000
pop_groupage_2009$moins_de_1_million <- pop_groupage_2009$total < 1000000

Maintenant, combinons les!

## subset(x = pop_groupage_2009, subset = femme_sup_homme & moins_de_1_million)

Avec l’insertion directe des résultats, l’on obtient la même chose.

subset(x = pop_groupage_2009, subset = femme > homme & total < 1000000)
##    annee groupage  femme  homme  total femme_sup_homme moins_de_1_million
## 61  2009    30-34 436501 391919 828420            TRUE               TRUE
## 62  2009    35-39 333542 330907 664449            TRUE               TRUE
## 63  2009    40-44 281004 276149 557153            TRUE               TRUE
## 65  2009    50-54 196356 192875 389231            TRUE               TRUE
## 71  2009      80+  44504  42779  87283            TRUE               TRUE

L’addition de critères se fait avec l’opérateur &. Le résultat donne les observations qui répondent à toutes les conditions posées.

4.2.3 Critères alternatifs: ou = |

La combinaison de critères dans une déclaration ne se pose pas toujours sous la forme additive. Il arrive qu’on veuille procéder sur la base de: soit…soit… Dans ce cas, il faut une autre expression.

Cherchons par exemple, à connaître les groupe pour lequels:

  • soit les femmes sont plus nombreuses que les hommes;

  • soit la population totale (hommes + femmes) est en dessous de 1 millions de personnes.

Au lieu du signe &, nous utilisons le signe |

subset(x = pop_groupage_2009, subset = femme > homme | total < 1000000)
##    annee groupage  femme  homme   total femme_sup_homme moins_de_1_million
## 58  2009    15-19 799081 745757 1544838            TRUE              FALSE
## 59  2009    20-24 624565 538927 1163492            TRUE              FALSE
## 60  2009    25-29 557627 457139 1014766            TRUE              FALSE
## 61  2009    30-34 436501 391919  828420            TRUE               TRUE
## 62  2009    35-39 333542 330907  664449            TRUE               TRUE
## 63  2009    40-44 281004 276149  557153            TRUE               TRUE
## 64  2009    45-49 221709 232779  454488           FALSE               TRUE
## 65  2009    50-54 196356 192875  389231            TRUE               TRUE
## 66  2009    55-59 136852 151319  288171           FALSE               TRUE
## 67  2009    60-64 126022 129916  255938           FALSE               TRUE
## 68  2009    65-69  78677  89929  168606           FALSE               TRUE
## 69  2009    70-74  67433  68569  136002           FALSE               TRUE
## 70  2009    75-79  36949  41667   78616           FALSE               TRUE
## 71  2009      80+  44504  42779   87283            TRUE               TRUE
## 72  2009       ND      0      0       0           FALSE               TRUE

Ici, la validation de l’une des conditions suffit. On voit des groupes au dessus de 1 million de personnes (violation du critère n°2). Toutefois, les femmes y sont plus nombreuses (validation du critère n°1). A l’inverse, certains groupes ont moins de femmes (violation du critère n°1), mais comptent moins d’1 millions de personnes (validation du critère n°2).

Souvent, il arrive qu’on veuille accumuler des critères à l’intérieur d’une seule variable. Supposons que l’on souhaite voir les informations concernant juste les moins de 15 ans. On sait que, dans ce cas, on aura à sélectionner trois groupes d’âge: 0-4, 5-9, et 10-14. La variable groupage doit être égale à l’une de ses valeurs. Reprenons la logique des critères alternatifs (soit…soit…).

subset(x = pop_groupage_2009, subset = groupage == "0-4" | groupage == "5-9" | groupage == "10-14")
##    annee groupage   femme   homme   total femme_sup_homme
## 55  2009      0-4 1321275 1353418 2674693           FALSE
## 56  2009      5-9 1178850 1225145 2403995           FALSE
## 57  2009    10-14  882725  935796 1818521           FALSE
##    moins_de_1_million
## 55              FALSE
## 56              FALSE
## 57              FALSE

Maintenant, ajoutons au critère de moins de 15 ans un autre, celui d’un total de moins de 2 millions, donc total < 2000000.

subset(x = pop_groupage_2009, subset = (groupage == "0-4" | groupage == "5-9" | groupage == "10-14") & (total < 2000000) )
##    annee groupage  femme  homme   total femme_sup_homme moins_de_1_million
## 57  2009    10-14 882725 935796 1818521           FALSE              FALSE

Il a suffit d’isoler les critères alternatifs entre parenthèses et d’y le critère additif.

4.2.4 Critères opposés: contraire = !

Souvent, il arrive que l’on souhaite sélectionner sur la base de l’opposition à un critère. Explorons à travers un exemple.

Plus haut, nous avons défini les groupes où femme > homme. Ceci revient à définir les groupes où la condition homme >= femme est violée. Voyons comment on part de la négation pour parvenir à ce même résultat.

Rappelons

subset(x = pop_groupage_2009, subset = femme > homme)
##    annee groupage  femme  homme   total femme_sup_homme moins_de_1_million
## 58  2009    15-19 799081 745757 1544838            TRUE              FALSE
## 59  2009    20-24 624565 538927 1163492            TRUE              FALSE
## 60  2009    25-29 557627 457139 1014766            TRUE              FALSE
## 61  2009    30-34 436501 391919  828420            TRUE               TRUE
## 62  2009    35-39 333542 330907  664449            TRUE               TRUE
## 63  2009    40-44 281004 276149  557153            TRUE               TRUE
## 65  2009    50-54 196356 192875  389231            TRUE               TRUE
## 71  2009      80+  44504  42779   87283            TRUE               TRUE

Passons maintenant par l’opposé.

subset(x = pop_groupage_2009, subset = !(femme <= homme))
##    annee groupage  femme  homme   total femme_sup_homme moins_de_1_million
## 58  2009    15-19 799081 745757 1544838            TRUE              FALSE
## 59  2009    20-24 624565 538927 1163492            TRUE              FALSE
## 60  2009    25-29 557627 457139 1014766            TRUE              FALSE
## 61  2009    30-34 436501 391919  828420            TRUE               TRUE
## 62  2009    35-39 333542 330907  664449            TRUE               TRUE
## 63  2009    40-44 281004 276149  557153            TRUE               TRUE
## 65  2009    50-54 196356 192875  389231            TRUE               TRUE
## 71  2009      80+  44504  42779   87283            TRUE               TRUE

En termes d’aperçu, nous avons le même résultat. Pour confirmer, sauvegardons les deux résultats sous forme de nouvelles variables dans le data frame, puis comparons-les.

pop_groupage_2009$femme_sup_homme <- pop_groupage_2009$femme > pop_groupage_2009$homme
pop_groupage_2009$homme_pas_sup_femme <- !(pop_groupage_2009$homme >= pop_groupage_2009$femme)
pop_groupage_2009$femme_sup_homme == pop_groupage_2009$homme_pas_sup_femme
##  [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [15] TRUE TRUE TRUE TRUE

Souvent le nombre d’observations est trop grand pour que l’on puisse inspecter à l’oeil le résultat de la déclaration pour toutes les observations. Il existe des fonctions qui permettent de conduire l’examen au niveau global. C’est le cas de la fonction identical.

identical(pop_groupage_2009$femme_sup_homme, pop_groupage_2009$homme_pas_sup_femme)
## [1] TRUE

Les deux vecteurs sont donc identiques. Les deux procédés mènent donc au même résultat.

La négation revèle toute son utilité quand on cherche à examiner les données sur la base de l’exclusion plutôt que celle de la sélection. Prenons un exemple dans notre cas. Supposons que nous souhaitions faire la somme des populations sans les enfants de moins de 5 ans. Dans ce cas, plutôt que de sélectionner les groupes qui sont au dessus de 5 ans, il s’avère plus commode d’exclure les moins de 5 ans.

# Les groupes homme, femme et total, avec l'exclusion de 0-4 ans
pop_groupage_2009_plus5ans <- subset(x = pop_groupage_2009, subset = groupage != "0-4", select = c(homme, femme, total))

Le résultat est la même chose que la ligne suivante.

# Les groupes homme, femme et total, avec l'exclusion de 0-4 ans
pop_groupage_2009_plus5ans <- subset(x = pop_groupage_2009, subset = !(groupage == "0-4"), select = c(homme, femme, total))

La preuve.

identical(subset(x = pop_groupage_2009, subset = groupage != "ND"),
          subset(x = pop_groupage_2009, subset = !(groupage == "ND")))
## [1] TRUE

Faisons les sommes pour la population restrainte.

# Vous rappelez-vous la fonction colSums du chapitre précédent?
colSums(pop_groupage_2009_plus5ans)
##    homme    femme    total 
##  5851572  6002397 11853969

Et maintenant, juste pour comparer, regardons sur la population globale.

# Les groupes homme, femme et total, sans aucun critère
pop_groupage_2009_avec5ans <- subset(x = pop_groupage_2009, select = c(homme, femme, total))
# Les sommmes
colSums(pop_groupage_2009_avec5ans)
##    homme    femme    total 
##  7204990  7323672 14528662

4.2.5 Conditionalités

Jusque là, nous avons parlé de déclarations dans une formulation simple. Nous les avons pas inscrites dans le cadre d’un arbre de décision. Il s’agit du schéma suivant: “si condition remplie, alors action 1, sinon action 2”. On délègue à la machine l’exécution de tâches sur la base de critères définis…ce qui est pratiquement le début de l’intelligence artificielle.

Dans notre example, nous avons vu qu’entre les hommes et les femmes, la supériorité numérique varie d’un groupe d’âge à un autre. Nous pouvons souhaiter générer une variable qui indiquera lequel des groupes est plus nombreux. Pour ce faire, R dispose de la fonction ifelse.

pop_groupage_2009$sup_num <- ifelse(# condition
                                    test = pop_groupage_2009$femme > pop_groupage_2009$homme, 
                                    # action si condition satisfaite
                                     yes = "femme > homme", 
                                    # action si condition non satisfaite
                                     no = "femme <= homme" 
                                     )

Regardons ce que celà donne.

head(pop_groupage_2009)
##    annee groupage   femme   homme   total        sup_num
## 55  2009      0-4 1321275 1353418 2674693 femme <= homme
## 56  2009      5-9 1178850 1225145 2403995 femme <= homme
## 57  2009    10-14  882725  935796 1818521 femme <= homme
## 58  2009    15-19  799081  745757 1544838  femme > homme
## 59  2009    20-24  624565  538927 1163492  femme > homme
## 60  2009    25-29  557627  457139 1014766  femme > homme

Avec cette nouvelle variable, nous pouvons déterminer, par exemple, le nombre de groupes pour lesquels il y a plus de femmes que d’hommes et vice-versa.

table(pop_groupage_2009$sup_num)
## 
## femme <= homme  femme > homme 
##             10              8

4.3 Les boucles

4.3.1 La solution aux tâches répétititives

Un grand avantage de la programmation est la capacité de déléguer à la machine l’exécution de tâches répétitives. R dispose de diverses fonctions qui permettent d’effectuer celles-ci en boucle. Ceci est très commode surtout quand le nombre de répétitions est élevé. Toutefois, la nécessité des boucles varie d’un objet à un autre. Si pour certains, des solutions alternatives et plus simples existent, pour d’autres, elles sont la meilleure option.

Dans ce chapitre, nous allons nous limiter à la fonction for. Vous pouvez regarder la fonction while. Entrez dans la console: help(“while”).

4.3.2 La fonction for

La fonction for est très pratique pour l’exécution des boucles dans R. Elle est structurée de la façon suivante:

for(var in seq){
  expr
}

var désigner une variable dans la séquence seq et expr la transformation à laquelle l’on soumet les éléments de cette dernière. Un exemple.

for(i in c(1:10)){
  print(i^2)
  }
## [1] 1
## [1] 4
## [1] 9
## [1] 16
## [1] 25
## [1] 36
## [1] 49
## [1] 64
## [1] 81
## [1] 100

Pour chaque i élément de la séquence allant de 1 à 10, nous affichons le carré de i.

4.3.3 Application sur vecteurs

Prenons un vecteur de chiffres.

x <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Utilisons une boucle avec la fonction for pour élever les éléments à leur carré et stockons dans un vecteur nommé y.

# Création d'une coquille vide de vecteur.
y <- c() 
  # Pour chaque élément dans le vecteur x,
for(i in x){ 
  # créer un élément dans le vecteur y qui en serait le carré.
  y[[i]] <- i^2 
  }

Regardons y

y
##  [1]   1   4   9  16  25  36  49  64  81 100

Ici, la boucle marche parfaitement, mais on peut s’en passer. Reprenons l’opération, mais avec une approche différente.

# Le vecteur de départ
x <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
# L'élévation au carré
y <- x^2
y
##  [1]   1   4   9  16  25  36  49  64  81 100

Même résultat. Moins de codage. Donc solution optimale! La fonction native (^) s’exécute déjà en boucle sur tous les éléments du vecteur.

Pour nous assurer que cette règle n’est pas limité qu’aux chiffres, testons avec les lettres. Prenons un vecteur de caractères.

x <- c("Mamadou", "Amadou", "Ahmed", "Ahmad", "Abdoul", "Zan", "Tchiè", "Mady")

Cherchons à détecter les prénoms qui contiennent la lettre “a” (en minuscule). R a des fonctions natives qui peuvent exécuter cette tâche dont grepl.

# Création d'une coquille vide de vecteur.
y <- c() 
  # Pour chaque élément dans le vecteur x,
for(i in x){ 
  # identifier les éléments contenant la lettre "a".
  y[i] <- grepl(pattern = "(a)", x = i) 
}

Regardons y.

y
## Mamadou  Amadou   Ahmed   Ahmad  Abdoul     Zan   Tchiè    Mady 
##    TRUE    TRUE   FALSE    TRUE   FALSE    TRUE   FALSE    TRUE

Encore une fois, on peut remarquer que R est sensible à la taille de la lettre (minuscule/majuscule). Regardez les résultats pour Ahmad et Ahmed. Reprenons en appliquant directement la formule au vecteur directement.

y <- grepl(pattern = "(a)", x)
y
## [1]  TRUE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE

Même résultat. Moins de codage. Donc solution optimale! La fonction native grepl s’exécute déjà en boucle sur tous les éléments du vecteur. Leçon: chaque fois, qu’une fonction native existe et peut exécuter une tâche, il est préférable de se passer de la boucle.

4.3.4 Application sur matrices

Maintenant, essayons sur une matrice.

x <- matrix(data = c(1:12), nrow = 3, byrow = TRUE)
x
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12

Comme avant, élévons les éléments à leur carré et stockons dans une matrice nommée y.

# Création d'une coquille vide de matrice
y <- c() 
  # Pour chaque élément dans la matrice x,
for(i in x){ 
  # créer un élément dans la matrice y qui en serait le carré.
  y[[i]] <- i^2 
}

Regardons y.

y
##  [1]   1   4   9  16  25  36  49  64  81 100 121 144

Ici la boucle donne le bon résultat, mais pas le bon format. Nous cherchons une matrice, mais c’est un vecteur que nous avons eu. Apparemment, la boucle doit aussi tenir compte du format. Ajustons-donc le format de la matrice qui recevra les résultats. Créons une coquille vide de matrice.

y <- matrix(data = rep(NA, times = 12), nrow = 3, byrow = TRUE) 
y
##      [,1] [,2] [,3] [,4]
## [1,]   NA   NA   NA   NA
## [2,]   NA   NA   NA   NA
## [3,]   NA   NA   NA   NA

Et reprenons la boucle.

  # Pour chaque ligne (i) de la matrice x, et
for(i in 1:nrow(x)){ 
    # pour chaque colonne (j) de la matrice x,
  for(j in 1:ncol(x)){ 
    # créer un élément dans la matrice y qui en serait le carré.
    y[i, j] <- x[i, j]^2 
  }
}

Regardons y.

y
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    9   16
## [2,]   25   36   49   64
## [3,]   81  100  121  144

Nous avons le bon résultat et le bon format. Mais que de lignes de codes!!!! Il doit y avoir une voie plus simple!

Maintenant, regardons une autre solution: l’implémentation directe du la formule (^2) sur la matrice de départ.

y <- x^2
y
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    9   16
## [2,]   25   36   49   64
## [3,]   81  100  121  144

A l’instar du vecteur, l’on peut appliquer des formules directements aux matrices. L’objet qui en résulte hérite de la structure et du format de la matrice de départ.

Ce qui marche pour les chiffres, marche-t-il pour les lettres aussi? Comme pour les vecteurs, testons avec une matrice de caractères. Considérons la matrice suivante.

x <- matrix(data = c("Zégoua", "Hamdallaye", "Zanbougou",
                     "Farimaké","Cinzani", "Tinzawatene",
                     "Nara", "Hawa Dembaya", "Bozobougou"), 
            nrow = 3, byrow = TRUE)
x
##      [,1]       [,2]           [,3]         
## [1,] "Zégoua"   "Hamdallaye"   "Zanbougou"  
## [2,] "Farimaké" "Cinzani"      "Tinzawatene"
## [3,] "Nara"     "Hawa Dembaya" "Bozobougou"

Cherchons-y dans les éléments qui contiennent la lettre “z” (minuscule!). Appliquons directement la formule à la matrice x.

y <- grepl(pattern = "(z)", x)
y
## [1] FALSE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE  TRUE

Nous avons le bon résultat, mais pas le bon format. R a généré le résultat sous format de vecteur. Ce qui en érode fortement la lisibilité. Ajustons! Nous pouvons générer le résultat et le déclarer sous le format de matrice.

# étape 1
y <- grepl(pattern = "(z)", x) 
# étape 2
y <- matrix(data = y, nrow = 3, byrow = TRUE) 
y
##       [,1]  [,2]  [,3]
## [1,] FALSE FALSE FALSE
## [2,] FALSE  TRUE FALSE
## [3,] FALSE  TRUE  TRUE

Ou tout simplement combiner les deux étapes.

# combinaison des 2 étapes
y <- matrix(data = grepl(pattern = "(z)", x), nrow = 3, byrow = TRUE) 
y
##       [,1]  [,2]  [,3]
## [1,] FALSE FALSE FALSE
## [2,] FALSE  TRUE FALSE
## [3,] FALSE  TRUE  TRUE

Malgré cet ajustement, l’application directe de la formule est préférable à la boucle car une fonction native existe déjà pour l’exécution de la tâche souhaitée. Sachant que les matrices sont fortement sollicitées en algèbre, il n’est pas surprenant de trouver que le format est respecté quand les opérations pour sur des chiffres, mais défait quand il s’agit de lettres ou caractères.

4.3.5 Application sur data frame

Partant de ce qu’on a vu avec les vecteurs et les matrices, on peut se douter que les boucles ne sont pas toujours le meilleur choix pour les data frame non plus.

Supposons que l’on veuille calculer pour chaque groupe d’âge de notre data frame l’écart entre les femmes et les hommes: femme - homme. On pourrait faire une boucle:

ecart_femme_homme <- c()
for(i in 1:nrow(pop_groupage_2009)){
  ecart_femme_homme[i] <- pop_groupage_2009[i, "femme"] - pop_groupage_2009[i, "homme"]
}
ecart_femme_homme
##  [1] -32143 -46295 -53071  53324  85638 100488  44582   2635   4855 -11070
## [11]   3481 -14467  -3894 -11252  -1136  -4718   1725      0

Un détour for peu utile quand on peut faire plus simple.

pop_groupage_2009$ecart_femme_homme <- pop_groupage_2009$femme - pop_groupage_2009$homme
head(x = pop_groupage_2009, n = 3)
##    annee groupage   femme   homme   total        sup_num ecart_femme_homme
## 55  2009      0-4 1321275 1353418 2674693 femme <= homme            -32143
## 56  2009      5-9 1178850 1225145 2403995 femme <= homme            -46295
## 57  2009    10-14  882725  935796 1818521 femme <= homme            -53071

4.3.6 Application sur listes

C’est avec les listes que les boucles prennent tout leur sens. Les vecteurs, matrices et data frame constituent tous des objets unitaires eux-mêmes. Ils ont leur propriétés propres à eux-mêmes (structure et comportements). Ceci veut dire qu’ils prêtent à l’assimilation par les fonctions. Celles-ci vont systématiquement s’appliquer sur tous les éléments désignés au sein de l’objet. Qu’il s’agisse d’une opération mathématiques (élévation au carré) ou de l’examen de texte (détection d’un caractère), l’objet peut servir d’intrant direct à la fonction utilisée dans la boucle.

Avec la liste, les choses sont différentes. La liste est un objet hôte. Bien qu’elle ait ses propriétés, elle sert de contenant à d’autres objets. De ce fait, elle peut abriter plusieurs objets sur lesquels l’on peut souhaiter exécuter la même opération en boucle. Et c’est là, qu’on est content que les boucles existent!

Illustrons!

Rappelons d’abord les noms des objets contenus dans notre liste pop_groupage_list.

names(pop_groupage_list)
## [1] "1976" "1987" "1998" "2009"

Maintenant, commençons avec le simple affichage de la première observation de tous les data frame de la liste.

  # Pour chaque élément de la liste
for(i in pop_groupage_list) { 
  # Assigner l'affichage de la 1ère observation à une variable
  obs1 <- head(x = i, n = 1) 
  # Affiche toutes les 1ères variables extraites
  print(obs1)
}
##   annee groupage  femme  homme   total
## 1  1976      0-4 589394 587015 1176409
##    annee groupage  femme  homme   total
## 19  1987      0-4 713507 719804 1433311
##    annee groupage  femme  homme   total
## 37  1998      0-4 824505 839795 1664300
##    annee groupage   femme   homme   total
## 55  2009      0-4 1321275 1353418 2674693

Nous avons vu plus haut qu’avec les déclarations conditionnelles, l’on peut exécuter des tâches sur la base d’un arbre de décision. Maintenant, imaginez que vous avez à répéter une même tâche sur plusieurs objets. Nous avons vu que la liste contient 4 data frames, tirés de 4 recensements (1976, 1987, 1998 et 2009). Imaginez que vous souhaitez déterminer qui des hommes et des femmes sont les plus nombreux et ce pour tous les années de recensement. Là, vous allez devoir définir une tâche et l’exécuter en boucle. Pensez-vous comme un agent de vaccination qui passe dans toutes les concessions (data frame) d’une rue (liste) pour vacciner des enfants (le test femme > homme). Générons dans chacun des data frame une variable femme_sup_homme qui est vrai (TRUE) quand femme > homme et faux (FALSE) dans le cas contraire.

  # Pour chaque élément "i" de la liste "pop_groupage_list"
for(i in pop_groupage_list){
  # Exécuter l'opération "femme > homme"
  i[, "femme_sup_homme"] <- i[, "femme"] > i[, "homme"] 
  # Assigner l'affichage des 3 premières observations à une variable
  obs3 <- head(x = i, n = 3) 
  # Afficher toutes les 3 premières observations extraites.
  print(obs3) 
}
##   annee groupage  femme  homme   total femme_sup_homme
## 1  1976      0-4 589394 587015 1176409            TRUE
## 2  1976      5-9 482851 492272  975123           FALSE
## 3  1976    10-14 321959 342807  664766           FALSE
##    annee groupage  femme  homme   total femme_sup_homme
## 19  1987      0-4 713507 719804 1433311           FALSE
## 20  1987      5-9 611562 633206 1244768           FALSE
## 21  1987    10-14 414302 452166  866468           FALSE
##    annee groupage  femme  homme   total femme_sup_homme
## 37  1998      0-4 824505 839795 1664300           FALSE
## 38  1998      5-9 797057 830211 1627268           FALSE
## 39  1998    10-14 589603 637495 1227098           FALSE
##    annee groupage   femme   homme   total femme_sup_homme
## 55  2009      0-4 1321275 1353418 2674693           FALSE
## 56  2009      5-9 1178850 1225145 2403995           FALSE
## 57  2009    10-14  882725  935796 1818521           FALSE

Allons plus loin en enrichissant les conditions. Voici la démarche:

  • sélectionnons seulement les moins de 15 ans: groupage est 0-4 ou 5-9 ou 10-14;

  • créons ensuite une colonne test_max qui indique qui des hommes ou des femmes a la supériorité numérique;

  • créons ensuite une colonne valeur_max qui donne la valeur de la population.

  # Pour chaque élément "i" de la liste "pop_groupage_list"
for(i in pop_groupage_list){
  # sélection des groupes d'âge dans 0-15 ans.
  i <- i[i["groupage"]=="0-4" | i["groupage"]=="5-9" | i["groupage"]=="10-14",]
  # déclarations conditionnelles pour les variables "test_max" et "valeur_max".
  i[, "test_max"] <- ifelse(# condition
                            test = i[,"femme"] > i[,"homme"], 
                            # action si condition satisfaite 
                            yes = "femme", 
                            # action si condition non satisfaite
                            no = "homme") 
  i[, "valeur_max"] <- ifelse(# condition
                              test = i[,"femme"] > i[,"homme"], 
                              # action si condition satisfaite 
                              yes = i[,"femme"], 
                              # action si condition non satisfaite
                              no = i[,"homme"]) 
  # Afficher toutes les 2 premières extraites.
  print(head(x = i, n = 2)) 
}
##   annee groupage  femme  homme   total test_max valeur_max
## 1  1976      0-4 589394 587015 1176409    femme     589394
## 2  1976      5-9 482851 492272  975123    homme     492272
##    annee groupage  femme  homme   total test_max valeur_max
## 19  1987      0-4 713507 719804 1433311    homme     719804
## 20  1987      5-9 611562 633206 1244768    homme     633206
##    annee groupage  femme  homme   total test_max valeur_max
## 37  1998      0-4 824505 839795 1664300    homme     839795
## 38  1998      5-9 797057 830211 1627268    homme     830211
##    annee groupage   femme   homme   total test_max valeur_max
## 55  2009      0-4 1321275 1353418 2674693    homme    1353418
## 56  2009      5-9 1178850 1225145 2403995    homme    1225145

4.3.7 Arrêtons-nous un instant!

Qu’avons-nous vu jusque là? Nous avons vu comment:

  • poser des critères et les insérer dans des déclarations ;

  • poser un raisonnement en arbre de décision avec les déclarations conditionnelles ;

  • les boucles marchent avec divers objets (vecteurs, matrices, data frame et listes).

Nous avons vu que c’est avec les listes que les boucles révèlent leur plus grande utilité. Il se trouve que R contient aussi des fonctions taillées spécialement pour tourner des fonctions en boucle sur les éléments d’une liste. Dans R-base seulement, il y a une grande famille de fonction dont lapply, sapply, vapply, tapply, mapply, rapply, eapply… Toutes ces fonctions sont des outils du paradigme split-apply-combine qui consiste à :

  • diviser des données en morceaux ;

  • à appliquer sur chaque morceau une fonction donnée ;

  • à rassembler les résultats en un nouveau morceau.

Nous allons nous limiter à lapply ici. Explorons lapply avec quelques exemples.

4.3.8 Paradigme split-apply-combine: illustration avec lapply

Considérons la liste suivante avec deux vecteurs et deux matrices:

malist <- list(monvect2 = seq(from = 0, to = 20, by = 0.5),
               monvect1 = rnorm(20, mean = 9.88, sd = 1.23),
               mamat1 = matrix(data = c(1:20), nrow = 4, byrow = TRUE),
               mamat2 = matrix(data = rnorm(20, mean = 7.43, sd = 1.80), nrow = 4, byrow = TRUE)
               )

Regardons le contenu.

str(malist)
## List of 4
##  $ monvect2: num [1:41] 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 ...
##  $ monvect1: num [1:20] 9.75 10.53 9.34 11.43 8.51 ...
##  $ mamat1  : int [1:4, 1:5] 1 6 11 16 2 7 12 17 3 8 ...
##  $ mamat2  : num [1:4, 1:5] 8.65 7.59 9.9 6.88 7.49 ...

Pour chaque objet de la liste, procédons à une agrégation avec la fonction sum.

for(i in malist){
  print(sum(i))
  }
## [1] 410
## [1] 200.8776
## [1] 210
## [1] 160.1925

Faisons la même chose avec “lapply”

lapply(X = malist, FUN = sum)
## $monvect2
## [1] 410
## 
## $monvect1
## [1] 200.8776
## 
## $mamat1
## [1] 210
## 
## $mamat2
## [1] 160.1925

Le même résultat est obtenu avec lapply, sous la forme d’une nouvelle liste.

Testons encore! Au lieu des sommes, générons cette fois-ci les moyennes de chaque objet de la liste. Avec la boucle…

for(i in malist){
  print(mean(i))
  }
## [1] 10
## [1] 10.04388
## [1] 10.5
## [1] 8.009627

…et avec lapply

lapply(X = malist, FUN = mean)
## $monvect2
## [1] 10
## 
## $monvect1
## [1] 10.04388
## 
## $mamat1
## [1] 10.5
## 
## $mamat2
## [1] 8.009627

Vous voyez la logique?

Nous avons vu que, pour les matrices et les vecteurs, l’on trouve souvent des fonctions qui sont déjà capables d’exécuter les tâches souhaitées (ne paniquez pas, avec la pratique, votre répertoire de fonctions s’agrandira!) Et quand celà est possible, une boucle n’est pas nécessaire. Le même principe va pour les listes. Quand il y a des fonctions natives qui peuvent a) exécuter la tâche souhaitée (générer une somme ou une moyenne par exemple) et b) insérer cette tâche dans une boucle (exécuter sur tous les objets d’une liste), alors, il est préférable d’embrasser cette voie. Les exemples précédents ont clairement illustré celà.

Contiuons avec d’autres exemples pour illustrer davantage. Cherchons à connaître les dimensions des objets de la liste (nombre de lignes, nombres de colonnes).

lapply(X = pop_groupage_list, FUN = dim)
## $`1976`
## [1] 18  5
## 
## $`1987`
## [1] 18  5
## 
## $`1998`
## [1] 18  5
## 
## $`2009`
## [1] 18  5

Et le nom des variables?

lapply(X = pop_groupage_list, FUN = colnames)
## $`1976`
## [1] "annee"    "groupage" "femme"    "homme"    "total"   
## 
## $`1987`
## [1] "annee"    "groupage" "femme"    "homme"    "total"   
## 
## $`1998`
## [1] "annee"    "groupage" "femme"    "homme"    "total"   
## 
## $`2009`
## [1] "annee"    "groupage" "femme"    "homme"    "total"

C’est simple et efficace. N’est-ce pas?

Maintenant, supposons qu’on veut déterminer la population totale pour chaque année en faisant la somme de la colonne total. Comment faire ? Avec une boucle, c’est facile.

for(i in pop_groupage_list){
  print(sum(i["total"]))
  }
## [1] 6392918
## [1] 7696349
## [1] 9810912
## [1] 14528662

Avec lapply.

lapply(X = pop_groupage_list, FUN = sum)
## Error in FUN(X[[i]], ...): only defined on a data frame with all numeric variables

lapply n’arrive pas à s’exécuter car nous n’avons pas spécifié qu’à l’intérieur de chaque data frame, il fallait prendre la variable total! Certes, lapply est destinée à exécuter les tâches en boucle, mais encore faudrait-il que celles-ci soient bien définies. Et c’est ça que fait une fonction. Elle exécute des tâches! Et ça, c’est le début d’un autre pan de la Data Science: la programmtion fonctionnelle. Dans ce terme, on va englober, l’art de faire des fonctions. Tout y passe: de la conception à la rapidité. Dans la prochaine, nous allons reprendre les idées déjà présentées, mais cette fois-ci en raisonnant en termes de fonctions.

4.4 Les fonctions

4.4.1 L’épine dorsale de R

Pour un data scientist, les fonctions sont d’une importance capitale car son flux de travail consiste à faire passer les données d’une fonction à une autre pour recadrer ses questions ou trouver des réponses à celles-ci. Depuis le début, nous parlons de fonctions. Qu’est-ce que c’est? Dans R, la fonction agit comme dans les mathématiques. C’est un règle ou une procédure qui détermine la transformation d’un intrant en extrant. Prenons l’exemple suivant:

\[y = f(x) = x^2\]

Dans cet exemple, la fonction élève les intrants au carré pour donner les extrants. Dans R, c’est la même chose! Nous avons déjà mentionnées certaines fonctions et avons montré ce qu’elles font. Revenons sur quelques unes.

4.4.2 Retour sur quelques fonctions

Prenons le vecteur suivant:

monvect <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

Si nous voulons compter le nombre d’éléments composants cet éléments, une fonction…

length(monvect)
## [1] 10

…faire la somme de ces éléments, une fonction…

sum(monvect)
## [1] 55

…faire la moyenne de ces éléments, une fonction…

mean(monvect)
## [1] 5.5

Revenons a notre liste. Pour voir sa structure, une fonction…

str(pop_groupage_list)
## List of 4
##  $ 1976:'data.frame':    18 obs. of  5 variables:
##   ..$ annee   : num [1:18] 1976 1976 1976 1976 1976 ...
##   ..$ groupage: Ord.factor w/ 18 levels "0-4"<"5-9"<"10-14"<..: 1 2 3 4 5 6 7 8 9 10 ...
##   ..$ femme   : num [1:18] 589394 482851 321959 333508 265842 ...
##   ..$ homme   : num [1:18] 587015 492272 342807 308607 218391 ...
##   ..$ total   : num [1:18] 1176409 975123 664766 642115 484233 ...
##  $ 1987:'data.frame':    18 obs. of  5 variables:
##   ..$ annee   : num [1:18] 1987 1987 1987 1987 1987 ...
##   ..$ groupage: Ord.factor w/ 18 levels "0-4"<"5-9"<"10-14"<..: 1 2 3 4 5 6 7 8 9 10 ...
##   ..$ femme   : num [1:18] 713507 611562 414302 379522 315753 ...
##   ..$ homme   : num [1:18] 719804 633206 452166 348200 260215 ...
##   ..$ total   : num [1:18] 1433311 1244768 866468 727722 575968 ...
##  $ 1998:'data.frame':    18 obs. of  5 variables:
##   ..$ annee   : num [1:18] 1998 1998 1998 1998 1998 ...
##   ..$ groupage: Ord.factor w/ 18 levels "0-4"<"5-9"<"10-14"<..: 1 2 3 4 5 6 7 8 9 10 ...
##   ..$ femme   : num [1:18] 824505 797057 589603 529270 409584 ...
##   ..$ homme   : num [1:18] 839795 830211 637495 492480 364333 ...
##   ..$ total   : num [1:18] 1664300 1627268 1227098 1021750 773917 ...
##  $ 2009:'data.frame':    18 obs. of  5 variables:
##   ..$ annee   : num [1:18] 2009 2009 2009 2009 2009 ...
##   ..$ groupage: Ord.factor w/ 18 levels "0-4"<"5-9"<"10-14"<..: 1 2 3 4 5 6 7 8 9 10 ...
##   ..$ femme   : num [1:18] 1321275 1178850 882725 799081 624565 ...
##   ..$ homme   : num [1:18] 1353418 1225145 935796 745757 538927 ...
##   ..$ total   : num [1:18] 2674693 2403995 1818521 1544838 1163492 ...

Pour voir le nom des éléments qu’elle contient, une fonction

names(pop_groupage_list)
## [1] "1976" "1987" "1998" "2009"

Vous voyez l’idée?

4.4.3 Pourquoi faire une fonction?

Vu la richesse de R on peut bien être amené à se demander pourquoi se donner la peine de faire une fonction. N’en exist-il pas déjà dans R ? La plupart du temps, oui! Mais pas tout le temps.

Autant, sont nombreuses les questions que la data scientist soulève, autant les voies qui s’offrent à lui pour y répondre sont variées. Les particularités de la question peuvent faire qu’il est souhaitable voire indispensable de personnaliser la réponse. D’où la nécéssité de créer de nouvelles fonctions. Celles-ci peuvent aussi bien s’incorporer dans le flux de travail que prendre intégralement celui-ci en charge.

4.4.4 Les basiques de la fonction

Bien que tous les sept milliards d’humains peuplant la terre partagent cette appélation commune, il demeure que l’on s’attache à donner à chacun d’entre eux une appélation particulière: le prénom! N’est-ce pas? De même, une fonction a besoin d’un nom! A ce niveu, il est utile d’indiquer qu’il y a des mots réservés qui ne peuvent pas être utilisés. Voir:

help("reserved")

Après le nom, il y a les arguments. Ceci est l’appélation donnée aux intrants. Ensuite on a le corps qui est la procédure à laquelle sont soumis ces intrants. Schématisons tout ça!

mafonction <- function(x){
  x^2
  }

Nous avons défini ici une fonction, mafonction, où x est l’argument et la procédure à laquelle il est soumis est l’élévation au carré. Testons le résultat.

mafonction(x = 25)
## [1] 625

Juste pour ironiser un peu, rappelons que c’est par une fonction, function, que nous venons de créer une fonction. Trop méta, R !!!!!!

Avançons un peu ici en créant une fonction avec deux arguments: x et y.

mafonction <- function(x, y){
  x + y
  }

Testons

mafonction(x = 1, y = 2)
## [1] 3

Souvent, il est possible d’assigner à un argument ou à tous une valeur par défaut.

mafonction <- function(x, y = 10){
  x + y
  }

Regardons ce qu’on obtient quand on ne spécifie pas la valeur passée à l’argument y.

mafonction(x = 3)
## [1] 13

Certaines fonctions contiennent plusieurs arguments. Par commodité, on a assigne à beaucoup des valeurs par défaut qui sont validées sauf si l’utilisateur en décide autrement.

Souvent, la fonction comprends des étapes intermédiaires. A vrai dire, c’est dans ça que réside la nécéssité des fonctions, le séquençage de procédures multiples en une seule commande. Revenant à notre example, nous pouvons assigner le résultat à une variable intermédiaire z.

mafonction <- function(x, y = 10){
  z <- x + y
  }
mafonction(x = 3)

L’exécution de la fonction sur le chiffre 3 n’a pas donné de résultat car nous n’avons pas demandé à la fonction d’afficher celui-ci. Pour que le résultat sorte, il faut expliciter.

mafonction <- function(x, y = 10){
  z <- x + y
  return(z)
  }
mafonction(x = 3)
## [1] 13

return() est très commode quand on doit passer par plusieurs étapes à l’intérieur de la fonction.

4.4.5 Fonctions et boucles

Les fonctions (écriture, évaluation, etc.) constitue un domaine vaste de la data science. Nous ne pourrons pas tout voir d’un seul coup. La maîtrise des règles (les do’s et les don’t’s) viennent avec la pratique. Ayant couvert les basiques, nous allons retourner à nos données pour illustrer. Vous vous rappelez la boucle suivante…

for(i in pop_groupage_list){
  print(sum(i["total"]))
  }
## [1] 6392918
## [1] 7696349
## [1] 9810912
## [1] 14528662

…qu’on avait pas réussi à insérer dans la fonction lapply? Elle marche. La raison est simple. lapply exécute des fonctions sur les objets contenus dans une liste. Elle ne peut pas systématiquement atteindre les éléments contenus dans ces objets. Nous avons pu faire des moyennes et des médianes sur des vecteurs et matrices à partir de lapply. La raison était simple: ces objets sont des ensembles homogènes. Ils contenaient tous des chiffres, qui sont des éléments assimilables par mean et median. Or, le data frame est un objet hétérogène, contenant des chiffres et des lettres. Les fonctions natives qu’on a utilisées ne peuvent faire la différence d’elles-mêmes. Elles doivent être guidées. De ce fait, nous devons inscrire la procédure souhaitée dans une fonction avant de passer celle-ci à lapply qui va l’exécuter en boucle sur tous les objets de la liste. Voici la fonction:

pop_somme <- function(df){
  sum(df["total"])
  }

Nous venons de définir une fonction où df est l’argument principal. On s’attend à un data frame comme intrant. On s’attend à ce que celui-ci ait une colonne nommée total dont les éléments seront agrégés par la fonction sum. Vous voyez ? C’est une solution très personnalisée! Regardons les résultats!

for(i in pop_groupage_list){
  print(sum(i["total"]))
}
## [1] 6392918
## [1] 7696349
## [1] 9810912
## [1] 14528662

Voici le résultat pour lapply.

lapply(X = pop_groupage_list, FUN = pop_somme)
## $`1976`
## [1] 6392918
## 
## $`1987`
## [1] 7696349
## 
## $`1998`
## [1] 9810912
## 
## $`2009`
## [1] 14528662

Qu’est-ce qui est préférable? Comme avant, les fonctions existantes sont toujours meilleures. lapply est intégrée à R. Elle constitue une meilleure boucle. Aussi, elle peut génère en extrant une liste, qui peut être assignée à un objet donné.