IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Cours complet pour débutants pour apprendre la programmation en R


précédentsommairesuivant

6. Concepts avancés

Objectifs du chapitre

  • Passer des valeurs à une fonction via l'argument ....
  • Effectuer des sommaires sur des tableaux à l'aide de la fonction apply.
  • Réduire des listes avec les fonctions lapply, sapply et mapply ; comparer l'effet de ces fonctions.
  • Concevoir comment la classe d'un objet peut modifier le traitement qu'en feront les fonctions génériques.

Ce chapitre traite de divers concepts et fonctions un peu plus avancés du langage R, dont les fonctions de la famille apply auxquelles nous avons fait référence à quelques reprises dans les chapitres précédents. Ce sont des fonctions d'une grande importance en R.

Énoncé du problème

Soit la fonction
kitxmlcodeinlinelatexdvpg(x) = \left\{\begin{matrix} 2x,& x\le5\\ 0,& \mathrm{ailleurs} \end{matrix}\right.finkitxmlcodeinlinelatexdvp
On essaie d'en faire une mise en œuvre en R :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
> g <- function(x)
+ {
+     if (x <= 5)
+         2 * x
+     else
+         0
+ }

On évalue kitxmlcodeinlinelatexdvpg(1)finkitxmlcodeinlinelatexdvp. On devrait obtenirkitxmlcodeinlinelatexdvp2 \times 1 = 2finkitxmlcodeinlinelatexdvp (cas kitxmlcodeinlinelatexdvpx\le5finkitxmlcodeinlinelatexdvp).

 
Sélectionnez
> g(1)
[1] 2

On évalue kitxmlcodeinlinelatexdvpg(10)finkitxmlcodeinlinelatexdvp. On devrait obtenir 0 (cas kitxmlcodeinlinelatexdvpx>5finkitxmlcodeinlinelatexdvp).

 
Sélectionnez
> g(10)
[1] 0

On désire maintenant évaluer la fonction à tous les entiers de 1 à 10, inclusivement. On devrait obtenir (2, 4, 6, 8, 10, 0, 0, 0, 0, 0).

 
Sélectionnez
1.
2.
3.
4.
5.
> g(1:10)
[1] 2 4 6 8 10 12 14 16 18 20
Warning message:
In if (x <= 5) 2 * x else 0 :
  la condition a une longueur > 1 et seul le premier élément est utilisé

Que se passe-t-il ? Les résultats sont erronés et le message d'avertissement nous indique que seulement la première valeur a été utilisée pour évaluer la condition. La fonction g évalue donc correctement son argument seulement lorsqu'il s'agit d'un vecteur de longueur 1. En d'autres termes, la fonction n'est pas vectorielle.

6-1. Argument '...'

La mention ... apparaît dans la définition de plusieurs fonctions en R. Il ne faut pas voir là de la paresse de la part des rédacteurs des rubriques d'aide, mais bel et bien un argument formel dont ... est le nom.

  • Cet argument signifie qu'une fonction peut accepter un ou plusieurs arguments autres que ceux faisant partie de sa définition.
  • Le contenu de l'argument ... n'est ni pris en compte, ni modifié par la fonction. Il est généralement simplement passé tel quel à une autre fonction qui, elle, saura traiter les arguments qui lui sont ainsi passés.
  • Pour des exemples, voir les définitions des fonctions apply, lapply et sapply, ci-dessous.

6-2. Fonction apply

La Image non disponiblefonction apply sert à appliquer une fonction quelconque sur une partie d'une matrice ou, plus généralement, d'un tableau. La syntaxe de la fonction est la suivante :

 
Sélectionnez
apply(X, MARGIN, FUN, ...)

  • X est une matrice ou un tableau ;
  • MARGIN est un vecteur d'entiers contenant la ou les dimensions de la matrice ou du tableau sur lesquelles la fonction doit s'appliquer ;
  • FUN est la fonction à appliquer ;
  • ... est un ensemble d'arguments supplémentaires, séparés par des virgules, à passer à la fonction FUN.

Lorsque X est une matrice, apply sert principalement à calculer des sommaires par ligne (dimension 1) ou par colonne (dimension 2) autres que la somme ou la moyenne (puisque les fonctions rowSums, colSums, rowMeans et colMeans existent pour ce faire).

  • Utiliser la fonction apply plutôt que des boucles puisque celle-ci est plus efficace.
  • Considérer les exemples suivants :
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
> (x <- matrix(sample(1:100, 20, rep = TRUE), 5, 4))
     [,1] [,2] [,3] [,4]
[1,]   27   90   21   50
[2,]   38   95   18   72
[3,]   58   67   69  100
[4,]   91   63   39   39
[5,]   21    7   77   78
> apply(x, 1, var) # variance par ligne
[1]  978.000 1181.583  335.000  612.000 1376.917
> apply(x, 2, min) # minimum par colonne
[1]  21   7  18  39
> apply(x, 1, mean, trim = 0.2) # moy. tronquée par ligne
[1] 47.00 55.75 73.50 58.00 45.75

Puisqu'il n'existe pas de fonctions internes pour effectuer des sommaires sur des tableaux, il faut toujours utiliser la fonction apply.

  • Si X est un tableau de plus de deux dimensions, alors l'argument passé à FUN peut être une matrice ou un tableau.
  • Lorsque X est un tableau à trois dimensions et que MARGIN est de longueur 1, cela équivaut à appliquer la fonction FUN sur des « tranches » (des matrices) de X. Si MARGIN est de longueur 2, on applique FUN sur des « carottes » (des vecteurs) tirées de X.
  • Truc mnémotechnique : la ou les dimensions absentes de MARGIN sont celles qui disparaissent après le passage de apply.
  • Considérer les exemples suivants :
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
> (x <- array(sample(1:10, 80, rep = TRUE), c(3, 3, 4)))
, , 1
     [,1] [,2] [,3]
[1,]   10    2    1
[2,]    3    3    4
[3,]    7    4    9
, , 2
     [,1] [,2] [,3]
[1,]    4    5    7
[2,]    5    2    8
[3,]    6    9    2
, , 3
     [,1] [,2] [,3]
[1,]    8    7    6
[2,]    5    8    8
[3,]    9    6    1
, , 4
     [,1] [,2] [,3]
[1,]    5    5    3
[2,]    8    9    1
[3,]    7    5    1
> apply(x, 3, det) # déterminants matrices 3 x 3
[1] 103 149 -103 -54
> apply(x, 1, sum) # sommes tranches horizontales
[1] 63 64 66
> apply(x, c(1, 2), sum) # sommes carottes horizontales
     [,1] [,2] [,3]
[1,]   27   19   17
[2,]   21   22   21
[3,]   29   24   13
> apply(x, c(1, 3), sum) # sommes carottes transversales
     [,1] [,2] [,3] [,4]
[1,]   13   16   21   13
[2,]   10   15   21   18
[3,]   20   17   16   13
> apply(x, c(2, 3), sum) # sommes carottes verticales
     [,1] [,2] [,3] [,4]
[1,]   20   15   22   20
[2,]    9   16   21   19
[3,]   14   17   15    5

6-3. Fonctions lapply et sapply

Les fonctions lapply et sapply sont similaires à la fonction apply en ce qu'elles permettent d'appliquer une fonction aux éléments d'une structure — le vecteur ou la liste en l'occurrence. Leur syntaxe est similaire :

 
Sélectionnez
lapply(X, FUN, …)
sapply(X, FUN, ...)
  • La fonction lapply applique une fonction FUN à tous les éléments d'un vecteur ou d'une liste X et retourne le résultat sous forme de liste. Le résultat est donc :

     
    Sélectionnez
    list(FUN(X[[1]], ...),
         FUN(X[[2]], ...),
         FUN(X[[3]], ...),
         ...)
  • Les éléments de X sont passés comme à la fonction FUN sans être nommés. Les règles habituelles d'évaluation d'un appel de fonction s'appliquent. Par conséquent, les éléments de X seront considérés comme les premiers arguments de FUN à moins que des arguments nommés dans ... aient préséance.

  • Par exemple, on crée une liste formée de quatre vecteurs aléatoires de taille 5, 6, 7 et 8 :

     
    Sélectionnez
    1.
    2.
    3.
    4.
    5.
    6.
    7.
    8.
    9.
    > (x <- lapply(5:8, sample, x = 1:10))
    [[1]]
    [1] 7 4 3 10 9
    [[2]]
    [1] 3 2 4 7 8 5
    [[3]]
    [1] 8 4 9 2 1 10 6
    [[4]]
    [1] 5 6 8 4 3 1 7 2
    

    Le premier argument de la fonction sample est x. Dans l'expression ci-dessus, cet argument est passé à la fonction via l'argument ... de lapply. Par conséquent, les valeurs successives de 5:8 servent comme deuxième argument à la fonction sample, soit la taille de l'échantillon.

  • On peut ensuite calculer la moyenne de chacun des vecteurs obtenus ci-dessus, toujours sans faire de boucle :

     
    Sélectionnez
    1.
    2.
    3.
    4.
    5.
    6.
    7.
    8.
    9.
    > lapply(x, mean)
    [[1]]
    [1] 6.6
    [[2]]
    [1] 4.833333
    [[3]]
    [1] 5.714286
    [[4]]
    [1] 4.5
    

    La fonction sapply est similaire à lapply, sauf que le résultat est retourné sous forme de vecteur, si possible. Le résultat est donc simplifié par rapport à celui de lapply, d'où le nom de la fonction.

  • Dans l'exemple ci-dessus, il est souvent plus utile d'obtenir les résultats sous la forme d'un vecteur :

     
    Sélectionnez
    > sapply(x, mean)
    [1] 6.600000 4.833333 5.714286 4.500000
  • Si le résultat de chaque application de la fonction est un vecteur et que les vecteurs sont tous de la même longueur, alors sapply retourne une matrice, remplie comme toujours par colonne :
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
> (x <- lapply(rep(5, 3), sample, x = 1:10))
[[1]]
[1] 6 2 9 5 4
[[2]]
[1] 1 10 6 7 4
[[3]]
[1] 6 5 8 4 9
> sapply(x, sort)
     [,1] [,2] [,3]
[1,]    2    1    4
[2,]    4    4    5
[3,]    5    6    6
[4,]    6    7    8
[5,]    9   10    9

Dans un grand nombre de cas, il est possible de remplacer les boucles for par l'utilisation de lapply ou sapply. On ne saurait donc trop insister sur l'importance de ces fonctions.

La fonction sapply permet de vectoriser une fonction R qui n'est pas vectorielle. On peut procéder de deux façons : en utilisant la fonction non vectorielle dans une application de sapply() ou en vectorisant la fonction à y ajoutant un appel à sapply.

6-4. Fonction mapply

La fonction mapply est une version multidimensionnelle de sapply. Sa syntaxe est, essentiellement,

 
Sélectionnez
mapply(FUN, ...)
  • Le résultat de mapply est l'application de la fonction FUN aux premiers éléments de tous les arguments contenus dans ..., puis à tous les seconds éléments, et ainsi de suite.
  • Ainsi, si v et w sont des vecteurs, mapply(FUN, v, w) retourne sous forme de liste, de vecteur ou de matrice, selon le cas, FUN(v[1], w[1]), FUN(v[2], w[2]), etc.
  • Par exemple :

     
    Sélectionnez
    1.
    2.
    3.
    4.
    5.
    6.
    7.
    8.
    9.
    > mapply(rep, 1:4, 4:1)
    [[1]]
    [1] 1 1 1 1
    [[2]]
    [1] 2 2 2
    [[3]]
    [1] 3 3
    [[4]]
    [1] 4
    
  • Les éléments de '...' sont recyclés au besoin.
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
> mapply(seq, 1:6, 6:8)
[[1]]
[1] 1 2 3 4 5 6
[[2]]
[1] 2 3 4 5 6 7
[[3]]
[1] 3 4 5 6 7 8
[[4]]
[1] 4 5 6
[[5]]
[1] 5 6 7
[[6]]
[1] 6 7 8

6-5. Fonction replicate

La fonction replicate est une fonction enveloppante de sapply simplifiant la syntaxe pour l'exécution répétée d'une expression.

  • Son usage est particulièrement indiqué pour les simulations. Ainsi, on peut construire une fonction fun qui fait tous les calculs d'une simulation, puis obtenir les résultats pour, disons, 10 000 simulations avec

     
    Sélectionnez
    > replicate(10000, fun(...))
  • L'annexe CPlanification d'une simulation en R présente en détail différentes stratégies — dont l'utilisation de la fonction replicate — pour la réalisation d'études de simulation en R.

6-6. Classes et fonctions génériques

Dans le langage R, tous les objets ont une classe. La classe est parfois implicite ou dérivée du mode de l'objet (consulter la rubrique d'aide de class pour de plus amples détails).

  • Certaines fonctions, dites fonctions génériques, se comportent différemment selon la classe de l'objet donné en argument. Les fonctions génériques les plus fréquemment employées sont print, plot et summary.
  • Une fonction générique possède une méthode correspondant à chaque classe qu'elle reconnaît et, généralement, une méthode default pour les autres objets. La liste des méthodes existant pour une fonction générique s'obtient avec la fonction methods :

     
    Sélectionnez
    1.
    2.
    3.
    4.
    5.
    6.
    7.
    8.
    9.
    10.
    11.
    12.
    13.
    14.
    15.
    16.
    17.
    > methods(plot)
     [1] plot.acf*           plot.data.frame*
     [3] plot.decomposed.ts* plot.default
     [5] plot.dendrogram*    plot.density*
     [7] plot.ecdf           plot.factor*
     [9] plot.formula*       plot.function
    [11] plot.hclust*        plot.histogram*
    [13] plot.HoltWinters*   plot.isoreg*
    [15] plot.lm*            plot.medpolish*
    [17] plot.mlm*           plot.ppr*
    [19] plot.prcomp*        plot.princomp*
    [21] plot.profile.nls*   plot.raster*
    [23] plot.spec*          plot.stepfun
    [25] plot.stl*           plot.table*
    [27] plot.ts             plot.tskernel*
    [29] plot.TukeyHSD*
    see ' ?methods' for accessing help and source code
    
  • À chaque méthode methode d'une fonction générique fun correspond une fonction fun.methode. C'est donc la rubrique d'aide de cette dernière fonction qu'il faut consulter au besoin, et non celle de la fonction générique, qui contient en général peu d'informations.

  • Il est intéressant de savoir que lorsque l'on tape le nom d'un objet à la ligne de commande pour voir son contenu, c'est la fonction générique print qui est appelée. On peut donc complètement modifier la représentation à l'écran du contenu d'un objet en créant une nouvelle classe et une nouvelle méthode pour la fonction print.

