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
}
où 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é.