class: center, middle, inverse, title-slide # Informatique : Fonctions ## Licence 3 Economie-Gestion - Parcours Economie-Finance ### Ewen Gallic ### Aix-Marseille Université ### 2020-2021 (mise à jour: 2020-11-05) --- class: inverse, center, middle # 1. Définition <br/><br/><br/> <div style='text-align: center;'> <img src = "images/fonctions/dilbert_agile_prog.gif" width = "700px" title = "http://dilbert.com/strips/comic/2007-11-26/" /> <br/> <span class = "lien_source">Source : <a href = "http://dilbert.com/strips/comic/2007-11-26/">http://dilbert.com/strips/comic/2007-11-26/</a></span> </div> --- ### Définition d'une fonction - La syntaxe est la suivante : ```r nom <- function(arguments) expression ``` - `nom` : nom donné à la fonction (attention à respecter les contraintes de nommage) ; - `arguments` : les paramètres de la fonction ; - `expression` : le corps de la fonction. --- ### Appel d'une fonction - Appel par son nom; - Paramètres passés entre parenthèses, juste derrière le nom de la fonction. ```r nom(argument_1, argument_2) ``` --- ### Exemple Créons la fonction `carre()`, qui retourne le carré d'un nombre. ```r carre <- function(x) x^2 carre(2) ``` ``` ## [1] 4 ``` ```r carre(-3) ``` ``` ## [1] 9 ``` --- class: exo # Exercice Créer une fonction qui, pour deux arguments `x` et `y` retourne le résultat suivant : `$$\frac{xy}{x^2 + y^2}.$$` Tester cette fonction pour les valeurs `\((x=0, y=0)\)`, puis `\((x=1, y=2)\)`. --- class: inverse, center, middle ## 2. La structure d'une fonction <br/><br/><br/> <div style='text-align: center;'> <img src = "images/fonctions/tintin_maths.jpg" width = "300px" title = "Tintin" /> <br/> <span class = "lien_source">Source : Tintin - L'étoile mystérieuse</span> </div> --- ### La structure d'une fonction Excepté pour les fonctions primitives du <em>package</em> `base`, les fonctions sont composées de trois parties : - une <b class = "coul_1">liste d'arguments</b> ; - un <b class = "coul_1">corps</b>, contenant du code exécuté lors de l’appel à la fonction ; - un <b class = "coul_1">environnement</b>, qui définit l'endroit où sont stockées les variables. L'accès (et la modification) peut se faire avec les fonctions : - `formals()` pour les paramètres ; - `body()` pour le corps ; - `environment()` pour l’environnement. --- class: titre_2, center, middle ## 2.1 Le corps de la fonction --- ### Le corps de la fonction - Une seule instruction dans le cas le plus simple ; - Entourer d'accolades pour mettre plusieurs instructions ; - Le <b class = "coul_1">résultat</b> est la valeur de la **dernière commande contenue** dans le corps de la fonction. ```r f <- function(x) { x^2 y <- x y } f(2) ``` ``` ## [1] 2 ``` --- ### Le corps de la fonction: `return()` - La fonction `return()` permet de retourner un résultat autre part qu'à la dernière ligne ; - Cela peut être utile dans le cas de conditions à l'intérieur du corps ; - Mais il est déconseillé d'utiliser `return()` en dernière ligne (inutile, rend la lecture du code plus pénible). ```r f <- function(x) { return(x^2) # Un commentaire de dernière ligne } f(2) ``` ``` ## [1] 4 ``` --- ### Le corps de la fonction: retourner plusieurs éléments - On peut retourner une liste, ce qui est extrêmement pratique (pas de conversion automatique !) ```r stat_des <- function(x) { list(moyenne = mean(x), ecart_type = sd(x)) } x <- runif(10) stat_des(x) ``` ``` ## $moyenne ## [1] 0.4097311 ## ## $ecart_type ## [1] 0.2474515 ``` --- ### Le corps de la fonction: résultat invisible - On peut éviter l'affichage du résultat dans la console en utilisant la fonction `invisible()` ; ```r stat_des_2 <- function(x) { invisible(list(moyenne = mean(x), ecart_type = sd(x))) } x <- runif(10) stat_des_2(x) str(stat_des_2(x)) ``` ``` ## List of 2 ## $ moyenne : num 0.639 ## $ ecart_type: num 0.209 ``` ```r stat_des_2(x)$moyenne ``` ``` ## [1] 0.6391139 ``` --- ### Le corps de la fonction: résultat invisible - L'affichage d'un résultat caché peut être forcé, en utilisant les parenthèses : ```r (stat_des_2(x)) ``` ``` ## $moyenne ## [1] 0.6391139 ## ## $ecart_type ## [1] 0.2093387 ``` --- ### Le corps d'une fonction : remarque - Si la dernière instruction est une assignation, le résultat est invisible ; - Mais la valeur d'assignation est bien retournée. ```r f <- function(x){ res <- x^2 } f(2) (f(2)) ``` ``` ## [1] 4 ``` ```r x <- f(2) x ``` ``` ## [1] 4 ``` --- class: titre_2, center, middle ## 2.2 Les arguments d'une fonction --- ### Les arguments d'une fonction - Jusqu'ici, un seul argument dans les fonctions ; - Si plusieurs : les séparer par une virgule. Exemple : soit la fonction de production `\(Y(L,K, M)\)`, qui dépend du nombre de travailleurs `\(L\)` et de la quantité de capital `\(K\)`, et du matériel `\(M\)`, telle que `\(Y(L,K, M) = L^{0.3}K^{0.5}M^{2}\)`. Cette fonction pourra s'écrire, en `R` de la manière suivante : ```r production <- function(l, k, m) l^(0.3) * k^(0.5) * m^(0.2) ``` --- class: exo # Exercice Soient deux points `\(x\)` et `\(y\)`, de coordonnées `\((x_1, x_2)\)` et `\((y_1, y_2)\)`, respectivement. Créer une fonction à deux arguments, `x` et `y`, qui retourne la distance euclidienne entre `x` et `y`, c'est-à-dire qui calcule : `$$d(x,y) = \sqrt{(x_1-y_1)^2 + (x_2-y_2)^2}$$` Tester cette fonction dans les 2 situations suivantes : 1. `x=c(0,0)` et `y=c(1,1)` 2. `x=c(1,1)` et `y=c(1,1)`. --- ### Appel sans nom Retournons à notre fonction de production. ```r production <- function(l, k, m) l^(0.3) * k^(0.5) * m^(0.2) ``` Si on nous donne les valeurs `\(L = 60\)` et `\(K = 42\)` et `\(M = 40\)`, on peut en déduire la production : ```r production(60, 42, 40) ``` ``` ## [1] 46.28945 ``` - Les arguments n'ont pas été nommés, `R` fait donc en fonction de l'ordre donné. --- ### Appel sans nom - `R` cherche d'abord les arguments nommés, puis complète en fonction de la position : ```r production(k = 42, m = 40, l = 60) ``` ``` ## [1] 46.28945 ``` ```r production(k = 42, 60, 40) ``` ``` ## [1] 46.28945 ``` --- ### Arguments effectifs - Argument <b class = "coul_1">formel</b> : argument de la fonction, utilisé dans le corps ; - Paramètre <b class = "coul_1">effectif</b> : valeur que l'on donne à l'argument formel ; - On peut donner une valeur par défaut à l'argument formel à l'aide du signe `=` ; - Si l'utilisateur ou l'utilisatrice omet cet argument lors de l'appel, l'argument prendra la valeur donnée par défaut ; - Sinon, la valeur attribuée lors de l'appel prévaudra. --- ### Arguments effectifs ```r # On propose de définir la valeur du capital à 42 par défaut production_2 <- function(l, m, k = 42) l^(0.3) * k^(0.5) * m^(0.2) production_2(l = 42, m = 40) ``` ``` ## [1] 41.59216 ``` ```r production_2(l = 42, m = 40, k = 2) ``` ``` ## [1] 9.076152 ``` --- class: exo # Exercice 1. Créer une fonction, qui étant donné un quantile `\(x\)`, une espérance `\(\mu\)` et un écart-type `\(\sigma\)`, retourne la densité de la loi normale. Comparer avec la fonction `dnorm()`. 2. Reprendre la fonction de la question 1, et donner les valeurs par défaut 0 et 1 pour l'espérance et l'écart-type respectivement. Rappel : la fonction de densité d'une loi normale est donnée par : `$$\varphi(x) = \frac{1}{\sigma\sqrt{2\pi}} e^{-\frac{1}{2}\left(\frac{x-\mu}{\sigma}\right)^2}.$$` --- ### Appel avec des noms partiels - `R` permet de ne pas saisir le nom complet des arguments ; - Cependant, il ne faut pas qu'il y ait d'ambiguïté ! ```r f <- function(premier, second, troisieme) premier + second + troisieme f(p = 1, s = 2, t = 3) ``` ``` ## [1] 6 ``` ```r # Problème d'ambiguïté f <- function(texte, nombre, nom) print(nom) f("hello", 2, no = 3) ``` ``` ## Error in f("hello", 2, no = 3): l'argument 3 correspond à plusieurs arguments formels ``` --- ### Appel avec des noms partiels Attention, si l'argument `...` figure dans la liste des arguments de la fonction que l'on utilise, le nommage partiel ne fonctionne pas. --- ### Fonctions sans arguments - Il est possible de créer une fonction sans arguments ```r f <- function() sample(letters, size = 10, replace = TRUE) ``` --- ### L'argument spécial `...` - L'argument `...` sert à indiquer que la fonction peut admettre d'autres arguments que ceux qui sont définis ; - Utile pour passer un argument à une fonction à l'intérieure d'une autre. ```r f <- function(...) names(list(...)) f(premier = 1, second = 2) ``` ``` ## [1] "premier" "second" ``` - Voir la fonction `sum()` par exemple. --- ### L'argument spécial `...` - Attention, une mauvaise orthographe est fatale quand le paramètre `...` est présent ! - L'argument mal écrit sera passé à `...` et aucune erreur ne sera retournée ; - On comprend mieux pourquoi l'abréviation n'est pas possible quand `...` est présent. ```r sum(3, NA, 4, na.rm = TRUE) ``` ``` ## [1] 7 ``` ```r sum(3, NA, 4, an.rm = TRUE) # Mauvaise écriture ``` ``` ## [1] NA ``` ```r sum(3, NA, 4, na = TRUE) # Abréviation ``` ``` ## [1] NA ``` --- class: titre_2, center, middle ## 2.3 La portée d'une fonction --- ### La portée d'une fonction - Lors de l'appel d'une fonction, le corps est interprété ; - Les variables créées, modifiées dans le corps ne vient qu'à l'intérieur de la fonction ; - On parle de <b class = "coul_1">portée</b> des variables ; - Une variable <b class = "coul_1">locale</b> (qui vit dans la fonction) peut avoir le même nom qu'une variable <b class = "coul_1">globale</b> (qui vit dans l'espace de travail) ; --- ### La portée d'une fonction ```r # Définition d'une variable globale valeur <- 1 # Définition d'une variable locale à la fonction f f <- function(x){ valeur <- 2 nouvelle_valeur <- 3 print(paste0("valeur vaut : ",valeur)) print(paste0("nouvelle_valeur vaut : ",nouvelle_valeur)) x + valeur } ``` --- ### La portée d'une fonction ```r f(3) ``` ``` ## [1] "valeur vaut : 2" ## [1] "nouvelle_valeur vaut : 3" ``` ``` ## [1] 5 ``` ```r # valeur n'a pas été modifiée valeur ``` ``` ## [1] 1 ``` ```r # nouvelle_valeur n'existe pas en dehors de f() nouvelle_valeur ``` ``` ## Error in eval(expr, envir, enclos): objet 'nouvelle_valeur' introuvable ``` --- ### La portée d'une fonction - Les variables sont définies dans des environnements ; - Ces environnements sont imbriqués les uns dans les autres ; - Si `R` ne trouve pas une variable dans l'environnement courant, il va chercher dans le (ou les) niveau(x) supérieur(s) ; - Ainsi, si une variable n'est pas définie dans le corps d'une fonction, `R` essaie de la trouver dans un environnement parent. ```r valeur <- 1 f <- function(x){ x + valeur } f(2) ``` ``` ## [1] 3 ``` --- ### La portée d'une fonction - Pour rendre accessible dans l'environnement global une variable créée dans le corps d'une fonction, on peut utiliser le symbole `<<-` ; - La fonction `assign()` est plus complète, puisqu'elle permet de choisir l'environnement d'affectation ; - Attention, cette manière de procéder n'est pas recommandable. --- ### La portée d'une fonction ```r rm(x) f <- function(x){ x <<- x + 1 } f(1) x ``` ``` ## [1] 2 ``` --- ### La portée d'une fonction ```r rm(x) f <- function(x){ # envir = .GlobalEnv signifie que que l'on veut définir dans l'environnement global assign(x = "x", value = x + 1, envir = .GlobalEnv) } f(4) x ``` ``` ## [1] 5 ``` --- class: inverse, center, middle ## 3. La documentation d'une fonction <br/><br/><br/> <div style='text-align: center;'> <img src = "images/fonctions/understand_nothing.gif" width = "300px" title = "Steve Carell" /> </div> --- ### La documentation d'une fonction - Documenter sa fonction pour faciliter son utilisation (par soi-même et par les autres) - Quelques règles simples issues de la création automatisée des _packages_ avec {`rroxygen`} - Ajouter avant la définition de la fonction un croisillon immédiatement suivi d'un guillemet simple (`#'`), puis sur plusieurs lignes, indiquer : - ce que fait la fonction - quels sont ses arguments (`@param`) - ce qu’elle retourne (`@return`) - des exemples d’utilisation --- ### La documentation d'une fonction: exemple ```r #' Calcul le niveau de production #' #' @param l montant de force de travail #' @param k montant de capital #' @param m montant de matériel #' @return Le niveau de production d'une Cobb-Doublas, fonction de \code{l}, \code{k} et \code{m} #' @examples #' production(k = 42, l = 60, m = 40) #' production(40, 20, 60) production <- function(l, k, m) { l^(0.3) * k^(0.5) * m^(0.2) } ``` --- class: exo # Exercice 1 : Modèle exponentiel Considérons un phénomène croissant que l'on modélise à l'aide d'un modèle logistique. La quantité que l'on modélise, `\(C\)` dépend du temps `\(t\)` et de trois paramètres : - `\(K\)` : la valeur de `\(C\)` quand `\(t\)` tend vers l'infini - `\(\tau\)` : la valeur du temps tel que `\(C(\tau) = K/2\)` (valeur de `\(t\)` au point milieu de la sigmoïde) - `\(r\)` : le taux de croissance La fonction est la suivante : $$ C(t) = \frac{K}{1+\exp\left(-r (t - \tau)\right)}$$ Créer une fonction en `R` qui retourne, en fonction du temps `t` et de valeurs pour les paramètres `K`, `r` et `t`, la valeur `C(t)`. Pour tester la fonction, utiliser par exemple les valeurs suivantes : `K = 155074`, `tau = 76.847 `, `r=0.094` pour une valeur de `t=10`. --- class: exo # Exercice 2 : Création de fonctions 1. Créer une fonction nommée `somme_n_entiers` qui retourne la somme des `\(n\)` premiers entiers. Son seul argument sera `n` ; 2.Utiliser la fonction `somme_n_entiers()` pour calculer la somme des 100 premiers entiers ; 3. Terminer la fonction par l'assignation du résultat dans un objet nommé `res`, puis évaluer l'expression suivante : `somme_n_entiers(100)`. Que peut-on constater ? 4. Charger les données `diamonds` du _package_ {`ggplot2`} dans la session `R` à l'aide de l'expression suivante : `data(diamonds, package = "ggplot2")` Créer une fonction que l'on appellera `prix_diamant_coupe()`, qui, quand on lui fournit la valeur de la coupe du diamant sous forme de caractères (`Fair`, `Good`, `Very Good`, `Premium`, ou `Ideal`), filtre le tableau de données `diamonds` pour ne conserver que les observations pour lesquelles la coupe du diamant correspond à celle indiquée en argument, et retourne le prix moyen des observations de la base ainsi filtrée ; --- class: exo # Exercice 2 : Création de fonctions 5. Reprendre le code de la fonction précédente, et le modifier pour retourner à présent une liste de deux éléments : (i) la moyenne des prix et (ii) l'écart-type ; 6. Créer la fonction `resume_diamant_coupe_couleur()`, qui pour une coupe et une couleur de diamant données, retourne une liste de deux éléments : (i) la moyenne des prix et (ii) l'écart-type pour les diamants possédant cette coupe et cette couleur (la couleur du diamant est une lettre allant de `J` pour les pires, à `D` pour les meilleurs). Tester la fonction pour la coupe `Fair` et la couleur `D` ; 7. Reprendre la fonction précédente, et lui attribuer la valeur `D` (en chaîne de caractères) comme argument effectif pour la couleur. Tester alors l'appel à la fonction en précisant : - la coupe `Fair` et la couleur `D`, - la coupe `Fair`, mais pas d'argument pour la couleur, - la coupe `Fair` et la couleur `E`, - la coupe non précisée mais la couleur `E` ; --- class: exo # Exercice 3 : Création de fonctions Supposons que les adresses e-mails des étudiant•e•s d'Aix-Marseille Université sont constituées de la manière suivante : le prénom et le nom de famille séparés par un point, le symbole arobase et le enfin le nom de domaine. Supposons de plus que les étudiant•e•s ont un seul prénom, et aucune particule au nom de famille. La syntaxe des adresses e-mail est donc comme suit : `nom.prenom@etu.univ-amu.fr`. `emails <- c("marie.petit@etu.univ-amu.fr", "jean.dupont@etu.univ-amu.fr", "isabelle.martinez@etu.univ-amu.fr", "pierre.moreau@etu.univ-amu.fr")` Créer une fonction, qui à partir d'**une seule** adresse e-mail d'un•e étudiant•e, retourne un tibble contenant trois variables : le prénom, le nom et l'adresse e-mail de cet•te étudiant•e.