Solution du problème

Débutons par expliquer ce qui cloche avec notre fonction g de départ :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
> g
function(x)
{
    if (x <= 5)
        2 * x
    else
        0
}

Il s'avère que la structure de contrôle if n'est pas une fonction vectorielle. Elle n'accepte qu'une seule valeur. De la rubrique d'aide :

 
Sélectionnez
cond: A length-one logical vector that is not 'NA'.
      Conditions of length greater than one are accepted
      with a warning, but only the first element is used.

Par conséquent, lorsque l'argument de la fonction g est 1:10, l'exécution de la fonction est la suivante :

  1. L'argument x vaut 1:10 ;
  2. La condition if (x <= 5) n'est évaluée que pour la première valeur de x ;
  3. Puisque x[1] <= 5, c'est l'expression vectorielle 2 * x qui est évaluée ;
  4. Le résultat de la fonction est 2 * x.

Comme indiqué précédemment, la fonction sapply peut rendre vectorielles une fonction ou une expression qui ne le sont pas. Ici, la solution la plus simple à court terme serait :

 
Sélectionnez
> sapply(1:10, g)
[1] 2 4 6 8 10 0 0 0 0 0

Une solution plus complète consisterait à transformer la fonction g pour la rendre vectorielle avec la fonction sapply :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
> g2 <- function(x)
+ {
+     h <- function(x)
+     {
+         if (x <= 5)
+             2 * x
+         else
+             0
+     }
+     sapply(x, h)
+ }
> g2(1:10)
[1] 2 4 6 8 10 0 0 0 0 0

