R
- STID 2ème annéeDans ce TP, nous allons aborder la création de fonction en R
.
On utilise l’opérateur function()
pour déclarer sa propre fonction. Celle-ci peut prendre autant de paramètres que nécessaire. Pour retourner une valeur, on utilise l’opérateur return()
. Pour créer une procédure, il suffit de déclarer une fonction qui ne retourne aucune valeur.
Voici un exemple de déclaration d’une procédure, qui affiche un texte simple (grâce à la fonction cat()
).
afficheBonjour <- function() {
cat("Bonjour\n")
}
afficheBonjour()
Bonjour
Et voici l’exemple d’une fonction, renvoyant le carré de \(\pi\).
pi2 <- function() {
res = pi ** 2
return(res)
}
a = pi2()
cat("Pi au carré = ", a, "\n")
Pi au carré = 9.869604
On peut donc passer un paramètre, en le nommant avec les mêmes contraintes que pour un nom de variable classique.
afficheBonjour <- function(prenom) {
cat("Bonjour", prenom, "\n")
}
afficheBonjour("FX")
Bonjour FX
Par contre, il est absolument nécessaire de donner une valeur à chaque paramètre de la fonction définie.
afficheBonjour()
Error in cat("Bonjour", prenom, "\n"): l'argument "prenom" est manquant, avec aucune valeur par défaut
De même que pour une procédure, on peut passer des paramètres à une fonction.
puissance <- function(a, b) {
res = a ** b
return(res)
}
a = puissance(2, 4)
cat("a =", a, "\n")
a = 16
On remarque ici que la variable a
à l’intérieur de la fonction est indépendante de la variable a
précédemment déclarée. On dit que la portée d’une variable d’une fonction est limitée à celle-ci.
Dans la suite, les exemples seront soit une procédure, soit une fonction, car le fonctionnement est généralement le même.
Quand on déclare une fonction à plusieurs paramètres, il est possible de préciser à quel paramètre on affecte chaque valeur passée. Dans la suite, nous déclarons une fonction à 2 paramètres (nom
et prenom
).
afficheBonjour <- function(nom, prenom) {
cat("Bonjour ", prenom, " ", nom, "\n")
}
afficheBonjour("Jollois", "FX") # ordre par défaut
Bonjour FX Jollois
afficheBonjour(nom = "Jollois", prenom = "FX") # idem mais en précisant le nom des paramètres
Bonjour FX Jollois
afficheBonjour(prenom = "FX", nom = "Jollois") # en modifiant l'ordre de passage des paramètres
Bonjour FX Jollois
On peut aussi définir des valeurs par défaut aux paramètres. Ces valeurs seront celles prises par le paramètre si, lors de l’appel, la valeur de celui-ci n’est pas défini. On reprend l’exemple suivant en fixant une valeur par défaut pour le prénom.
afficheBonjour <- function(nom, prenom = "??") {
cat("Bonjour ", prenom, " ", nom, "\n")
}
afficheBonjour("Jollois", "FX") # rien de changé par raport à avant
Bonjour FX Jollois
afficheBonjour("Jollois")
Bonjour ?? Jollois
...
)Lorsqu’on définit une fonction qui en utilise une autre, on peut permettre le passage de paramètres (nommés onligatoirement) à cette sous-fonction. Par exemple, nous créons une fonction qui va afficher la moyenne d’un vecteur. Mais si ce vecteur contient des NA
, la fonction mean()
renverra NA
. Nous allons donc permettre de passer le paramètre na.rm
dans la fonction mean()
.
afficheMoyenne <- function(x, ...) {
m = mean(x, ...)
cat("Moyenne =", m, "\n")
}
afficheMoyenne(c(3, 9, 1, 7, 12))
Moyenne = 6.4
afficheMoyenne(c(3, 9, 1, NA, 12))
Moyenne = NA
afficheMoyenne(c(3, 9, 1, NA, 12), na.rm = TRUE)
Moyenne = 6.25
lapply
et associésOn manipule en R
des listes sans le savoir, avec les data.frames
.
class(mtcars)
[1] "data.frame"
is.list(mtcars)
[1] TRUE
Mais on peut aussi bien évidemment les créer ex-nihilo. Celles-ci peuvent contenir des objets de tout type, même des sous-listes.
l = list(a = "chaîne",
b = 12,
c = 1:10,
d = head(mtcars),
e = list(x = 1:10, y = log(1:10)))
length(l)
[1] 5
names(l)
[1] "a" "b" "c" "d" "e"
l[1]
$a
[1] "chaîne"
l[[1]]
[1] "chaîne"
l$a
[1] "chaîne"
l[1:2]
$a
[1] "chaîne"
$b
[1] 12
l[c("a", "c")]
$a
[1] "chaîne"
$c
[1] 1 2 3 4 5 6 7 8 9 10
l[sapply(l, length) == 1]
$a
[1] "chaîne"
$b
[1] 12
lapply
et autresLa fonction lapply()
permet d’exécuter une fonction (passée en deuxième paramètre) à chaque élément d’une liste (passée en premier paramètre), ou un vecteur. Il existe aussi les fonctions sapply()
et vapply()
, qui sont similaires mais qui cherchent à simplifier le résultat (la deuxième permet de spécifier le nom des retours de la fonctions, si ceux-ci sont multiples).
lapply(l, class)
$a
[1] "character"
$b
[1] "numeric"
$c
[1] "integer"
$d
[1] "data.frame"
$e
[1] "list"
sapply(l, class)
a b c d e
"character" "numeric" "integer" "data.frame" "list"
On a parfois (voire souvent) besoin d’utiliser une fonction spécifique dans les fonctions comme lapply()
. On peut soit la définir avant et l’utiliser comme une autre.
infoElement <- function(e) {
return(c(classe = class(e), longueur = length(e)))
}
lapply(l, infoElement)
$a
classe longueur
"character" "1"
$b
classe longueur
"numeric" "1"
$c
classe longueur
"integer" "10"
$d
classe longueur
"data.frame" "11"
$e
classe longueur
"list" "2"
sapply(l, infoElement)
a b c d e
classe "character" "numeric" "integer" "data.frame" "list"
longueur "1" "1" "10" "11" "2"
vapply(l, infoElement, c(CLASSE = "", LONG = ""))
a b c d e
CLASSE "character" "numeric" "integer" "data.frame" "list"
LONG "1" "1" "10" "11" "2"
Mais puisqu’on ne l’utilise généralement que dans cette fonction, il est possible de la déclarer directement dans la fonction lapply()
. On parle alors de fonction anonyme (comme en JavaScript par exemple).
sapply(l, function(e) {
return(c(classe = class(e), longueur = length(e)))
})
a b c d e
classe "character" "numeric" "integer" "data.frame" "list"
longueur "1" "1" "10" "11" "2"
On a parfois besoin d’appliquer une fonction qui ne retourne rien à une liste, par exemple pour afficher l’élément ou une partie de celui-ci. Dans l’exemple ci-dessous, on remarque que le code affiche bien chaque élément, mais renvoie aussi une liste contenant les éléments (qui est donc identique à la liste passée en paramètre). Ce comportement est dû au fait que la fonction ne renvoie pas de résultat.
lapply(l, function (e) { print(e); })
[1] "chaîne"
[1] 12
[1] 1 2 3 4 5 6 7 8 9 10
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
$x
[1] 1 2 3 4 5 6 7 8 9 10
$y
[1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
[8] 2.0794415 2.1972246 2.3025851
$a
[1] "chaîne"
$b
[1] 12
$c
[1] 1 2 3 4 5 6 7 8 9 10
$d
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
$e
$e$x
[1] 1 2 3 4 5 6 7 8 9 10
$e$y
[1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
[8] 2.0794415 2.1972246 2.3025851
Dans ce type de cas, si on veut éviter ce comportement, on peut utiliser la fonction invisibile()
. Ceci va rendre invisible l’exécution du code et on ne verra donc pas la liste retournée par lapply()
.
invisible(lapply(l, function (e) { print(e); }))
[1] "chaîne"
[1] 12
[1] 1 2 3 4 5 6 7 8 9 10
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
$x
[1] 1 2 3 4 5 6 7 8 9 10
$y
[1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
[8] 2.0794415 2.1972246 2.3025851
Il est possible de faire une recherche dans une liste (ou un vecteur) avec les fonctions Find()
et Position()
. Celles-ci renvoient le premier élément trouvé (ou le dernier car il est possible de partir de la droite). La fonction passée en premier paramètre doit renvoyer les valeurs TRUE
ou FALSE
.
On cherche par exemple ici le premier (ou dernier) élément de type vector
dans la liste précédemment créée.
Find(is.vector, l)
[1] "chaîne"
Find(is.vector, l, right = TRUE)
$x
[1] 1 2 3 4 5 6 7 8 9 10
$y
[1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
[8] 2.0794415 2.1972246 2.3025851
Position(is.vector, l)
[1] 1
Position(is.vector, l, right = TRUE)
[1] 5
Pour récupérer tous les éléments d’une liste respectant une condition (grâce à la fonction passée en paramètre donc), on dispose de la fonction Filter()
. Nous récupérons ici tous les éléments de la liste qui sont de type vector
.
Filter(is.vector, l)
$a
[1] "chaîne"
$b
[1] 12
$c
[1] 1 2 3 4 5 6 7 8 9 10
$e
$e$x
[1] 1 2 3 4 5 6 7 8 9 10
$e$y
[1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
[8] 2.0794415 2.1972246 2.3025851
On peut opérer une opération de réduction d’une liste à l’aide d’une fonction binaire (à deux paramètres donc).
Reduce(function(a, b) return(a + b), 1:5, 0)
[1] 15
Pour fonctionner correctement, la fonction doit retourner un objet utilisable dans la fonction. Dans l’exemple ci-dessous, nous transformons mtcars
en une liste de 32 éléments, chacune étant une liste nommée des caractéristiques de la voiture (avec en plus le nom de celle-ci).
mt = lapply(1:nrow(mtcars), function(i) {
return(c(nom = rownames(mtcars)[i], as.list(mtcars[i,])))
})
unlist(mt[[1]]) # unlist() utilisé pour améliorer l'affichage
nom mpg cyl disp hp drat
"Mazda RX4" "21" "6" "160" "110" "3.9"
wt qsec vs am gear carb
"2.62" "16.46" "0" "1" "4" "4"
Imaginons qu’on souhaite faire la somme des consommations, il nous faut créer une liste initiale avec la valeur 0
pour l’élément mpg
. Ensuite, on additionne les deux valeurs qu’on stocke dans a
(qui aura pour première valeur init
) et on retourne celle-ci.
init = list(mpg = 0)
Reduce(function(a, b) { a$mpg = a$mpg + b$mpg; return(a)}, mt, init)
$mpg
[1] 642.9
Créer une fonction affichePays()
qui prend en paramètre un nom de pays et un continent, et qui l’affiche sur une ligne :
pays (continent)
NA
par défaut) =
NA
, alors on afficherapays (continent)
pays (continent - indépendance en XXXX)
f(10, c(1, 2, 3))
renverra .6
world
A partir des données présentes dans le fichier world-liste.RData
, répondre aux questions suivantes. Ces données concernent les pays dans le monde (à la fin du siècle dernier), et sont représentées sous forme de liste dans l’objet Country.liste
.
affichePays()
créée précédemment dans une fonction anonyme, pour afficher les informations des pays (un pays par ligne)1
avec la nouvelle liste créée