-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path04-manipuler.Rmd
311 lines (220 loc) · 7.87 KB
/
04-manipuler.Rmd
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
```{R echo = FALSE, warnings = FALSE}
source("include.cpp.r")
includeCppPath <- "../introRcppManipulation/src/"
```
# Manipuler les objets de R en C++
All the examples are in the R package...
Install it with
```{R eval = FALSE, prompt = TRUE}
devtools::install_github("introRcpp/introRcppManipulation")
```
Load it with
```{R}
library(introRcppManipulation)
```
## Premiers objets Rcpp : les vecteurs
La librairie `Rcpp` définit des types `NumericVector`, `IntegerVector`
et `LogicalVector` qui permettent de manipuler en C++ les vecteurs de R.
### Créer des vecteurs, les manipuler
L'initialisation avec la syntaxe utilisée dans `vec0` remplit le vecteur de 0.
Notez l'accès aux éléments d'un vecteur par l'opérateur `[]`; **contrairement
à la convention utilisée par R, les vecteurs sont numérotés de 0 à n-1.**
```{R echo = FALSE, results = "asis"}
include.cpp("vec.cpp")
```
### Exemple : compter les zéros
```{R echo = FALSE, results = "asis"}
include.cpp("countZeroes.cpp")
```
Comment les performances de cette fonction se comparent-elles avec le code R `sum(a == 0)` ?
```{r prompt = TRUE, comment = NA, fig.width = 5, fig.height = 2, fig.align = 'center', message = FALSE, cache = TRUE}
set.seed(1); a <- sample(0:99, 1e6, TRUE)
countZeroes(a);
sum(a == 0)
mbm <- microbenchmark::microbenchmark( R = sum(a == 0), Cpp = countZeroes(a))
mbm
ggplot2::autoplot(mbm)
```
La différence de vitesse d'exécution s'explique en partie par le fait que le
code R commence par créer un vecteur de type `logical` (le résultat de `a == 0`),
puis le parcourt pour faire la somme. Ceci implique beaucoup de lectures écritures
en mémoire, ce qui ralentit l'exécution.
## Vecteurs
### Creating vectors
On a vu l'initialisation avec la syntaxe `NumericVector R(n)` qui crée
un vecteur de longueur $n$, rempli de $0$. On peut utiliser `NumericVector R(n, 1.0)`
pour un vecteur rempli de $1$ ; **attention à bien taper `1.0` pour avoir un
`double` et non un `int`; dans le cas contraire, on a un message d'erreur
difficilement compréhensible à la compilation**.
On peut utiliser `NumericVector R = no_init(n);` pour un vecteur non
initialisé (ce qui fait gagner du temps d'exécution).
```{R echo = FALSE, results = "asis"}
include.cpp("zeros.cpp")
include.cpp("whatever.cpp")
include.cpp("uninitialized.cpp")
include.cpp("favourites.cpp")
```
```{r prompt = TRUE, comment = NA}
zeros(5)
whatever(5, 2L)
uninitialized(5) # sometime 0s, not always
favourites()
```
Comparons les performances des trois premières fonctions (comme
à chaque fois, les résultats peuvent varier d'une architecture
à l'autre).
```{r prompt = TRUE, comment = NA, fig.width = 5, fig.height = 2, fig.align = 'center', message = FALSE, cache = TRUE}
mbm <- microbenchmark::microbenchmark(zeros(1e6), whatever(1e6, 0), uninitialized(1e6))
mbm
ggplot2::autoplot(mbm)
```
### Accessing elements
Using `x.size()` or `x.length()`.
Beware O-based indices.
BLA BLA
### Missing data
Cette fonction utilise `IntegerVector::is_na` qui est la bonne manière de
tester si un membre d'un vecteur entier est `NA`.
```{R echo = FALSE, results = "asis"}
include.cpp("countNAs.cpp")
```
Une nouveauté : le fichier `countNAS.h` ...
```{R echo = FALSE, results = "asis"}
include.cpp("countNAs.h")
include.cpp("getNonNAs.cpp")
```
Comparons ces deux fonctions avec leurs analogues R, `sum(is.na(x))` et `x[!is.na(x)]`.
```{r prompt = TRUE, comment = NA, fig.width = 5, fig.height = 2, fig.align = 'center', message = FALSE, cache = TRUE}
x <- sample( c(NA, rnorm(10)), 1e6, TRUE)
mbm <- microbenchmark::microbenchmark( sum(is.na(x)), countNAs(x), x[!is.na(x)], getNonNAs(x) )
mbm
ggplot2::autoplot(mbm)
```
## Vecteurs nommés
Ça n'est pas passionnant en soi (on ne manipule pas si souvent des
vecteurs nommés), mais ce qu'on voit là sera utile pour les listes
et les data frames.
### Créer des vecteurs nommés
Voici d'abord comment créer un vecteur nommé.
```{R echo = FALSE, results = "asis"}
include.cpp("createVec1.cpp")
```
Application :
```{r prompt = TRUE, comment = NA}
a <- createVec1()
a
```
Une syntaxe plus dense est possible :
```{R echo = FALSE, results = "asis"}
include.cpp("createVec2.cpp")
```
Cela produit le même résultat.
```{r prompt = TRUE, comment = NA}
createVec2()
```
### Accéder aux éléments par leurs noms
On utilise toujours la syntaxe `x[]` :
```{R echo = FALSE, results = "asis"}
include.cpp("getOne.cpp")
```
Notez la fonction `Rcpp::stop` qui correspond à la fonction R du même nom.
```{r prompt = TRUE, comment = NA, error = TRUE}
getOne(a)
getOne(b)
```
### Obtenir les noms d'un vecteur
Et voici comment obtenir les noms d'un vecteur.
```{R echo = FALSE, results = "asis"}
include.cpp("names1.cpp")
```
Utilisons cette fonction:
```{r prompt = TRUE, comment = NA, error = TRUE}
names1(a)
```
Cette fonction semble se comporter correctement, elle a cependant un gros
défaut. Nous y reviendrons dans la section suivante.
## Objets génériques : SEXP
Les objets R les plus génériques sont les `SEXP`, « S expression ». Les principaux types de `SEXP`
sont illustrés par la fonction suivante.
```{R echo = FALSE, results = "asis"}
include.cpp("RType.cpp")
```
Utiliser les types définis par Rcpp est généralement plus
facile et plus sûr. Cependant à l'intérieur des fonctions Rcpp ils peuvent
être utiles, par exemple dans le cas où une fonction peut renvoyer des objets
de types différents, par exemple soit un `NILSXP`, soit un objet d'un autre type.
### Exemple : vecteurs nommés (ou pas)
Testons à nouveau la fonction `names1`, en lui passant un vecteur non nommé.
```{r prompt = TRUE, comment = NA, error = TRUE}
b <- seq(0,1,length=6)
names1(b)
```
Bien sûr, le vecteur `b` n'a pas de noms ; la fonction `x.names()` a renvoyé
l'objet `NULL`, de type `NILSXP`, qui ne peut être utilisé pour
initialiser le vecteur `R` de type `STRSXP`.
Une solution est d'attraper le
résultat de `x.names()` dans un `SEXP`, et de tester son type
avec `TYPEOF`.
```{R echo = FALSE, results = "asis"}
include.cpp("names2.cpp")
```
```{r prompt = TRUE, comment = NA, error = TRUE}
names2(a)
names2(b)
```
### Exemple : énumerer les noms et le contenu
On va utiliser l'opérateur `CHAR` qui, appliqué à un élément
d'un `CharacterVector`, renvoie une valeur de type `const char *`
c'est-à-dire un pointeur vers une chaîne de caractère (constante,
ie non modifiable) « à la C » (voir chapitre dédié).
```{R echo = FALSE, results = "asis"}
include.cpp("enumerate.cpp")
```
```{r prompt = TRUE, comment = NA}
enumerate(a)
```
## Facteurs
```{R echo = FALSE, results = "asis"}
include.cpp("getLevels.cpp")
```
```{r prompt = TRUE, comment = NA}
x <- factor( sample(c("M","F"), 10, TRUE) )
getLevels(x)
x <- sample(1:2, 10, TRUE)
# getLevels(x)
attr(x, "levels") <- c(0.1, 0.4)
# getLevels(x)
```
```{R echo = FALSE, results = "asis"}
include.cpp("someFactor.cpp")
```
```{r prompt = TRUE, comment = NA}
someFactor()
```
## Listes et Data frames
Nous avons déjà vu les fonctions utiles dans le cas des vecteurs nommés, en particulier
`containsElementNamed`.
La fonction suivante prend une liste `L` qui a un élément `L$alpha` de type `NumericVector`
et renvoie celui-ci à l'utilisateur. En cas de problème un message d'erreur informatif
est émis.
```{R echo = FALSE, results = "asis"}
include.cpp("getAlpha.cpp")
```
Pour renvoyer des valeurs hétéroclites dans une liste c'est très facile:
```{R echo = FALSE, results = "asis"}
include.cpp("createList.cpp")
```
```{r prompt = TRUE, comment = NA}
createList()
```
Les data frames, ont l'a vu, sont des listes avec quelques attributs supplémentaires.
En Rcpp cela fonctionne de la même façon, avec la classe `DataFrame`.
Ils ont une certaine tendance à se transformer en liste quand on leur ajoute des
éléments.
Here is a useful trick.
```{R echo = FALSE, results = "asis"}
include.cpp("createDF.cpp")
```
```{r prompt = TRUE, comment = NA}
createDF()
```