Attention, toutefois, cette solution n'est pas une panacée. Par exemple, dans ce cas bien précis où la fonction est une fonction en branches, une autre solution fait appel à la fonction ifelse :

 
Sélectionnez
1.
2.
3.
4.
> g3 <- function(x)
+     ifelse (x <= 5, 2 * x, 0)
> g3(1:10)
[1] 2 4 6 8 10 0 0 0 0 0

Pour le problème sous étude, on peut faire encore beaucoup mieux en tirant directement profit de l'approche vectorielle de R. Par une judicieuse utilisation des valeurs booléennes, il est possible d'éliminer complètement la condition if :

 
Sélectionnez
1.
2.
3.
4.
> g4 <- function(x)
+     2 * x * (x <= 5)
> g4(1:10)
[1] 2 4 6 8 10 0 0 0 0 0

On préférera, dans l'ordre, les fonctions g4, g3 (la fonction ifelse est lente, mais néanmoins plus rapide que sapply), g2 et l'approche combinant sapply et la fonction g de départ.

6-7. Exemples

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
291.
292.
293.
294.
295.
296.
297.
298.
299.
300.
301.
302.
303.
304.
305.
306.
307.
308.
309.
310.
311.
312.
313.
314.
315.
316.
317.
318.
319.
320.
321.
322.
323.
324.
325.
326.
327.
328.
329.
330.
331.
332.
333.
334.
335.
336.
337.
338.
339.
340.
341.
342.
343.
344.
345.
346.
347.
348.
349.
350.
351.
352.
353.
354.
355.
356.
357.
358.
359.
360.
361.
362.
363.
364.
365.
366.
367.
368.
369.
370.
371.
372.
373.
374.
375.
376.
377.
378.
379.
380.
381.
382.
383.
384.
385.
386.
387.
388.
389.
390.
391.
392.
393.
394.
395.
396.
397.
398.
399.
400.
401.
402.
403.
404.
405.
406.
407.
408.
409.
410.
###
### FONCTION 'apply'
###

