-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy path015_fonctions.Rmd
795 lines (580 loc) · 32 KB
/
015_fonctions.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
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
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
# Las funciones {#fonctions}
## ¿Qué es una función?
Con este capítulo vamos a echar un primer vistazo al poder de R a través de las funciones. Una función es un conjunto de líneas de código para realizar una tarea en particular. Hemos visto muchas funciones en capítulos anteriores, unas simples como la función `+` para añadir números, o otras más complejas como `c()` o `data.frame()` que permiten crear un `vector` o `data.frame`. En cualquier caso, se puede reconocer una función gracias a los paréntesis que la siguen en los cuales vamos a ingresar **argumentos**. Los argumentos corresponden a la información que queremos transmitir a nuestra función para que realice la tarea que queremos lograr.
Para funciones simples como `+`, los paréntesis han sido eliminados para que el código sea más fácil de leer, pero es una función que puede usarse con paréntesis si usamos el signo `+` entre comillas. Los argumentos son los números que queremos agregar.
```{r 015-fonctions-1}
5 + 2
'+'(5, 2)
```
En este capítulo nos enfocaremos en las funciones más comunes. No se trata de aprender todo de memoria, sino de saber que existen estas funciones y de poder consultar más adelante este capítulo como referencia. ¡Con tiempo y práctica eventualmente los sabremos de memoria! Hay más de 1000 funciones en la versión básica de R, y más de 10000 paquetes adicionales que se pueden instalar, cada uno con docenas de funciones. Antes de comenzar a escribir una nueva función, siempre debemos verificar que ya no exista.
## Las funciones más comunes
Para trabajar con las funciones, vamos a usar los datos `iris` que están incluidos con la versión básica de R y que corresponden a la longitud y el ancho de los sépalos y pétalos de diferentes especies de iris. Los datos `iris` estan en una `data.frame` de 5 columnas y 150 líneas. Para obtener más información sobre los datos `iris`, podemos consultar la documentación R con la función `help(iris)`. El acceso a la documentación es el tema de la siguiente sección.
### El acceso a la documentación
#### `help()` {#l015help}
La función esencial de R es acceder a la documentación (en ingles). **Todas las funciones R tienen documentación**. Podemos acceder a la documentación con la función `help()` o usando el atajo `?`.
```{r 015-fonctions-2, eval = FALSE}
help(matrix) # equivalente a ?matrix
```
La documentación siempre está estructurada de la misma manera. Primero tenemos el nombre de la función buscada `matrix`, seguida entre llaves por el nombre del paquete R cuya función depende. Veremos cómo instalar paquetes adicionales más adelante. Por ahora tenemos los que vienen con la versión básica de R. Aquí podemos ver que la función `matrix()` depende del paquete `base`.
Podemos ver la etiqueta de la función (`Matrices`), seguida de los parafos `Description`, `Usage`, y `Arguments`. Algunas veces se agregan los párrafos `Details`, `Note`, `References` y `See also`. El último párrafo es `Ejemplos`. La última línea de la documentación permite volver al índice del paquete del que depende la función consultada.
Al copiar `help(matrix)` en nuestra consola R, podemos ver que el párrafo `Description` indica lo que hace la función. En el caso de `help(matrix)`, hay tres funciones: `matrix()`, `as.matrix()` y `is.matrix()`.
```{r 015-fonctions-3}
# Description
# matrix creates a matrix from the given set of values.
# as.matrix attempts to turn its argument into a matrix.
# is.matrix tests if its argument is a (strict) matrix.
```
El párrafo `Usage` explica cómo usar la función y cuáles son los valores predeterminados para cada parámetro.
```{r 015-fonctions-4}
# Usage
# matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE,
# dimnames = NULL)
```
La función `matrix()` puede tomar 5 argumentos: `data`, `nrow`, `ncol`, `byrow`, y `dimnames`. Podemos ver que por defecto una `matrix` consistirá de una sola línea y una sola columna, y que la información se completará por columna.
El párrafo `Arguments` detalla los valores y el tipo de contenedor de cada argumento de nuestra función. Por ejemplo, podemos ver que el argumento `dimnames` debe ser del tipo `list`. Es por eso que hemos usado este formato en la sección [`matrix`](#l014matrix).
```{r 015-fonctions-5}
# Arguments
# data an optional data vector (including a list or expression vector).
# Non-atomic classed R objects are coerced by as.vector and all
# attributes discarded.
# nrow the desired number of rows.
# ncol the desired number of columns.
# byrow logical. If FALSE (the default) the matrix is filled by columns,
# otherwise the matrix is filled by rows.
# dimnames A dimnames attribute for the matrix: NULL or a list of length 2
# giving the row and column names respectively. An empty list is
# treated as NULL, and a list of length one as row names. The
# list can be named, and the list names will be used as names for
# the dimensions.
```
El párrafo `Details` proporciona elementos adicionales en la función. El párrafo `Examples` proporciona ejemplos reproducibles en la consola.
```{r 015-fonctions-6}
## Example of setting row and column names
mdat <- matrix(c(1,2,3, 11,12,13), nrow = 2, ncol = 3, byrow = TRUE,
dimnames = list(c("row1", "row2"),
c("C.1", "C.2", "C.3")))
mdat
```
El nombre de los argumentos no es necesario para que una función sea interpretada correctamente por R. Sin embargo, es mejor usar explicitamente el nombre de los argumentos seguidos por el signo `=` para que el código sea más legible.
```{r 015-fonctions-7}
# buen ejemplo
mdat <- matrix(c(1, 2, 3, 11, 12, 13), nrow = 2, ncol = 3, byrow = TRUE)
# mal ejemplo
mdat <- matrix(c(1, 2, 3, 11, 12, 13), 2, 3, TRUE)
```
#### `help.search()` {#l015helpsearch}
La función `help.search()` o `??` permite buscar una expresión en toda la documentación. Es útil cuando buscamos una función sin saber el nombre exacto de la función en R.
```{r 015-fonctions-8, eval = FALSE}
help.search("average")
```
La función `help.search()` devuelve una página que contiene la lista de páginas donde se encontró la expresión en la forma `package-name::function-name`.
### Ver los datos
#### `str()` {#l015str}
La función `str()` permite visualizar la estructura interna de un objeto, como se indica en la documentación que podemos consultar con `help(str)`.
```{r 015-fonctions-9}
str(iris)
```
La función `str()` devuelve el tipo de objeto (`data.frame`), el número de observaciones (150), el número de variables (5), el nombre de cada variable (`Sepal.Length` , `Sepal.Width`, `Petal.Length`, `Petal.Width`, y `Species`), el tipo de cada variable (`num`,` Factor`), y los primeros valores de cada una de las variables. Es una función útil para echar un vistazo a un conjunto de datos, pero también para verificar que los datos sean del tipo requirido antes de realizar un análisis estadístico.
#### `head()` y `tail()` {#l015head}
La función `head()` devuelve los primeros valores de un objeto, y la función `tail()` devuelve los últimos valores de un objeto. Por defecto, se devuelven seis valores, el argumento `n` controla el número de valores a devolver.
```{r 015-fonctions-10}
head(iris)
tail(iris)
head(iris, n = 2)
```
#### `names()` {#l015names}
Ya hemos visto la función `names()`, que permite conocer los nombres de los elementos de un objeto, pero también asignar nombres a los elementos de un objeto como a un [`matrix`](#l014matrix), a una [`list`](#l014list) o a un [`data.frame`](#l014dataframe).
```{r 015-fonctions-11}
names(iris)
irisCopy <- iris
names(irisCopy) <- c("a", "b", "c", "d", "e")
names(irisCopy)
```
#### `cat()` y `print()` {#l015print}
La función `cat()` se usa para mostrar el contenido de un objeto mientras que la función `print()` devuelve el valor de un objeto con la capacidad de realizar conversiones.
```{r 015-fonctions-12}
cat(names(iris))
print(names(iris))
cat(iris[1, 1])
print(iris[1, 1])
print(iris[1, 1], digits = 0)
```
### Manipular los datos
#### `rank()` {#l015rank}
La función `rank()` devuelve el número de la posición ordenada de cada elemento de un conjunto de elementos. En el caso de elementos del mismo valor, el argumento `ties.method` hace posible hacer una elección sobre la clasificación. Como con todas las funciones, los detalles están presentes en la documentación.
```{r 015-fonctions-13}
vecManip <- c(10, 20, 30, 70, 60, 50, 40)
rank(vecManip)
vecManip2 <- c(10, 20, 30, 10, 50, 10, 40)
rank(vecManip2)
rank(vecManip2, ties.method = "first")
rank(vecManip2, ties.method = "min")
print(iris[, 1])
rank(iris[, 1], ties.method = "average")
# help(rank)
# ...
# Usage
# rank(x, na.last = TRUE,
# ties.method = c("average", "first", "last",
# "random", "max", "min"))
```
#### `order()` {#l015order}
La función `order()` devuelve el número de la reorganización de los elementos en función de su posición. Es muy útil, por ejemplo, para ordenar un `data.frame` en función de una columna.
```{r 015-fonctions-14}
print(vecManip2)
rank(vecManip2)
order(vecManip2)
print(iris[, 1])
rank(iris[, 1])
order(iris[, 1])
head(iris[order(iris[, 1]),], n = 10)
```
#### `sort()` {#l015sort}
La función `sort()` se usa para ordenar los elementos de un objeto. No permite la clasificación por más de una variable, como es el caso de `order()`.
```{r 015-fonctions-15}
print(vecManip2)
sort(vecManip2)
print(iris[, 1])
sort(iris[, 1])
```
#### `append()` {#l015append}
Esta función se usa para agregar un elemento a un `vector` en una posición determinada por el argumento `after`. Esta función también es más rápida que su alternativa [`c()`](#l014vector).
```{r 015-fonctions-16}
print(vecManip2)
append(vecManip2, 5)
append(vecManip2, 5, after = 2)
```
#### `cbind()` y `rbind()` {#l015cbind}
Las funciones `cbind()` y `rbind()` permiten combinar elementos por columna o por línea.
```{r 015-fonctions-17}
cbind(vecManip2, vecManip2)
rbind(vecManip2, vecManip2)
```
#### `paste()` y `paste0()` {#l015paste}
Estas son dos funciones que usaremos mucho a partir de ahora. Las funciones `paste()` y `paste0()` se usan para concatenar cadenas de texto. La función `paste0()` es equivalente a `paste()` sin proponer un separador entre los elementos a concatenar. La función `paste0()` también es más rápida.
```{r 015-fonctions-18}
paste(1, "a")
paste0(1, "a")
paste(1, "a", sep = "_")
paste0("prefix_", vecManip2, "_suffix")
paste(vecManip2, rank(vecManip2), sep = "_")
```
#### `rev()` {#l015rev}
La función `rev ()` devuelve los elementos de un objeto en orden inverso.
```{r 015-fonctions-19}
print(vecManip2)
rev(vecManip2)
```
#### `%in%()` {#l015in}
La función `%in%()` se puede comparar con un [operador de comparación](#l011opcomp). Esta función toma dos objetos como argumentos y devuelve `TRUE` o `FALSE` para cada elemento del primer objeto de acuerdo con su presencia o ausencia en el segundo objeto. Para acceder a la documentación de la función, use `help('%in%')` (con comillas simples).
```{r 015-fonctions-20}
print(vecManip)
print(vecManip2)
vecManip %in% vecManip2
vecManip2 %in% vecManip
```
### Funciones matemáticas {#l015maths}
Ya hemos visto las funciones `+`, `-`, `*`, `/`, `^`, `%%` y otros [operadores aritméticos](#l011opari). R también tiene funciones matemáticas básicas como exponencial `exp()`, raíz cuadrada `sqrt()`, valor absoluto `abs()`, sinus `sin()`, coseno `cos()`, tangente `tan()`, logaritmo `log()`, logaritmo base 10 `log10()`, arco coseno `acos()`, arco sinus `asin()`, y arco tangente `atan()`.
```{r 015-fonctions-21}
print(vecManip2)
exp(vecManip2)
sqrt(vecManip2)
abs(-vecManip2)
sin(vecManip2)
cos(vecManip2)
tan(vecManip2)
log(vecManip2)
log10(vecManip2)
acos(vecManip2/100)
asin(vecManip2/100)
atan(vecManip2/100)
```
### Estadísticas descriptivas
También podemos realizar estadísticas descriptivas de forma muy simple a partir de un conjunto de datos.
#### `mean()` {#l015mean}
La función `mean()` devuelve la media. Para ignorar los valores faltantes `NA`, hay que afectar el valor `TRUE` al argumento `na.rm()`.
```{r 015-fonctions-22}
mean(iris[, 1])
vecManip3 <- c(1, 5, 6, 8, NA, 45, NA, 14)
mean(vecManip3)
mean(vecManip3, na.rm = TRUE)
```
#### `sd()` {#l015sd}
La función `sd()` devuelve la desviación estándar.
```{r 015-fonctions-23}
sd(iris[, 1])
print(vecManip3)
sd(vecManip3)
sd(vecManip3, na.rm = TRUE)
```
#### `max()` y `min()` {#l015max}
La función `max()` devuelve el valor máximo y `min()` el valor mínimo.
```{r 015-fonctions-24}
max(iris[, 1])
print(vecManip3)
max(vecManip3)
max(vecManip3, na.rm = TRUE)
min(iris[, 1])
min(vecManip3)
min(vecManip3, na.rm = TRUE)
```
#### `quantile()` {#l015quantile}
La función `quantile()` devuelve el cuantil definido por el argumento `probs`.
```{r 015-fonctions-25}
quantile(iris[, 1])
quantile(iris[, 1], probs = c(0, 0.25, 0.5, 0.75, 1))
quantile(iris[, 1], probs = c(0, 0.1, 0.5, 0.9, 1))
```
#### `summary()` {#l015summary}
La función `summary()` devuelve un resumen con el mínimo, primer cuartil, mediana, promedio, tercer cuartil y máximo.
```{r 015-fonctions-26}
summary(iris[, 1])
```
#### `median()` {#l015median}
La función `median()` devuelve la mediana.
```{r 015-fonctions-27}
median(iris[, 1])
print(vecManip3)
median(vecManip3)
median(vecManip3, na.rm = TRUE)
```
#### `length()` {#l015length}
La función `length()` devuelve el tamaño de un objeto (número de elementos).
```{r 015-fonctions-28}
length(iris[, 1])
print(vecManip3)
length(vecManip3)
```
#### `nrow()` et `ncol()` {#l015nrow}
La función `nrow()` devuelve el número de líneas y la función `ncol()` el número de columnas en un objeto.
```{r 015-fonctions-29}
nrow(iris)
ncol(iris)
```
#### `round()`, `ceiling()`, `floor()`, et `trunc()` {#l015round}
La función `round()` le permite seleccionar una cierta cantidad de decimales (0 por defecto)
```{r 015-fonctions-30}
round(5.56874258564)
round(5.56874258564, digits = 2)
```
La función `ceiling()` devuelve el entero más pequeño que no es inferior al valor especificado.
```{r 015-fonctions-31}
ceiling(5.9999)
ceiling(5.0001)
```
La función `floor()` devuelve el entero más grande que no excede el valor especificado.
```{r 015-fonctions-32}
floor(5.9999)
floor(5.0001)
```
La función `trunc()` devuelve la parte entera del valor especificado.
```{r 015-fonctions-33}
trunc(5.9999)
trunc(5.0001)
```
#### `rowSums()` et `colSums()` {#l015rowsums}
Las funciones `rowSums()` y `colSums()` calculan la suma de filas y columnas.
```{r 015-fonctions-34}
rowSums(iris[, c(1, 2, 3, 4)])
colSums(iris[, c(1, 2, 3, 4)])
```
#### `rowMeans()` et `colMeans()` {#l015rowmeans}
Las funciones `rowMeans()` y `colMeans()` calculan el promedio de filas y columnas.
```{r 015-fonctions-35}
rowMeans(iris[, c(1, 2, 3, 4)])
colMeans(iris[, c(1, 2, 3, 4)])
```
#### `aggregate()` {#l015aggregate}
La función `aggregate()` permite agrupar los elementos de un objeto de acuerdo con un valor. El argumento `by` define el elemento sobre el que se realiza la agrupación. Debe ser del tipo [`list`](#l014list).
```{r 015-fonctions-36}
aggregate(iris[, c(1, 2, 3, 4)], by = list(iris$Species), FUN = mean)
aggregate(iris[, c(1, 2)], by = list(iris$Species), FUN = summary)
```
#### `range()` {#l015range}
La función `range()` devuelve el mínimo y el máximo.
```{r 015-fonctions-37}
range(iris[, 1])
print(vecManip3)
range(vecManip3)
range(vecManip3, na.rm = TRUE)
```
#### `unique()` {#l015unique}
La función `unique()` devuelve los valores únicos de un objeto (sin duplicados).
```{r 015-fonctions-38}
unique(iris[, 1])
print(vecManip3)
unique(vecManip3)
```
## Otras funciones útiles
No podemos abordar todas las funciones útiles, aquí solo abordaremos ciertas funciones. A lo largo de este libro, se usarán nuevas funciones. Cuando se utiliza una nueva función, nuestro reflejo siempre debe ser el mismo: **consultar la documentación** con la función `help()`.
### `seq_along()` {#l015seqalong}
La función `seq_along()` se usa para crear un `vector` del tamaño del objeto rellenado y tomando como valores los números de 1 a N (N corresponde al número de elementos del objeto). Esta función nos servirá mucho en el capítulo sobre bucles.
```{r 015-fonctions-39}
print(vecManip3)
seq_along(vecManip3)
```
### `:` {#l0152points}
La función `:` permite crear una secuencia desde `a` hacia `b` por pasos de 1. Ha sido difícil escribir los capítulos anteriores sin usarlo ya que esta función es muy útil. Aquí estan algunos ejemplos.
```{r 015-fonctions-40}
5:10
head(iris[, c(1, 2, 3, 4)])
head(iris[, 1:4]) # ;-)
miVec01 <- c(1, 2, 3, 4)
miVec01 <- 1:4 # ;-)
-10:12
5:-5
paste("X", 1:10, sep = "_")
```
### `rep()` {#l015rep}
La función `rep()` permite repetir elementos.
```{r 015-fonctions-41}
miVec12 <- c(1, 1, 1, 1, 1, 1, 1, 1, 1)
miVec12 <- rep(1, times = 9) # ;-)
rep("Hola", times = 3)
rep(1:3, time = 3)
rep(1:3, length.out = 10)
rep(1:3, each = 3)
```
### `seq()` {#l015seq}
La función `seq()` permite crear una secuencia personalizada.
```{r 015-fonctions-42}
seq(from = 0, to = 1, by = 0.2)
seq(from = 20, to = 10, length.out = 10)
letters[seq(from = 1, to = 26, by = 2)]
rep(seq(from = 1, to = 2, by = 0.5), times = 3)
```
### `getwd()` {#l015getwd}
La función `getwd()` establece la carpeta de trabajo. Esto corresponde a la ubicación relativa desde la cual R se posiciona para identificar los archivos. Este concepto tendrá sentido cuando veamos cómo importar y exportar datos.
```{r 015-fonctions-43}
getwd()
```
### `setwd()` {#l015setwd}
La función `setwd()` se usa para definir un nuevo directorio de trabajo (carpeta de trabajo).
```{r 015-fonctions-44}
oldWd <- getwd()
print(oldWd)
setwd("..")
getwd()
setwd(oldWd)
getwd()
```
### `list.files()` {#l015listfiles}
La función `list.files ()` se usa para listar todos los archivos en el directorio de trabajo.
```{r 015-fonctions-45}
list.files(pattern = "(html)$") # html
list.files(pattern = "(pdf)$") # pdf
```
### `ls()` {#l015ls}
Al igual que la función `list.files()` hace posible listar todos los archivos presentes en el directorio de trabajo, la función `ls()` permite listar todos los objetos presentes en el entorno de trabajo de R.
```{r 015-fonctions-46}
ls()
zzz <- "a new object"
ls()
```
### `rm()` {#l015rm}
La función `rm()` permite eliminar un objeto presente en el entorno de trabajo de R.
```{r 015-fonctions-47}
rm(zzz)
ls()
```
## Algunos ejercicios para practicar
Aquí hay algunos ejercicios para mejorar el uso de las funciones y aprender nuevas gracias a la documentación. Algunos ejercicios son difíciles, podremos volver a resolverlos más tarde.
### Secuencias
#### Vamos a reproducir las siguientes secuencias:
-3 -4 -5 -6 -7 -8 -9 -10 –11
-3 -1 1 3 5 7 9 11
3.0 3.2 3.4 3.6 3.8 4.0
20 18 16 14 12 10 8 6
"a" "f" "k" "p" "u" "z"
"a" "a" "a" "a" "a" "f" "f" "f" "f" "f" "k" "k" "k" "k" "k" "p" "p" "p" "p" "p" "u" "u" "u" "u" "u" "z" "z" "z" "z" "z"
#### Posibles soluciones (porque siempre hay varias soluciones):
```{r 015-fonctions-48}
-3:-11
seq(from = -3, to = 11, by = 2)
seq(from = 3.0, to = 4.0, by = 0.2)
letters[seq(from = 1, to = 26, by = 5)]
letters[rep(seq(from = 1, to = 26, by = 5), each = 5)]
```
### Estadísticas descriptivas
En el conjunto de datos `iris`, ¿cuántos valores de ancho del sépalo son mayores que 3? Entre 2.8 y 3.2?
¿Cómo se puede visualizar la distribución de datos (función `table()`)?
¿Cuáles son los 10 valores más pequeños?
¿Cómo se calcula el intervalo que contiene el 90% de los valores?
Si la distribución de los datos era Normal, ¿cuál sería el valor teórico de este intervalo del 90% (función `qnorm()`)?
Soluciones:
```{r 015-fonctions-49}
length(iris$Sepal.Width[iris$Sepal.Width > 3])
length(iris$Sepal.Width[iris$Sepal.Width > 2.8 &
iris$Sepal.Width < 3.2])
table(iris$Sepal.Width)
table(round(iris$Sepal.Width))
irisSepWCopy <- iris$Sepal.Width
irisSepWCopy <- irisSepWCopy[order(irisSepWCopy)]
head(irisSepWCopy, n = 10)
quantile(irisSepWCopy, probs = c(0.05, 0.95))
qnorm(
p = c(0.05, 0.95),
mean = mean(irisSepWCopy),
sd = sd(irisSepWCopy)
)
```
## Escribir una función {#l015function}
Cuando reproducimos las mismas operaciones varias veces, el código se vuelve difícil de escribir y de mantener porque si tenemos que hacer una modificación, tendremos que repetirla cada vez que la usemos. Esto es un signo de la necesidad de usar una **función**. En el siguiente ejemplo, sera largo modificar el código si queremos agregar +45 en lugar de +20 para cada línea.
```{r 015-fonctions-50}
35 + 20
758 + 20
862 + 20
782 + 20
```
Como todas las funciones básicas de R, nuestras funciones tendrán un **nombre** y **argumentos**. Al igual que con los nombres de los objetos y los nombres de los archivos, es importante elegir bien el nombre de nuestra función (ver la [sección sobre objetos](#l011object)). Para crear una función utilizaremos la función `function()` que toma como argumento los argumentos de nuestra función. La función devolverá el resultado deseado. Por defecto, el resultado devuelto es el último utilizado, pero es mejor usar la función `return()`. La siguiente función `addX()` toma como argumento `x` y devuelve `x + 20`.
```{r 015-fonctions-51}
addX <- function(x){
return(x + 20)
}
```
Nuestro código se convierte en:
```{r 015-fonctions-52}
addX(35)
addX(758)
addX(862)
addX(782)
```
Si queremos cambiar el código para agregar 45 en lugar de 20, simplemente cambiamos la función `addX()`.
```{r 015-fonctions-53}
addX <- function(x){
return(x + 45)
}
addX(35)
addX(758)
addX(862)
addX(782)
```
Aquí podríamos haber usado el formato `vector` para evitar la repetición, pero eso no siempre es posible.
```{r 015-fonctions-54}
c(35, 758, 862, 782) + 20
```
Vamos a esribir una nueva función que contará el número de consonantes y vocales en minúsculas en una palabra. Primero separaremos todas las letras de la palabra con la función `strsplit` (podemos consultar la ayuda para saber más acerca de esta función). Luego contaremos las vocales y las consonantes con la función [`length()`](#l015length). Para la lista de letras, usaremos el objeto `letters` incluido en R que contiene las 26 letras en minuscula (consulte la ayuda con `?letters`).
```{r 015-fonctions-55}
print(letters) # las 26 letras
countVowelConso <- function(word){ # nombre: countVowelConso ; argumento: word
wordSplit <- strsplit(word, split = "")[[1]] # separar letras de word
vowels <- c("a", "e", "i", "o", "u", "y") # las vocales
numVowel <- length(wordSplit[wordSplit %in% vowels]) # numero de vocales
consonants <- letters[!letters %in% vowels] # las consonantes
numConso <- length(wordSplit[wordSplit %in% consonants]) # numero de consonantes
return(c(numVowel, numConso)) # el resultado de la funcion
}
```
Ahora podemos usar nuestra función.
```{r 015-fonctions-56}
countVowelConso(word = "qwertyuiop azertyuiop")
```
Esta función se puede modificar mostrando un mensaje más explícito. Aunque en general se debe evitar este tipo de mensaje para evitar sobrecargar las funciones, puede ser útil verificar que todo esté funcionando correctamente (luego lo borraremos).
```{r 015-fonctions-57}
countVowelConso <- function(word){
wordSplit <- strsplit(word, split = "")[[1]]
vowels <- c("a", "e", "i", "o", "u", "y")
numVowel <- length(wordSplit[wordSplit %in% vowels])
consonants <- letters[!letters %in% vowels]
numConso <- length(wordSplit[wordSplit %in% consonants])
print(paste0("Hay ", numVowel, " vocales y ",
numConso, " consonantes en la palabra '", word, "'."))
return(c(numVowel, numConso))
}
countVowelConso(word = "qwertyuiop azertyuiop")
```
Por otro lado, si usamos `countVowelConso(word = 5)`, se devolverá un error porque nuestra función espera un objeto de tipo `character`. En general, se recomienda *manejar los errores* devueltos por nuestras funciones para que nuestro código sea más fácil de mantener. Aquí simplemente comprobaremos que el argumento sea de tipo `character`, en un `vector` de tamaño 1. También comentaremos nuestra función para encontrar rápidamente lo que hace (comentario insertado en la primera línea, que a veces encontramos en la última línea de las funciones).
```{r 015-fonctions-58}
countVowelConso <- function(word){ # número de vocales y consonantes
if(is.vector(word) & is.character(word) & length(word) == 1){
wordSplit <- strsplit(word, split = "")[[1]]
vowels <- c("a", "e", "i", "o", "u", "y")
numVowel <- length(wordSplit[wordSplit %in% vowels])
consonants <- letters[!letters %in% vowels]
numConso <- length(wordSplit[wordSplit %in% consonants])
return(c(numVowel, numConso))
} else {
print(paste0("Error: ",
"argumento 'word' incorrecto (", word, ")"))
}
}
countVowelConso(word = "qwertyuiop azertyuiop")
countVowelConso(word = 5)
```
Con R como para cualquier lenguaje de programación, para un problema siempre hay múltiples soluciones. Recordamos la sección sobre tipos de datos ([tipo de datos `logical`](#l013logi)), así como la sección sobre [operadores de comparación](#l011opcomp) que el valor de `TRUE` es 1 y el valor de `FALSE` es 0. Hemos visto anteriormente que la función [`% in%`](#l015in) devuelve `TRUE` o `FALSE` para cada elemento del primer objeto dependiendo de su presencia o ausencia en el segundo objeto. Nuestra función podría haber usado otra función en lugar de `length()` para contar vocales y consonantes (función `sum()`).
```{r 015-fonctions-59}
countVowelConsoAlt <- function(word){ # número de vocales y consonantes
if(is.vector(word) & is.character(word) & length(word) == 1){
wordSplit <- strsplit(word, split = "")[[1]]
vowels <- c("a", "e", "i", "o", "u", "y")
numVowel <- sum(wordSplit %in% vowels) # <- cambio aqui
consonants <- letters[!letters %in% vowels]
numConso <- sum(wordSplit %in% consonants) # <- cambio aqui
return(c(numVowel, numConso))
} else {
print(paste0("Error: ",
"argumento 'word' incorrecto (", word, ")"))
}
}
countVowelConsoAlt(word = "qwertyuiop azertyuiop")
```
No existe una solución óptima en absoluto, todo depende de los objetivos deseados. La primera solución puede ser más fácil de entender, y la segunda puede ser más rápida en términos de velocidad de ejecución (repitiendo el uso de la función 10000 veces, el ahorro de tiempo es casi cero en nuestro caso).
```{r 015-fonctions-60}
system.time(replicate(n = 10000, countVowelConso(word = "qwertyuiop azertyuiop")))
system.time(replicate(n = 10000, countVowelConsoAlt(word = "qwertyuiop azertyuiop")))
```
Una función puede tener **valores predeterminados** para sus argumentos. Este es el caso para la mayoría de las funciones existentes. Por defecto, nuestra función ahora contará el número de vocales y consonantes en la palabra `qwerty` (los paréntesis son necesarios incluso en ausencia de argumentos).
```{r 015-fonctions-61}
countVowelConsoAlt <- function(word = "qwerty"){ # número de vocales y consonantes
if(is.vector(word) & is.character(word) & length(word) == 1){
wordSplit <- strsplit(word, split = "")[[1]]
vowels <- c("a", "e", "i", "o", "u", "y")
numVowel <- sum(wordSplit %in% vowels)
consonants <- letters[!letters %in% vowels]
numConso <- sum(wordSplit %in% consonants)
return(c(numVowel, numConso))
} else {
print(paste0("Error: ",
"argumento 'word' incorrecto (", word, ")"))
}
}
countVowelConsoAlt() # no hay que olvidar los paréntesis
```
R tiene muchas funciones, por lo tanto, antes de comenzar a escribir una nueva función, siempre debemos verificar que ya no exista en la versión básica de R o en los **packages** desarrollado por la comunidad de usuarios. Para esto podemos usar la ayuda con la función `??miBusqueda`, pero también nuestro navegador de Internet.
## Otras funciones desarrolladas por la comunidad de usuarios: los *packages*
Un package (o paquete) es un conjunto de archivos que agregaremos a R para usar funciones (o conjuntos de datos) que otras personas hayan desarrollado. Actualmente hay más de 10,000 paquetes en los servidores CRAN de R (CRAN; https://cran.r-project.org/web/packages/), más de 1000 en los servidores de BioConductor (para análisis genómicos) y varios cientos en GitHub. Cada paquete hace posible usar nuevas funciones para casi todo ... Por lo tanto, puede ser difícil encontrar el paquete adecuado para lo que queremos lograr, y es importante dedicar tiempo a la busqueda del paquete adecuado y probar varios soluciones.
Para usar un paquete, primero debemos **instalarlo**, y luego **cargarlo** en nuestra sesión R.
### Instalar un paquete
Una vez que hemos seleccionado nuestro paquete, podemos descargarlo e instalarlo con la función `install.packages()`, que toma el nombre del paquete entre comillas como argumento (la función tolera la ausencia de comillas, pero es mejor usarlas para que el código sea más legible). Algunos paquetes ya son instalados por defecto con R, como `stats` (que también se carga de forma predeterminada).
```{r 015-fonctions-62, eval = FALSE}
install.packages("stats") # R statistical functions
```
La instalación de un paquete debe hacerse una vez, luego el paquete está en nuestra computadora.
### Cargar un paquete
Para poder usar las funciones de un paquete, tenemos que cargarlo en nuestra sesión R. Hay tantos paquetes disponibles que R no cargará todos los que tenemos instalados por defecto, sino solo los que necesitaremos para nuestro estudio actual. Para cargar un paquete usamos la función `library()` o `require()`.
```{r 015-fonctions-63, eval = FALSE}
library("stats")
```
La carga del paquete debe hacerse cada vez que queremos ejecutar nuestro código, por lo tanto, es una parte integral de nuestro script.
### Portabilidad del código
Acabamos de ver que la instalación de un paquete solo se debe hacer una vez por computadora, y que la carga de un paquete se debe lograr para cada nueva sesión de R. Si uno cambia de computadora o si compartimos un script con colegas, puede haber errores de ejecución relacionados con la falta de instalación de un paquete. Para superar este problema, se recomienda utilizar una función que verifique si los paquetes necesarios para ejecutar un script están instalados; si es necesario, instálelos y luego cárguelos. Hay muchas funciones para hacer esto en Internet. La solución que proponemos aquí es una mezcla adaptada de diferentes fuentes. No es necesario comprender los detalles de este script por el momento, sino simplemente comprender lo que hace. Este es un ejemplo para el paquete `stats` y `graphics`, dos paquetes que ya estan presente con la versión básica de R, pero podemos tratar todos los paquetes disponibles en CRAN; la lista se puede encontrar aquí: https://cran.r-project.org/web/packages/available_packages_by_name.html.
```{r 015-fonctions-64, eval = FALSE}
pkgCheck <- function(packages){
for(x in packages){
try(if(!require(x, character.only = TRUE)){
install.packages(x, dependencies = TRUE)
if(!require(x, character.only = TRUE)){
stop()
}
})
}
}
pkgCheck(c("stats", "graphics"))
```
Alternativamente, podemos usar la función `.packages()` para listar los paquetes disponibles en el CRAN en orden alfabético.
```{r 015-fonctions-65}
head(.packages(all.available = TRUE), n = 30)
```
La función `pkgCheck()` asegura la **portabilidad** de nuestros scripts: funcionarán en todas las computadoras sin tener que realizar ningún cambio. Por lo tanto, nuestros scipts pueden adjuntarse, por ejemplo, a nuestros artículos científicos y así garantizar la **reproducibilidad** de nuestros resultados.
## Conclusión
Felicitaciones! Ahora sabemos reconocer y usar una función, sabemos cómo buscar ayuda para una función e incluso sabemos escribir nuestras propias funciones. También sabemos que hay muchas funciones desarrolladas por la comunidad de usuarios de R dentro de paquetes (*packages*) que sabemos cómo instalar y cargar, y asegurar la **portabilidad** de nuestros scripts de una computadora a otra (importante para la **reproducibilidad de los resultados**). El próximo capítulo se enfocará en leer y escribir archivos porque nuestros datos suelen estar en archivos de texto u hojas de cálculo.