class: center, middle, inverse, title-slide # Informatique : Boucles et calculs vectoriels ## Licence 3 Economie-Gestion - Parcours Economie-Finance ### Ewen Gallic ### Aix-Marseille Université ### 2020-2021 (mise à jour: 2021-11-18) --- class: inverse, center, middle # 1. Les boucles <br/><br/><br/> <div style='text-align: center;'> <img src = "images/boucles/loop.png" width = "300px" title = "Portal - Caution Infinite Loop by caycowa" /> <br/> <span class = "lien_source">Source : <a href = "http://fav.me/d4f8wxx">Portal - Caution Infinite Loop by caycowa, on Deviantart</a></span> </div> --- class: inverse, center, middle ## 2. Les boucles avec `while()` --- ### Les boucles avec `while()` - Répéter une instruction tant qu'une condition d'arrêt n'est pas honorée ; - Nombre de répétitions inconnu _a priori_. ```r while(condition) instruction ``` - `condition` doit être un logique ; - `instruction` peut contenir plusieurs instructions, si entouré par des accolades. --- ### Les boucles avec `while()` ```r x <- 100 while(x/3 > 1){ x <- x/3 } x/3 > 1 ``` ``` ## [1] FALSE ``` ```r x ``` ``` ## [1] 1.234568 ``` - Attention à bien s'assurer d'avoir une condition qui pourra être honorée ; - R éprouve des difficultés à arrêter le calcul d'une boucle infinie... --- class: exo # Exercice Considérons le vecteur suivant : ```r x <-c(2,1,4,5,2,1,6) ``` À partir de combien d'éléments de `x` la somme cumulée devient-elle supérieure ou égale à 10 ? Pour répondre à cette question, utilisez une boucle `while`: - à chaque itération, la somme cumulée doit être mise à jour pour venir ajouter à la valeur précédente la valeur courante du ie élément de `x` - un itérateur doit être créé - la somme cumulée doit être initialisée à 0 --- class: exo # Exercice 1. Calculer, dans une expérience de "pile "ou "face", le nombre de tirages alétoires nécessaires avant d'obtenir le premier "pile". Note : penser à utiliser la fonction `sample()`. --- class: inverse, center, middle ## 3. Les boucles avec `for()` --- ### Les boucles avec `for()` - Répéter une instruction un nombre de fois défini _a priori_. ```r for(variable in vector) instruction ``` - `variable` : nom de variable locale à la fonction `for`, qui prendra une valeur différente à chaque itération ; - `vector` : le vecteur contenant les valeurs que va prendre `variable` ; - `instruction`: l'instruction à répéter à chaque itération (si entre accolades, permet d'évaluer plusieurs instructions). --- ### Les boucles avec `for()` Dans l'exemple suivant, on affiche dans la console, un par un, les noms du vecteur. ```r for(nom in c("Sonia", "Anne-Hélène", "Julien-Yacine")) print(nom) ``` ``` ## [1] "Sonia" ## [1] "Anne-Hélène" ## [1] "Julien-Yacine" ``` --- ### Les boucles avec `for()` - Les boucles avec `for()` sont pratiques pour remplir les éléments d'une liste ou d'un vecteur ; - La création _a priori_ de l'objet à remplir, à la taille souhaitée est recommandée ; - En effet, l'allocation en mémoire est plus efficace de la sorte, plutôt que de laisser `R` créer un objet plus long à chaque itération. ```r # Manière peu efficace resultat <- NULL for(i in seq_len(3)) { resultat[i] <- i } resultat ``` ``` ## [1] 1 2 3 ``` --- ### Les boucles avec `for()` ```r # Manière plus économique resultat <- rep(NA, 3) for(i in seq_len(3)) { resultat[i] <- i } resultat ``` ``` ## [1] 1 2 3 ``` --- class: exo # Exercice 1. Utiliser une boucle `for` pour calculer, sur 50 tirages de "pile" ou "face" avec une pièce de monnaie combien de "pile" sont obtenus. Note : penser à utiliser la fonction `sample()` pour effectuer le tirage. --- class: inverse, center, middle ## 4. Les conditions --- ### Les conditions - Les conditions sont primordiales en programmation ; - Elles permettent d'exécuter du code, ou de retourner des objets, à condition de remplir certains critères (sinon, le code n'est pas exécuté, il est ignoré) --- ### Les conditions : instruction `if`... `else` - Les instructions `if` et `else` fournissent un moyen d'exécuter du code si une condition est respectée ou non. - Deux syntaxes possibles : ```r # Première forme (pas de code si condition == FALSE) if (condition) instruction # Seconde forme if (condition) instruction si vrai else instruction si faux ``` - `condition` : un logique ; - `instruction` : du code à évaluer en fonction de la condition. --- ### Les conditions : instruction `if`... `else` - Exemple avec une condition simple ```r x <- 2 if(x == 2) print("Hello") ``` ``` ## [1] "Hello" ``` ```r x <- 3 if(x == 2) print("Hello") ``` --- ### Les conditions : instruction `if`... `else` - Exemple avec des instructions dans le cas contraire ```r if(x == 2) print("Hello") else print("x est différent de 2") ``` ``` ## [1] "x est différent de 2" ``` ```r if(x == 2){ print("Hello") } else {# x != 2 x <- x-1 print(paste0("La nouvelle valeur de x : ", x)) } ``` ``` ## [1] "La nouvelle valeur de x : 2" ``` --- class: exo # Exercice 1. Créer une fonction qui retourne `TRUE` si un nombre `x` est divisible par une autre nombre `diviseur`, et `FALSE` sinon. --- ### Les conditions : la fonction `switch()` - La fonction `switch()` permet d'éxecuter du code en fonction d'une valeur prise par une variable. ```r switch(valeur_test, cas_1 = { instruction_cas_1 }, cas_2 = { instruction_cas_2 }, ... ) ``` - `valeur_test` : nombre ou chaîne de caractère ; - Si `valeur_test` vaut `cas_1`, évaluer `instruction_cas_1` ; - Si `valeur_test` vaut `cas_2`, évaluer `instruction_cas_2`. --- ### Les conditions : la fonction `switch()` ```r centre <- function(x, type) { switch(type, mean = mean(x), median = median(x)) } x <- rcauchy(10) centre(x, "mean") ``` ``` ## [1] -0.03528412 ``` ```r centre(x, "median") ``` ``` ## [1] 0.1941483 ``` --- ### L'intstruction `repeat`... `break` - `repeat`... `break` permet de répéter une expression ; - La présence d'un test d'arrêt est nécessaire, à l'aide de l'instruction `break`. ```r i <- 1 repeat { i <- i + 1 if(i == 3) break } i ``` ``` ## [1] 3 ``` --- ### L'intstruction `next`... `break` - `next`... `break` permet de passer immédiatement à l'itération suivante d'une boucle `for`, `while` ou `repeat`. ```r resul <- rep(NA, 10) for(i in 1:10) { if(i == 5) next resul[i] <- i } # Le 5e élément de resul est resté non-disponible resul ``` ``` ## [1] 1 2 3 4 NA 6 7 8 9 10 ``` --- ### Barre de progression - Pour suivre l'évolution d'un calcul sans afficher dans la console la valeur d'un compteur à chaque itération : barre de progression - `txtProgressBar()` du _package_ {`utils`} : - `min` et `max` : valeurs numériques pour les bornes de la barre de progression - `style` : style de la barre de progression (si `style=3` : pourcentage de progression affiché) ```r nb_tours <- 10 p_b <- txtProgressBar(min = 1, max = nb_tours, style = 3) for(i in 1:nb_tours){ Sys.sleep(0.1) setTxtProgressBar(p_b, i) } ``` --- class: exo # Exercice Neuf (9) fichiers CSV sont présents dans le dossier situé à l'adresse suivante : egallic.fr/www/Enseignement/R/Exercices/Loops. La structure de chacun de ces fichiers CSV est identique d'un fichier à l'autre : - 3 colonnes (`x`, `y` et `row_number`) - 1000 lignes. Le nom des fihciers suit la syntaxe suivante : `loop_file_xxx.csv`, où `xxx`vaut `001`, `002`, ..., `009`. --- class: exo # Exercice L'objectif est de charger ces 9 fichiers à l'aide d'une boucle et d'empiler leur contenu dans un seul tableau de données. 1. Créez une liste vide nommée `df_l`, de longueur 9, à l'aide de la fonction `vector()`. 2. Créez une chaîne de caractères nommée `i` contenant la valeur `1`. 3. À l'aide de la fonction `str_pad()`, créez la chaîne de caractères `i_pad` formattant la valeur de `i` de manière à ajouter des "0" devant la valeur de `i` ; `i_pad` doit avoir une longueur de 3 caractères. Exemple : Si `i` vaut 1, alors `i_pad` doit valoir `001`. 4. Créez un lien vers le ie fichier, en vous appuyant sur la valeur de `i_pad` (concaténation). 5. Importez le ie fichier CSV dans R, en fournissant le lien créé. 6. Stockez le contenu importé dans le ie élément de la liste `df_l`. 7. À l'aide d'une boucle, importez les 9 fichiers CSV et stockez le résultat dans `df_l`. --- class: inverse, center, middle # 5. La vectorisation <br/><br/><br/> <div style='text-align: center;'> <img src = "images/boucles/assembly_line.jpg" width = "400px" title = "Travail à la chaine" /> <br/> <span class = "lien_source">Source : <a href="http://commons.wikimedia.org/wiki/File:Douglas_F4D-1_Skyray_El_Segundo_assembly_line1_c1956.jpg">U.S. Navy National Museum of Naval Aviation</a></span> </div> --- ### La vectorisation - Les boucles sont lentes en `R` ; - De nombreux cas où elles peuvent être évitées ; - Emploi des calculs vectoriels. --- ### La vectorisation - Voici un exemple d'un calcul en boucle que l'on peut vectoriser : ```r # Somme des logarithmes des 10 premiers entiers somme_log <- 0 for(i in seq_len(10)){ somme_log <- somme_log + log(i) } somme_log ``` ``` ## [1] 15.10441 ``` - À chaque itération, on modifie la valeur de `somme_log` pour lui ajouter la valeur du logarithme d'un entier `i`. --- ### La vectorisation - La même chose en vectorisant : ```r # En vectorisant le calcul sum(log(seq_len(10))) ``` ``` ## [1] 15.10441 ``` - La fonction `log()` est appliquée à tous les éléments de `seq_len(10)` ; - Puis la fonction `sum()` se charge d'additionner tous les éléments entre eux. Et avec l'opérateur pipe pour une lecture facilitée : ```r library(magrittr) seq_len(10) %>% log() %>% sum() ``` ``` ## [1] 15.10441 ``` --- class: inverse, center, middle # 6. Les boucles avec `{purrr}` --- ### Les boucles avec `{purrr}` - Opérations en boucles à effectuer sur des <b class="coul_2">listes</b> ou des <b class="coul_2">tableaux de données</b> : {`purrr`} - Excellents résumés visuels produits par RStudio : https://github.com/rstudio/cheatsheets/blob/master/purrr.pdf - Le nom de ces fonctions commence par le préfixe `map`. - Si structure voulue du résultat différente d'une liste : ajout d'un suffixe au nom de la fonction | Fonction | Résultat | | :------- | --------------------: | | `map()` | Liste | | `map_chr()` | Vecteur de caractères | | `map_dbl()` | Vecteur de _doubles_ | | `map_int()` | Vecteur d'entiers | | `map_lgl()` | Vecteur de booléens | | `map_dfr()` | _data frame_ créé en concaténant par les lignes (`rows`) à chaque itération | | `map_dfc()` | _data frame_ créé en concaténant par les colonnes (`columns`) à chaque itération | --- ### Les boucles avec `{purrr}` | Fonction | Résultat | | :------- | --------------------: | | `map()` | Liste | | `map_chr()` | Vecteur de caractères | | `map_dbl()` | Vecteur de _doubles_ | | `map_int()` | Vecteur d'entiers | | `map_lgl()` | Vecteur de booléens | | `map_dfr()` | _data frame_ créé en concaténant par les lignes (`rows`) à chaque itération | | `map_dfc()` | _data frame_ créé en concaténant par les colonnes (`columns`) à chaque itération | Ces fonctions prennent les arguments suivants : - `.x` : une liste ou un vecteur ; - `.f` : une fonction, une formule (qui sera transformée automatiquement en fonction) ou un vecteur / une liste ; - `...` : des arguments additionnels passés à la fonction appliquée à chaque élément. --- class: titre_2, center, middle ## 6.1. Listes ou vecteurs en _input_ --- ### Les boucles avec `{purrr}` - Vecteur/liste donné(e) à l'argument `.x` des fonctions de type `map` - La fonction (ou formule) donnée à `.f` est appliquée <b class="coul_1">à chaque élément de la liste ou du vecteur</b> - Structure du résultat : dépend de la fonction `map` utilisée (cf suffixe) Exemple : 100 ré-échantillons : calcul de la moyenne sur chaque ré-échantillon. --- ### Les boucles avec `{purrr}` : mise en place de l'exemple Générons une liste de longueur 100 contenant 50 valeurs dans chaque élément de la liste (50 tirages d'une loi Normale) ```r set.seed(263) simulations <- vector(mode = "list", length = 100) for(i in 1:length(simulations)){ simulations[[i]] <- rnorm(n = 50, mean = sample(-10:10, 1), sd = runif(1, min=1, max = 5)) } length(simulations) ``` ``` ## [1] 100 ``` ```r head(simulations[[1]]) ``` ``` ## [1] -10.233237 -8.904499 -10.737834 -7.454745 -9.909407 -8.800618 ``` --- ### Les boucles avec `{purrr}` : exemple avec `for()` Nous souhaitons calculer la moyenne de chacun des 100 éléments de cette liste. Avec une boucle `for`, on pourrait faire comme suit : ```r mean_samples <- vector(mode="numeric", length = 100) for(i in 1:length(simulations)) mean_samples[[i]] <- mean(simulations[[i]]) head(mean_samples) ``` ``` ## [1] -10.220707 9.918496 -8.248388 -6.879007 -7.120338 -7.047220 ``` --- ### Les boucles avec `{purrr}` : `map_dbl()` Avec une fonction du _package_ {`purrr`}: - liste en entrée - vecteur de numériques en sortie (moyennes pour chaque élément du vecteur) - fonction utilisable : `map_dbl()` ```r library(tidyverse) ``` ```r mean_samples_2 <- map_dbl(simulations, mean) head(mean_samples_2) ``` ``` ## [1] -10.220707 9.918496 -8.248388 -6.879007 -7.120338 -7.047220 ``` Cette méthode a l'avantage de produire un code plus élégant et plus rapide à lire que celui de la boucle `for()`. --- ### Les boucles avec `{purrr}` : `map_dbl()` On peut également faire appel à l'opérateur Pipe pour rendre la lecture encore plus aisée : ```r mean_samples_2 <- simulations %>% map_dbl(mean) mean_samples_2 ``` ``` ## [1] -10.220706832 9.918496408 -8.248388114 -6.879007305 -7.120338137 ## [6] -7.047220375 4.931532798 1.737085155 -4.574383932 9.124556772 ## [11] 8.943954917 -1.491493691 -2.102030208 -4.482548167 3.654855784 ## [16] -9.517749801 4.551358128 2.994606026 -7.024923832 -1.282303259 ## [21] 4.776338753 8.113667251 -0.005680119 -7.822027500 5.840622204 ## [26] -7.013243130 10.245686303 -2.494307517 1.160991875 9.831144445 ## [31] 8.541101350 9.939128125 -8.016577992 6.618923700 -2.392853171 ## [36] 9.811093012 7.152688946 7.839453654 1.805668153 0.807152483 ## [41] -4.083873449 -6.201068955 0.976897994 4.657250189 -8.989363151 ## [46] 0.577597625 6.659060898 -4.410346718 7.106535001 10.239388318 ## [51] 5.122665438 0.925472081 6.986124688 -0.549512683 8.281610084 ## [56] -8.996323839 -3.789399121 8.566588603 5.056361674 -6.544961787 ## [61] 4.688616994 4.184239569 -4.086320691 -0.948916985 -7.888490946 ## [66] -7.571293779 9.654688184 -3.806053202 -0.033916370 -4.631940001 ## [71] -9.585889283 2.024452985 0.155805524 3.853431768 3.198035329 ## [76] 0.717163963 -8.703658524 4.933347261 -2.057835871 0.519469116 ## [81] -1.229960013 -7.186693872 0.795571538 7.798208519 -1.879021894 ## [86] 8.785325837 -4.159943980 10.085720427 9.284437013 -8.354120159 ## [91] -0.199227397 -0.493466773 5.077457476 4.597561044 5.475898139 ## [96] -1.884072789 9.456231575 1.116570283 -3.141993129 -6.144874012 ``` --- ### Les boucles avec `{purrr}` : exemple 2 Application d'une fonction nécessitant de fournir d'<b class="coul_1">autres paramètres</b> que l'élément de la liste ou du vecteur d'entrée : - exemple avec la fonction `quantile()` : `x` et `probs` - ajouter les noms des arguments et leur valeur associée comme arguments à la fonction `map()` Exemple avec le quantile d'ordre 25% : ```r simulations %>% map_dbl(quantile, probs = .25) %>% head() ``` ``` ## [1] -11.110399 8.129051 -11.295354 -8.150094 -9.325573 -8.541390 ``` --- ### Les boucles avec `{purrr}` : retourner un tableau Pour <b class="coul_1">retourner un tableau de données</b> contenant le résultat de l'application de plusieurs fonctions : création d'une fonction à la volée : `map_df()` Exemple : pour chaque élément du vecteur ou de la liste d'entrée : moyenne des valeurs, 1et et 3e quartiles : ```r simulations %>% map_df(function(x){ tibble(mean = mean(x), q1 = quantile(x, probs = .25), q3 = quantile(x, probs = .75)) }) ``` ``` ## # A tibble: 100 × 3 ## mean q1 q3 ## <dbl> <dbl> <dbl> ## 1 -10.2 -11.1 -9.37 ## 2 9.92 8.13 12.0 ## 3 -8.25 -11.3 -5.24 ## 4 -6.88 -8.15 -5.33 ## 5 -7.12 -9.33 -5.17 ## 6 -7.05 -8.54 -5.38 ## 7 4.93 3.44 6.22 ## 8 1.74 0.946 2.52 ## 9 -4.57 -6.14 -2.94 ## 10 9.12 5.77 12.1 ## # … with 90 more rows ``` --- ### Les boucles avec `{purrr}` : retourner un tableau Afin d'éviter de devoir écrire `function(x)`, il est possible d'utiliser un raccourci : `~`. L'élément courant auquel on applique une fonction est alors désigné par le symbole `.` : ```r simulations %>% map_df(~tibble( mean = mean(.), q1 = quantile(., probs = .25), q3 = quantile(., probs = .75) )) ``` ``` ## # A tibble: 100 × 3 ## mean q1 q3 ## <dbl> <dbl> <dbl> ## 1 -10.2 -11.1 -9.37 ## 2 9.92 8.13 12.0 ## 3 -8.25 -11.3 -5.24 ## 4 -6.88 -8.15 -5.33 ## 5 -7.12 -9.33 -5.17 ## 6 -7.05 -8.54 -5.38 ## 7 4.93 3.44 6.22 ## 8 1.74 0.946 2.52 ## 9 -4.57 -6.14 -2.94 ## 10 9.12 5.77 12.1 ## # … with 90 more rows ``` --- ### Les boucles avec `{purrr}` : retourner un tableau Notes : - dans le résultat final, les tableaux créés à chaque itération ont été collés par lignes. - c'est le cas puisque nous avons fait appel à la fonction `map_df()`. - Si en revanche on supprime le suffixe `_df()` de cette fonction, et que nous utilions uniquement `map()`, alors le résultat final sera rétribué dans une liste de la même longueur que celle donnée en _input_ à l'argument `.x` (c'est-à-dire `simulations` dans notre exemple) : ```r tmp <- simulations %>% map(~tibble( mean = mean(.), q1 = quantile(., probs = .25), q3 = quantile(., probs = .75)) ) length(tmp) ``` ``` ## [1] 100 ``` --- ### Les boucles avec `{purrr}` : retourner un tableau ```r tmp[[1]] ``` ``` ## # A tibble: 1 × 3 ## mean q1 q3 ## <dbl> <dbl> <dbl> ## 1 -10.2 -11.1 -9.37 ``` --- class: titre_2, center, middle ## 6.2. Tableaux de données en _input_ --- ### Tableaux de données en _input_ - Type de données en entrée : <b class="coul_1">tableau de données</b> - Argument `.f` de la fonction `map` : fonction (ou formule) qui sera appliquée <b class="coul_1">à chacune des colonnes du tableau de données</b> Exemple : moyenne des colonnes numériques dans `iris`: ```r iris %>% select_if(is.numeric) %>% map_df(mean) ``` ``` ## # A tibble: 1 × 4 ## Sepal.Length Sepal.Width Petal.Length Petal.Width ## <dbl> <dbl> <dbl> <dbl> ## 1 5.84 3.06 3.76 1.20 ``` --- ### Tableaux de données en _input_ Si la fonction à appliquer aux colonnes d'un tableau de données retourne <b class="coul_2">plusieurs valeurs</b> : deuxième appel à une fonction `map` sur le résultat d'une première Exemple : 1. test de normalité sur chacune des colonnes d'un tableau de données 2. extraction de la p-value associée au test de Shapiro-Wilk - `shapiro.test()` retourne un élément nommé `p.value` qui contient la p-value ```r iris %>% select_if(is.numeric) %>% map(shapiro.test) %>% map_dbl("p.value") ``` ``` ## Sepal.Length Sepal.Width Petal.Length Petal.Width ## 1.018116e-02 1.011543e-01 7.412263e-10 1.680465e-08 ``` --- ### Tableaux de données en _input_ : plusieurs fonctions à appliquer Il est possible d'appliquer plusieurs fonctions à chaque colonne du tableau de données - Le résultat pour chaque colonne peut être lui-même un tableau de données - Si ajout de l'argument `.id` à la fonction `map_df()`: le nom de la colonne à laquelle le calcul de la fonction `map_df()` est effectué est retourné dans une colonne dont le nom est la chaîne de caractères donné à `.id` ```r iris %>% select_if(is.numeric) %>% map_df(~tibble( mean = mean(.), q1 = quantile(., probs = .25), q3 = quantile(., probs = .75) ), .id = "variable" ) ``` ``` ## # A tibble: 4 × 4 ## variable mean q1 q3 ## <chr> <dbl> <dbl> <dbl> ## 1 Sepal.Length 5.84 5.1 6.4 ## 2 Sepal.Width 3.06 2.8 3.3 ## 3 Petal.Length 3.76 1.6 5.1 ## 4 Petal.Width 1.20 0.3 1.8 ``` --- ### Tableaux de données en _input_ : colonnes de facteurs - Fonctions à appliquer à des colonnes factorielles Exemple : obtenir le nombre de valeurs distincte pour chaque niveau : ```r iris %>% select_if(is.factor) %>% map_df(~tibble( n_dist = n_distinct(.), class = class(.x) ), .id = "variable" ) ``` ``` ## # A tibble: 1 × 3 ## variable n_dist class ## <chr> <int> <chr> ## 1 Species 3 factor ``` --- ### Tableaux de données en _input_ : colonnes de facteurs Astuce : pour connaître le type de données de chaque colonne, on peut faire appel à la fonction `map_chr()` en appelant la fonction `class()` sur chaque colonne : ```r iris %>% map_chr(class) ``` ``` ## Sepal.Length Sepal.Width Petal.Length Petal.Width Species ## "numeric" "numeric" "numeric" "numeric" "factor" ``` --- class: inverse, center, middle # 7. Les boucles avec `{base}` --- ## Les fonctions de la famille `apply` du _package_ `base` - Il existe des fonctions du même type que celles du _package_ `purrr`, dans le _package_ {`base`} ; - Elles sont cependant moins uniformisées dans leur syntaxe ; | Fonction | Input | Output | |:--------:|:-----:|:------:| | `apply()` | Matrice ou tableau | Vecteur ou tableau ou liste | | `lapply()` | Liste ou vecteur | Liste | | `sapply()` | Liste ou vecteur | Vecteur ou matrice ou liste | | `vapply()` | Liste ou vecteur | Vecteur ou matrice ou liste | | `tapply()` | Tableau et facteurs | Tableau ou liste | | `mapply()` | Listes et/ou vecteurs | Vecteur ou matrice ou liste | --- ### Les fonctions de la famille `apply` du _package_ `base` : `lapply()` - Applique une fonction à chaque élément de la liste ou du vecteur fourni en argument ; - Retourne le résultat sous une forme de liste. ```r lapply(X, FUN, ...) ``` - `X` : vecteur ou liste donné en argument ; - `FUN` : fonction à appliquer à chaque élément ; - `...` : permet de fournir des paramètres à des fonctions imbriquées dans `FUN`. --- ### Les fonctions de la famille `apply` du _package_ `base` : `lapply()` ```r liste <- list(normale = rnorm(10), logiques = c(TRUE, TRUE, FALSE), x = c(0, NA, 3)) ``` - Liste contenant la longueur des éléments de `liste` : ```r lapply(liste, length) ``` ``` ## $normale ## [1] 10 ## ## $logiques ## [1] 3 ## ## $x ## [1] 3 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `lapply()` - Calcul de la moyenne pour chaque élément ```r lapply(liste, mean, na.rm = TRUE) ``` ``` ## $normale ## [1] -0.3552211 ## ## $logiques ## [1] 0.6666667 ## ## $x ## [1] 1.5 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `lapply()` - Utilisation d'une fonction définie par l'utilisateur : ```r lapply(liste, function(x) x / mean(x, na.rm = TRUE)) ``` ``` ## $normale ## [1] 0.2236123 1.0414955 5.4238832 0.9699041 -0.7633706 -3.5233054 ## [7] -0.9357730 6.9474557 1.1839661 -0.5678680 ## ## $logiques ## [1] 1.5 1.5 0.0 ## ## $x ## [1] 0 NA 2 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `lapply()` - La fonction définie peut utiliser plusieurs arguments : ```r lapply(liste, function(x, y) x / mean(x, na.rm = TRUE) + y, y = 2) ``` ``` ## $normale ## [1] 2.223612 3.041495 7.423883 2.969904 1.236629 -1.523305 1.064227 ## [8] 8.947456 3.183966 1.432132 ## ## $logiques ## [1] 3.5 3.5 2.0 ## ## $x ## [1] 2 NA 4 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `lapply()` - Appliquer `lapply()` sur un `data.frame` (qui est une liste), permet d'appliquer une fonction sur chaque colonne de ce `data.frame` : - Par exemple, pour afficher le mode de chaque colonne du `data.frame` `cars` : ```r unlist(lapply(cars, class)) ``` ``` ## speed dist ## "numeric" "numeric" ``` - Ou encore pour calculer la moyenne pour chaque colonne : ```r unlist(lapply(cars, mean)) ``` ``` ## speed dist ## 15.40 42.98 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `lapply()` - Il arrive de vouloir rassembler le résultat fourni par `lapply()` dans un `data.frame` ; - La fonction `do.call()` permet de réaliser ce tour de passe-passe : ```r l <- lapply(1:3, function(x) cbind(valeur = x, lettre = LETTERS[x])) data.frame(do.call("rbind", l)) ``` ``` ## valeur lettre ## 1 1 A ## 2 2 B ## 3 3 C ``` - L’appel de `do.call("rbind", x)` revient à faire `rbind(x[1], x[2], ..., x[n])` avec `x` un vecteur de taile `n`. --- ### Les fonctions de la famille `apply` du _package_ `base` : `lapply()` - On peut également utiliser `bind_rows()` de {`dplyr`} en retournant un tableau à chaque itération plutôt qu'un vecteur : ```r l <- lapply(1:3, function(x) tibble(valeur = x, lettre = LETTERS[x])) bind_rows(l) ``` ``` ## # A tibble: 3 × 2 ## valeur lettre ## <int> <chr> ## 1 1 A ## 2 2 B ## 3 3 C ``` --- class: exo # Exercice 1. Créer une liste de longueur 5 dans laquelle chaque élément doit être composé d'un échantillon d'observations issues d'une loi Normale centrée réduite ; 2. Sur chaque échantillon de l'objet créé, calculer la moyenne et l'écart-type. Le résultat doit être sous forme d'un tableau de données. --- ### Les fonctions de la famille `apply` du _package_ `base` : `sapply()` - Applique une fonction à chaque élément de la liste ou du vecteur fourni en argument ; - Retourne le résultat sous une forme de liste, de vecteur ou de matrice. ```r sapply(X, FUN, ..., simplify, USE.NAMES) ``` - `X` : vecteur ou liste donné en argument ; - `FUN` : fonction à appliquer à chaque élément ; - `...` : permet de fournir des arguments à des fonctions imbriquées dans `FUN` ; - `simplify` : si `FALSE`, même résultat que `lapply()` ; sinon (par défaut), `R` tente de retourner le résultat sous une forme simplifiée : - si les résultats de la fonction `FUN` sont des scalaires : vecteur, - si les éléments retournés par la fonction `FUN` sont de même taille : matrice ; - `USE.NAMES` : si `TRUE`, et si `X` est de type `character`, utilise `X` comme nom pour le résultat (à moins que le résultat possède déjà des noms). --- ### Les fonctions de la famille `apply` du _package_ `base` : `sapply()` ```r (x <- list(a = 1:10, beta = exp(-3:3), logic = c(TRUE,FALSE,FALSE,TRUE))) ``` ``` ## $a ## [1] 1 2 3 4 5 6 7 8 9 10 ## ## $beta ## [1] 0.04978707 0.13533528 0.36787944 1.00000000 2.71828183 7.38905610 ## [7] 20.08553692 ## ## $logic ## [1] TRUE FALSE FALSE TRUE ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `sapply()` - Application de la fonction `quantile()` à chaque élément ; - Retourne la médiane et les quartiles ; - Avec `lapply()` : ```r lapply(x, quantile) ``` ``` ## $a ## 0% 25% 50% 75% 100% ## 1.00 3.25 5.50 7.75 10.00 ## ## $beta ## 0% 25% 50% 75% 100% ## 0.04978707 0.25160736 1.00000000 5.05366896 20.08553692 ## ## $logic ## 0% 25% 50% 75% 100% ## 0.0 0.0 0.5 1.0 1.0 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `sapply()` - Avec `sapply()` : ```r sapply(x, quantile) ``` ``` ## a beta logic ## 0% 1.00 0.04978707 0.0 ## 25% 3.25 0.25160736 0.0 ## 50% 5.50 1.00000000 0.5 ## 75% 7.75 5.05366896 1.0 ## 100% 10.00 20.08553692 1.0 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `sapply()` - Un exemple d'utilisation de `USE.NAMES` : ```r sapply(LETTERS[1:3], str_length) ``` ``` ## A B C ## 1 1 1 ``` ```r sapply(LETTERS[1:3], str_length, USE.NAMES = FALSE) ``` ``` ## [1] 1 1 1 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `vapply()` - `vapply()` similaire à `sapply()` ; - Possède un type de valeurs spécifiées ; - Utilisation plus sûre et parfois plus rapide ; - Si `data.frame` en entrée, même résultat que `sapply()` ; - Si liste vide en entrée, retourne un logique de longueur nulle. ```r vapply(X, FUN, FUN.VALUE, ..., USE.NAMES) ``` - `X` : vecteur ou liste donné en argument ; - `FUN` : fonction à appliquer à chaque élément ; - `FUN.VALUE` : vecteur masque pour la valeur retournée par `FUN` ; - `...` : permet de fournir des arguments à des fonctions imbriquées dans `FUN` ; - `USE.NAMES` : si `TRUE`, et si `X` est de type `character`, utilise `X` comme nom pour le résultat (à moins que le résultat possède déjà des noms). --- ### Les fonctions de la famille `apply` du _package_ `base` : `vapply()` ```r sapply(cars, is.numeric) ``` ``` ## speed dist ## TRUE TRUE ``` ```r vapply(cars, is.numeric, FUN.VALUE = logical(1)) ``` ``` ## speed dist ## TRUE TRUE ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `vapply()` - Avec la liste vide : ```r sapply(list(), is.numeric) ``` ``` ## list() ``` ```r vapply(list(), is.numeric, FUN.VALUE = logical(1)) ``` ``` ## logical(0) ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `apply()` - Appliquer une fonction à une partie d'un `data.frame` ; - Le résultat est un vecteur ou un tableau, ou encore une liste. ```r apply(X, MARGIN, FUN, ...) ``` - `X` : matrice ou `data.frame` donné en argument ; - `MARGIN` : si `1`, `FUN` est appliquée aux lignes, si `2`, `FUN` est appliqué aux colonnes ; - `FUN` : fonction à appliquer à chaque élément ; - `...` : permet de fournir des paramètres à des fonctions imbriquées dans `FUN`. --- ### Les fonctions de la famille `apply` du _package_ `base` : `apply()` ```r (X <- matrix(1:9, ncol = 3)) ``` ``` ## [,1] [,2] [,3] ## [1,] 1 4 7 ## [2,] 2 5 8 ## [3,] 3 6 9 ``` - Somme par ligne ```r apply(X, MARGIN = 1, sum) ``` ``` ## [1] 12 15 18 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `apply()` - Somme par colonne ```r apply(X, MARGIN = 2, sum) ``` ``` ## [1] 6 15 24 ``` - Fonction définie par l'utilisateur ```r apply(X, MARGIN = 1, function(x) sum(x) / sum(X)) ``` ``` ## [1] 0.2666667 0.3333333 0.4000000 ``` --- class: exo # Exercice 1. Importer dans `R` les données sur les salaires de professeurs d'Universités depuis la source suivante : <a href="http://data.princeton.edu/wws509/datasets/salary.dat">http://data.princeton.edu/wws509/datasets/salary.dat</a> (la première ligne contient les noms des variables) ; 2. Calculer le salaire (`sl`) moyen par sexe (`sx`) ; 3. Calculer le salaire (`sl`) moyen par sexe (`sx`) et niveau d'études (`dg`) ; 4. Même question, mais ajouter également le salaire médian. --- ### Les fonctions de la famille `apply` du _package_ `base` : `tapply()` - `tapply()` s'applique à chaque cellule d'un tableau ; - La fonction s'applique à des regroupements faits selon une liste de facteurs. ```r tapply(X,INDEX, FUN, ..., simplify) ``` - `X` : `data.frame` donné en argument ; - `INDEX` : liste d'un ou plusieurs facteurs, de même longueur que `X` ; - `FUN` : fonction à appliquer à chaque élément ; - `...` : permet de fournir des paramètres à des fonctions imbriquées dans `FUN` ; - `simplify` : si `TRUE` (par défaut), retourne un tableau de scalaires, si `FALSE`, le résultat est un tableau de mode `list`. --- ### Les fonctions de la famille `apply` du _package_ `base` : `tapply()` ```r head(iris) ``` ``` ## Sepal.Length Sepal.Width Petal.Length Petal.Width Species ## 1 5.1 3.5 1.4 0.2 setosa ## 2 4.9 3.0 1.4 0.2 setosa ## 3 4.7 3.2 1.3 0.2 setosa ## 4 4.6 3.1 1.5 0.2 setosa ## 5 5.0 3.6 1.4 0.2 setosa ## 6 5.4 3.9 1.7 0.4 setosa ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `tapply()` - Moyenne de la longueur des sépales par espèce : ```r tapply(iris$Sepal.Length, iris$Species, mean) ``` ``` ## setosa versicolor virginica ## 5.006 5.936 6.588 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `tapply()` - Moyenne de la longueur des sépales par espèce, résultat sous forme de liste : ```r tapply(iris$Sepal.Length, iris$Species, mean, simplify = FALSE) ``` ``` ## $setosa ## [1] 5.006 ## ## $versicolor ## [1] 5.936 ## ## $virginica ## [1] 6.588 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `mapply()` - `mapply()` applique une fonction à plusieurs listes ou vecteurs. ```r mapply(FUN, ..., MoreArgs, SIMPLIFY, USE.NAMES) ``` - `FUN` : fonction à appliquer aux vecteurs ou listes fournies (<em>via</em> `...`) ; - `MoreArgs` : liste de paramètres supplémentaires à fournir à la fonction à appliquer ; - `SIMPLIFY` : même usage que pour `sapply()` ; - `USE.NAMES` : même usage que pour `sapply()`. --- ### Les fonctions de la famille `apply` du _package_ `base` : `mapply()` ```r (l1 <- list(a = c(1:5), b = c(6:10))) ``` ``` ## $a ## [1] 1 2 3 4 5 ## ## $b ## [1] 6 7 8 9 10 ``` ```r (l2 <- list(c = c(11:15), d = c(16:20))) ``` ``` ## $c ## [1] 11 12 13 14 15 ## ## $d ## [1] 16 17 18 19 20 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `mapply()` - Somme des éléments correspondants de `l1` et `l2` : ```r mapply(sum, l1$a, l1$b, l2$c, l2$d) ``` ``` ## [1] 34 38 42 46 50 ``` --- ### Les fonctions de la famille `apply` du _package_ `base` : `mapply()` - Attention au recyclage silencieux ! ```r (l1 <- list(a = c(1:5), b = c(6:20))) ``` ``` ## $a ## [1] 1 2 3 4 5 ## ## $b ## [1] 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ``` ```r mapply(sum, l1$a, l1$b, l2$c, l2$d) ``` ``` ## [1] 34 38 42 46 50 39 43 47 51 55 44 48 52 56 60 ``` --- ### La fonction `Vectorize()` - `Vectorize()` permet de convertir une fonction scalaire en une fonction vectorielle ; - N'améliore cependant pa la rapidité d'exécution du code... - Mais son utilisation permet d'éviter des lignes de codes ! ```r Vectorize(FUN, vectorize.args, SIMPLIFY, USE.NAMES) ``` - `FUN` : fonction à appliquer à chaque élément ; - `vectorize.args` : vecteur d'arguments (de type `character`) qui devaient être vectorisés (par défaut, tous les arguments de `FUN`) ; - `SIMPLIFY` : même usage que pour `sapply()` ; - `USE.NAMES` : même usage que pour `sapply()`. --- ### La fonction `Vectorize()` - Une fonction scalaire simple : ```r f <- function(x = 1:3, y) c(x, y) ``` - On la "vectorise" : ```r vf <- Vectorize(f, SIMPLIFY = FALSE) ``` --- ### La fonction `Vectorize()` ```r f(1:3, 1:3) ``` ``` ## [1] 1 2 3 1 2 3 ``` ```r vf(1:3, 1:3) ``` ``` ## [[1]] ## [1] 1 1 ## ## [[2]] ## [1] 2 2 ## ## [[3]] ## [1] 3 3 ``` --- ### La fonction `Vectorize()` - Vectorise seulement y, pas x ```r vf(y = 1:3) ``` ``` ## [[1]] ## [1] 1 2 3 1 ## ## [[2]] ## [1] 1 2 3 2 ## ## [[3]] ## [1] 1 2 3 3 ```