## Création d'une matrice et d'un tableau à trois dimensions
## pour les exemples.
m <- matrix(sample(1:100, 20), nrow = 4, ncol = 5)
a <- array(sample(1:100, 60), dim = 3:5)

## Les fonctions 'rowSums', 'colSums', 'rowMeans' et
## 'colMeans' sont des raccourcis pour des utilisations
## fréquentes de 'apply'.
rowSums(m)                      # somme par ligne
apply(m, 1, sum)                # idem, mais moins lisible
colMeans(m)                     # somme par colonne
apply(m, 2, mean)               # idem, mais moins lisible

## Puisqu'il n'existe pas de fonctions comme 'rowMax' ou
## 'colProds', il faut utiliser 'apply'.
apply(m, 1, max)                # maximum par ligne
apply(m, 2, prod)               # produit par colonne

## L'argument '... ' de 'apply' permet de passer des arguments
## à la fonction FUN.
m[sample(1:20, 5)] <- NA        # ajout de données manquantes
apply(m, 1, var, na.rm = TRUE)  # variance par ligne sans NA

## Lorsque 'apply' est utilisée sur un tableau, son résultat
## est de dimension dim(X)[MARGIN], d'où le truc
## mnémotechnique donné dans le texte du chapitre.
apply(a, c(2, 3), sum) # le résultat est une matrice
apply(a, 1, prod) # le résultat est un vecteur

## L'utilisation de 'apply' avec les tableaux peut rapidement
## devenir confondante si l'on ne visualise pas les calculs
## qui sont réalisés. On reprend ici les exemples du chapitre
## en montrant comment l'on calculerait le premier élément de
## chaque utilisation de 'apply'. Au besoin, retourner à
## l'indiçage des tableaux au chapitre 2.
(x <- array(sample(1:10, 80, rep = TRUE), c(3, 3, 4)))
apply(x, 3, det)            # déterminants des quatre matrices 3 x 3
det(x[, , 1])               # équivalent pour le premier déterminant

apply(x, 1, sum)            # sommes des trois tranches horizontales
sum(x[1, , ])               # équivalent pour la première somme

apply(x, c(1, 2), sum)      # sommes des neuf carottes horizontales
sum(x[1, 1, ])              # équivalent pour la première somme

apply(x, c(1, 3), sum)      # sommes des 12 carottes transversales
sum(x[1, , 1])              # équivalent pour la première somme

apply(x, c(2, 3), sum)      # sommes des 12 carottes verticales
sum(x[, 1, 1])              # équivalent pour la première somme

###
### FONCTIONS 'lapply' ET 'sapply'
###

## La fonction 'lapply' applique une fonction à tous les
## éléments d'un vecteur ou d'une liste et retourne une liste,
## peu importe les dimensions des résultats. La fonction
## 'sapply' retourne un vecteur ou une matrice, si possible.
##
## Somme « interne » des éléments d'une liste.
(x <- list(1:10, c(-2, 5, 6), matrix(3, 4, 5)))
sum(x) # erreur
lapply(x, sum)              # sommes internes (liste)
sapply(x, sum)              # sommes internes (vecteur)

## Création de la suite 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, ... , 1,
## 2, ... , 9, 10.
lapply(1:10, seq)           # le résultat est une liste
unlist(lapply(1:10, seq))   # le résultat est un vecteur

## Soit une fonction calculant la moyenne pondérée d'un
## vecteur. Cette fonction prend en argument une liste de deux
## éléments : 'donnees' et 'poids'.
fun <- function(x)
sum(x$donnees * x$poids)/sum(x$poids)

## On peut maintenant calculer la moyenne pondérée de
## plusieurs ensembles de données réunis dans une liste
## itérée.
(x <- list(list(donnees = 1:7,
poids = (5:11)/56),
list(donnees = sample(1:100, 12),
poids = 1:12),
list(donnees = c(1, 4, 0, 2, 2),
poids = c(12, 3, 17, 6, 2))))
sapply(x, fun)              # aucune boucle explicite !

###
### EXEMPLES ADDITIONNELS SUR L'UTILISATION DE L'ARGUMENT
### '... ' AVEC 'lapply' ET 'sapply'
###

## Aux fins des exemples ci-dessous, on crée d'abord une liste
## formée de nombres aléatoires. Cette expression fait usage
## de l'argument '... ' de 'lapply'. Pouvez-vous la décoder?
## Nous y reviendrons plus loin, ce qui compte pour le moment
## c'est simplement de l'exécuter.
x <- lapply(c(8, 12, 10, 9), sample, x = 1:10, replace = TRUE)

## Soit maintenant une fonction qui calcule la moyenne
## arithmétique des données d'un vecteur 'x' supérieures à une
## valeur 'y'. On remarquera que cette fonction n'est pas
## vectorielle pour 'y', c'est-à-dire qu'elle n'est valide que
## lorsque 'y' est un vecteur de longueur 1.
fun <- function(x, y) mean(x[x > y])

## Pour effectuer ce calcul sur chaque élément de la liste
## 'x', nous pouvons utiliser 'sapply' plutôt que 'lapply',
## car chaque résultat est de longueur 1. Cependant, il faut
## passer la valeur de 'y' à la fonction 'fun'. C'est là
## qu'entre en jeu l'argument '... ' de 'sapply'.
sapply(x, fun, 7) # moyennes des données > 7

## Les fonctions 'lapply' et 'sapply' passent tour à tour les
## éléments de leur premier argument comme *premier* argument
## à la fonction, sans le nommer explicitement. L'expression
## ci-dessus est donc équivalente à
##
##   c(fun(x[[1]], 7), ... , fun(x[[4]], 7)
##
## Que se passe-t-il si l'on souhaite passer les valeurs à un
## argument de la fonction autre que le premier ? Par exemple,
## supposons que l'ordre des arguments de la fonction 'fun'
## ci-dessus est inversé.
fun <- function(y, x) mean(x[x > y])

## Les règles d'appariement des arguments des fonctions en R
## font en sorte que lorsque les arguments sont nommés dans
## l'appel de fonction, leur ordre n'a pas d'importance. Par
## conséquent, un appel de la forme
##
##   fun(x, y = 7)
##
## est tout à fait équivalent à fun(7, x). Pour effectuer les calculs
##
##   c(fun(x[[1]], y = 7), ... , fun(x[[4]], y = 7)
##
## avec la liste définie plus haut, il s'agit de nommer
## l'argument 'y' dans '... ' de 'sapply'.
sapply(x, y = 7)

## Décodons maintenant l'expression
##
## lapply(c(8, 12, 10, 9), sample, x = 1:10, replace = TRUE)
##
## qui a servi à créer la liste. La définition de la fonction
## 'sample' est la suivante :
##
##   sample(x, size, replace = FALSE, prob = NULL)
##
## L'appel à 'lapply' est équivalent à
##
##   list(sample(8, x = 1:10, replace = TRUE),
##      ... ,
##      sample(9, x = 1:10, replace = TRUE))
##
## Toujours selon les règles d'appariement des arguments, on
## voit que les valeurs 8, 12, 10, 9 seront attribuées à
## l'argument 'size', soit la taille de l'échantillon.
## L'expression crée donc une liste comprenant quatre
## échantillons aléatoires de tailles différentes des nombres
## de 1 à 10 pigés avec remise.
##
## Une expression équivalente, quoique moins élégante, aurait
## recours à une fonction anonyme pour replacer les arguments
## de 'sample' dans l'ordre voulu.
lapply(c(8, 12, 10, 9),
       function(x) sample(1:10, x, replace = TRUE))

## La fonction 'sapply' est aussi très utile pour vectoriser
## une fonction qui n'est pas vectorielle. Supposons que l'on
## veut généraliser la fonction 'fun' pour qu'elle accepte un
## vecteur de seuils 'y'.
fun <- function(x, y)
    sapply(y, function(y) mean(x[x > y]))

## Utilisation sur la liste 'x' avec trois seuils.
sapply(x, fun, y = c(3, 5, 7))

###
### FONCTION 'mapply'
###

## Création de quatre échantillons aléatoires de taille 12.
x <- lapply(rep(12, 4), sample, x = 1:100)

## Moyennes tronquées à 0, 10, 20 et 30%, respectivement, de
## ces quatre échantillons aléatoires.
mapply(mean, x, 0:3/10)

###
### FONCTION 'replicate'
###

## La fonction 'replicate' va répéter un certain nombre de
## fois une expression quelconque. Le principal avantage de
## 'replicate' sur 'sapply' est qu'on n'a pas à se soucier des
## arguments à passer à une fonction.
##
## Par exemple, on veut simuler dix échantillons aléatoires
## indépendants de longueur 12. On peut utiliser 'sapply',
## mais la syntaxe n'est ni élégante, ni facile à lire
## (l'argument 'i' ne sert à rien).
sapply(rep(1, 10), function(i) sample(1:100, 12))

## En utilisant 'replicate', on sait tout de suite de quoi il
## s'agit. À noter que les échantillons se trouvent dans les
## colonnes de la matrice résultante.
replicate(10, sample(1:100, 12))

## Vérification que la moyenne arithmétique (bar{X}) est un
## estimateur sans biais de la moyenne de la loi normale. On
## doit calculer la moyenne de plusieurs échantillons
## aléatoires, puis la moyenne de toutes ces moyennes.
##
## On définit d'abord une fonction pour faire une simulation.
## Remarquer que dans la fonction ci-dessous, 'mean' est tour
## à tour le nom d'un argument (qui pourrait aussi bien être
## «toto») et la fonction pour calculer une moyenne.
fun <- function(n, mean, sd)
    mean(rnorm(n, mean = mean, sd = sd))

## Avec 'replicate', on fait un grand nombre de simulations.
x <- replicate(10000, fun(100, 0, 1)) # 10000 simulations
hist(x)                     # distribution de bar{X}
mean(x)                     # moyenne de bar{X}

###
### CLASSES ET FONCTIONS GÉNÉRIQUES
###

## Pour illustrer les classes et fonctions génériques, on
## reprend la fonction de point fixe 'fp3' des exemples du
## chapitre 5 en y faisant deux modifications :
##
##   1. Ajout d'un compteur pour le nombre d'itérations ;
##   2. La fonction retourne une liste de classe 'fp'
##      contenant diverses informations relatives à la
##      procédure de point fixe.
##
## Ainsi, la fonction 'fp4' retourne un objet qui peut ensuite
## être manipulé par des méthodes de fonctions génériques.
## C'est l'approche de programmation objet favorisée dans le
## langage R.
fp4 <- function(FUN, start, echo = FALSE, TOL = 1E-10)
{
    x <- start              # valeur de départ
    i <- 0                  # compteur des itérations

    if (echo)
        expr <- expression(print(xt <- x))
    else
        expr <- expression(xt <- x)

    repeat
    {
        eval(expr)

        x <- FUN(xt)        # nouvelle valeur
        i <- i + 1          # incrémenter le compteur

        if (abs(x - xt)/xt < TOL)
            break
    }

    structure(list(fixed.point = x, # point fixe
                   nb.iter = i,     # nombre d'itérations
                   fun = FUN,       # fonction f(x)
                   x0 = start,      # valeur de départ
                   TOL = TOL),      # précision relative
              class = "fp")
}

## On crée maintenant des méthodes pour la classe 'fp' pour
## les fonctions génériques les plus courantes, soit 'print',
## 'summary' et 'plot'.
##
## La méthode de 'print' sera utilisée pour afficher seulement
## la valeur du point fixe. C'est en quelque sorte
## l'utilisation la plus simple de la fonction 'fp4'.
##
## La méthode de 'summary' fournira un peu plus d'informations
## sur la procédure de point fixe.
##
## Enfin, la méthode de 'plot' fera un graphique de la
## fonction f(x) et son intersection avec la droite y = x.
print.fp <- function(x)
         print(x$fixed.point)
summary.fp <- function(x)
{
    if (class(x) != "fp")
        stop("object is not of class 'fp'")
    cat("Function:\n ")
    print(x$fun)
    cat("\n")
    cat("Fixed point:\n ", x$fixed.point, fill = TRUE)
    cat("\n")
    cat("Number of iterations:\n ", x$nb.iter, fill = TRUE)
    cat("\n")
    cat("Precision:\n ", x$TOL, fill = TRUE)
}

plot.fp <- function(x, ...)
{
    ## Valeur du point fixe
    fp <- x$fixed.point

    ## Il faut déterminer un intervalle pour lequel tracer la
    ## fonction. Celui-ci est déterminé de façon arbitraire
    ## comme un multiple de la distance entre la valeur de
    ## départ et la valeur du point fixe.
    r <- abs(x$x0 - fp)

    ## Fonction à tracer
    FUN <- x$fun

    ## Fonction y = x. 'FUN2' est nécessaire parce que 'curve'
    ## n'admet pas de fonctions anonymes en argument.
    FUN2 <- function(x) x

    ## Graphique de la fonction 'FUN'
    curve(FUN, from = fp - 3 * r, to = fp + 3 * r,
    xlab = "x", ylab = "f(x)", lwd = 2)

    ## Ajout de la droite 'FUN2' au graphique
    curve(FUN2, add = TRUE, lwd = 1)

    ## Ajout d'un point sur le point fixe
    points(fp, FUN(fp), ...)
}

## Exemples d'utilisation
x <- fp4(function(x) 3^(-x), start = 0.5)
x                           # affichage de 'print.fp'
summary(x)                  # plus d'information
plot(x)                     # graphique de base
plot(x, pch = 21,           # graphique plus élaboré...
     bg = "orange",         # ... consulter la rubrique
     cex = 2, lwd = 2)      # ... d'aide de 'par'
     
###
### OPÉRATEURS EN TANT QUE FONCTIONS
###

## Les opérateurs représentés par des caractères spéciaux sont
## des fonctions comme les autres. On peut donc les appeler
## comme toute autre fonction. (En fait, l'interprète R fait
## cette traduction à l'interne. )
x <- sample(1:100, 12)      # un vecteur
x + 2                       # appel usuel
"+"(x, 2)                   # équivalent
x[c(3, 5)]                  # extraction usuelle
"["(x, c(3, 5))             # équivalent
x[1] <- 0; x                # assignation usuelle
"[<-"(x, 2, 0)              # équivalent (à x[2] <- 0)

## D'une part, cela explique pourquoi il faut placer les
## opérateurs entre guillemets (" ") lorsqu'on les utilise
## dans les fonctions comme 'outer', 'lapply', etc.
outer(x, x, +)              # erreur de syntaxe
outer(x, x, "+")            # correct

## D'autre part, cela permet d'utiliser les opérateurs
## d'extraction "[" et "[[" dans de telles fonctions. Par
## exemple, voici comment extraire le deuxième élément de
## chaque élément d'une liste.
(x <- list(1:4, 8:2, 6:12, -2:2)) # liste quelconque
x[[1]][2]                   # 2e élément du 1er élément
x[[2]][2]                   # 2e élément du 2e élément
x[[3]][2]                   # 2e élément du 3e élément
x[[4]][2]                   # 2e élément du 4e élément
lapply(x, "[", 2)           # même chose en une ligne
sapply(x, "[", 2)           # résultat sous forme de vecteur

###
### COMMENT JOUER DES TOURS AVEC R
###

## Redéfinir un opérateur dans l'espace de travail de
## quelqu'un...
"+" <- function(x, y) x * y # redéfinition de "+"
5 + 2 # ouch!
ls()                        # traîtrise dévoilée...
rm("+")                     # ... puis éliminée
5 + 2                       # c'est mieux

## Faire croire qu'une fonction fait autre chose que ce
## qu'elle fait en réalité. Si l'attribut "source" d'une
## fonction existe, c'est son contenu qui est affiché lorsque
## l'on examine une fonction.
f <- function(x, y) x + y   # vraie fonction
attr(f, "source") <- "function(x, y) x * y" # ce qui est affiché
f                           # une fonction pour faire le produit?
f(2, 3)                     # non!
str(f)                      # structure de l'objet
attr(f, "source") <- NULL   # attribut "source" effacé
f                           # c'est mieux

## Redéfinir la méthode de 'print' pour une classe d'objet...
## Ici, l'affichage d'un objet de classe "lm" cause la
## fermeture de R !
print.lm <- function(x) q("ask")
x <- rnorm(10)              # échantillon aléatoire
y <- x + 2 + rnorm(10)      # modèle de régression linéaire
lm(y ~ x)                   # répondre "c"!

6-8. Exercices

6.1 (solution) À l'exercice 4.2, on a calculé la moyenne pondérée d'un vecteur d'observations

kitxmlcodelatexdvpX_w = \sum_{i=1}^n \frac{w_i}{w_\Sigma}X_ifinkitxmlcodelatexdvp

où kitxmlcodeinlinelatexdvpX_\Sigma = \sum_{i=1}^n w_ifinkitxmlcodeinlinelatexdvp Si l'on a plutôt une matrice kitxmlcodeinlinelatexdvpn \times pfinkitxmlcodeinlinelatexdvp d'observations kitxmlcodeinlinelatexdvpX_{ij}finkitxmlcodeinlinelatexdvp on peut définir les moyennes pondérées

kitxmlcodelatexdvpX_{iw} = \sum_{j=1}^p \frac{w_{ij}}{w_{i\Sigma}}X_{ij},\ w_{i\Sigma} = \sum_{j=1}^p w_{ij}finkitxmlcodelatexdvp kitxmlcodelatexdvpX_{wj} = \sum_{i=1}^n \frac{w_{ij}}{w_{\Sigma j}}X_{ij},\ w_{\Sigma j} = \sum_{i=1}^n w_{ij}finkitxmlcodelatexdvp

et

kitxmlcodelatexdvpX_{ww} = \sum_{i=1}^n\sum_{j=1}^p \frac{w_{ij}}{w_{\Sigma\Sigma}},\ w_{\Sigma\Sigma} = \sum_{i=1}^n\sum_{j=1}^p w_{ij}finkitxmlcodelatexdvp

De même, on peut définir des moyennes pondérées calculées à partir d'un tableau de données kitxmlcodeinlinelatexdvpX_{ijk}finkitxmlcodeinlinelatexdvp de dimensions kitxmlcodeinlinelatexdvpn\times p\times rfinkitxmlcodeinlinelatexdvp dont la notation suit la même logique que ci-dessus. Écrire des expressions R pour calculer, sans boucle, les moyennes pondérées suivantes.

  1. kitxmlcodeinlinelatexdvpX_{iw}finkitxmlcodeinlinelatexdvp en supposant une matrice de données kitxmlcodeinlinelatexdvpn\times pfinkitxmlcodeinlinelatexdvp.
  2. kitxmlcodeinlinelatexdvpX_{wj}finkitxmlcodeinlinelatexdvp en supposant une matrice de données kitxmlcodeinlinelatexdvpn\times pfinkitxmlcodeinlinelatexdvp.
  3. kitxmlcodeinlinelatexdvpX_{ww}finkitxmlcodeinlinelatexdvp en supposant une matrice de données kitxmlcodeinlinelatexdvpn\times pfinkitxmlcodeinlinelatexdvp.
  4. kitxmlcodeinlinelatexdvpX_{ijw}finkitxmlcodeinlinelatexdvp en supposant un tableau de données kitxmlcodeinlinelatexdvpn\times p\times rfinkitxmlcodeinlinelatexdvp.
  5. kitxmlcodeinlinelatexdvpX_{iww}finkitxmlcodeinlinelatexdvp en supposant un tableau de données kitxmlcodeinlinelatexdvpn\times p\times rfinkitxmlcodeinlinelatexdvp.
  6. kitxmlcodeinlinelatexdvpX_{wjw}finkitxmlcodeinlinelatexdvp en supposant un tableau de données kitxmlcodeinlinelatexdvpn\times p\times rfinkitxmlcodeinlinelatexdvp.
  7. kitxmlcodeinlinelatexdvpX_{www}finkitxmlcodeinlinelatexdvp en supposant un tableau de données kitxmlcodeinlinelatexdvpn\times p\times rfinkitxmlcodeinlinelatexdvp.

6.2 (solution) Générer les suites de nombres suivantes à l'aide d'expressions R. (Évidemment, il faut trouver un moyen de générer les suites sans simplement concaténer les différentes sous-suites.)

  1. 0, 0, 1, 0, 1, 2,…, 0, 1, 2, 3,…, 10.
  2. 10, 9, 8,…, 2, 1, 10, 9, 8,… 3, 2,…, 10, 9, 10.
  3. 10, 9, 8,…, 2, 1, 9, 8,…, 2, 1,…, 2, 1, 1.

6.3 (solution) La fonction de densité de probabilité et la fonction de répartition de la loi de Pareto de paramètres et sont, respectivement,

kitxmlcodelatexdvpf(x) = \frac{\alpha\lambda^\alpha}{(x+\lambda)^{\alpha+1}}finkitxmlcodelatexdvp

et

kitxmlcodelatexdvpF(x) = 1 - \left( \frac{\lambda}{x+\lambda} \right)^\alphafinkitxmlcodelatexdvp

La fonction suivante simule un échantillon aléatoire de taille kitxmlcodeinlinelatexdvpnfinkitxmlcodeinlinelatexdvp issu d'une distribution de Pareto de paramètres kitxmlcodeinlinelatexdvp\alpha= shapefinkitxmlcodeinlinelatexdvp et kitxmlcodeinlinelatexdvp\lambda=scalefinkitxmlcodeinlinelatexdvp :

 
Sélectionnez
rpareto <- function(n, shape, scale)
    scale * (runif(n)^(-1/shape) - 1)
  1. Écrire une expression R utilisant la fonction rpareto ci-dessus qui permet de simuler cinq échantillons aléatoires de tailles 100, 150, 200, 250 et 300 d'une loi de Pareto avec kitxmlcodeinlinelatexdvp\alpha = 2finkitxmlcodeinlinelatexdvp et kitxmlcodeinlinelatexdvp\lambda = 5000finkitxmlcodeinlinelatexdvp. Les échantillons aléatoires devraient être stockés dans une liste.
  2. On vous donne l'exemple suivant d'utilisation de la fonction paste :

     
    Sélectionnez
    > paste("a", 1:5, sep = "")
    [1] "a1" "a2" "a3" "a4" "a5"

    Nommer les éléments de la liste créée en a) sample1…, sample5.

  3. Calculer la moyenne de chacun des échantillons aléatoires obtenus en a). Retourner le résultat dans un vecteur.

  4. Évaluer la fonction de répartition de la loi de Pareto(2, 5000) en chacune des valeurs de chacun des échantillons aléatoires obtenus en a). Retourner les valeurs de la fonction de répartition en ordre croissant.

  5. Faire l'histogramme des données du cinquième échantillon aléatoire avec la fonction hist.

  6. Ajouter 1000 à toutes les valeurs de tous les échantillons simulés en a), ceci afin d'obtenir des observations d'une distribution de Pareto translatée.

6.4 (solution) Une base de données contenant toutes les informations sur les assurés est stockée dans une liste de la façon suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
> x[[1]]
$num.police
[1] 1001

$franchise
[1] 500

$nb.acc
[1] 0 1 1 0

$montants
[1] 3186.864 3758.389

> x[[2]]
$num.police
[1] 1002

$franchise
[1] 250

$nb.acc
[1] 4 0 0 4 1 1 0

$montants
[1]  16728.7354  1414.7264 1825.7495  282.5609
[5]   1684.6686 14869.1731 7668.4196 2501.7257
[9] 108979.3725  2775.3161

Ainsi, x[[i]] contient les informations relatives à l'assuré i. Sans utiliser de boucles, écrire des expressions ou des fonctions R qui permettront de calculer les quantités suivantes.

  1. La franchise moyenne dans le portefeuille.
  2. Le nombre annuel moyen de réclamations par assuré.
  3. Le nombre total de réclamations dans le portefeuille.
  4. Le montant moyen par accident dans le portefeuille.
  5. Le nombre d'assurés n'ayant eu aucune réclamation.
  6. Le nombre d'assurés ayant eu une seule réclamation dans leur première année.
  7. La variance du nombre total de sinistres.
  8. La variance du nombre de sinistres pour chaque assuré.
  9. La probabilité empirique qu'une réclamation soit inférieure à x (un scalaire) dans le portefeuille.
  10. La probabilité empirique qu'une réclamation soit inférieure à x (un vecteur) dans le portefeuille.

précédentsommairesuivant

Licence Creative Commons
Le contenu de cet article est rédigé par Vincent Goulet et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2018 Developpez.com.