-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnotation.qmd
470 lines (339 loc) · 19.5 KB
/
notation.qmd
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
# Notación de R {#notation}
Ahora que tiene una baraja de cartas, necesita una forma de hacer: Primero, querrás una forma de barajar el mazo de vez en cuando. Y luego, querrás repartir cartas del mazo (una carta a la vez, cualquiera que sea la carta que esté arriba, no somos tramposos).
Para hacer estas cosas, deberá trabajar con los valores individuales dentro de su data frame, una tarea esencial para la ciencia de datos. Por ejemplo, para repartir una carta de la parte superior de su mazo, deberá escribir una función que seleccione la primera fila de valores en su data frame, como esta
``` r
repartir(mazo)
## cara palo valor
## rey picas 13
```
Puede seleccionar valores dentro de un objeto de R con el sistema de notación de R.
## Selección de Valores
R tiene un sistema de notación que le permite extraer valores de objetos de R. Para extraer un valor o un conjunto de valores de un data frame, escriba el nombre del data frame seguido de un par de corchetes:
``` r
mazo[ , ]
```
Entre paréntesis irán dos índices separados por una coma. Los índices le dicen a R qué valores devolver. R usará el primer índice para crear un subconjunto de las filas del data frame y el segundo índice para crear un subconjunto de las columnas.
Tiene una opción cuando se trata de escribir índices. Hay seis formas diferentes de escribir un índice para R, y cada una hace algo ligeramente diferente. Todas son muy simples y bastante prácticas, así que echemos un vistazo a cada una de ellas. Puede crear índices con:
- Enteros positivos
- Enteros negativos
- Cero
- Espacios en blanco
- Valores lógicos
- Nombres
La forma más simple de escribir índices es la de los números enteros positivos.
### Números Enteros Positivos
R trata los enteros positivos como la notación *ij* en álgebra lineal: `mazo[i,j]` devolverá el valor de `mazo` que está en la *iésima* fila y la *jésima* columna, Figura @fig-positive. Tenga en cuenta que *i* y *j* solo necesitan ser números enteros en el sentido matemático. Se pueden guardar como números en R
``` r
head(mazo)
## cara palo valor
## rey picas 13
## reina picas 12
## jota picas 11
## diez picas 10
## nueve picas 9
## ocho picas 8
mazo[1, 1]
## "rey"
```
Para extraer más de un valor, utilice un vector de enteros positivos. Por ejemplo, puedes devolver la primera fila de `mazo` con `mazo[1, c(1, 2, 3)]` o `mazo[1, 1:3]`:
``` r
mazo[1, c(1, 2, 3)]
## cara palo valor
## rey picas 13
```
R devolverá los valores de `mazo` que están tanto en la primera fila como en la primera, segunda y tercera columna. Tenga en cuenta que R en realidad no eliminará estos valores de `mazo`. R le dará un nuevo conjunto de valores que son copias de los valores originales. Luego puede guardar este nuevo conjunto en un objeto de R con el operador de asignación de R:
``` r
nuevo <- mazo[1, c(1, 2, 3)]
nuevo
## cara palo valor
## rey picas 13
```
::: callout-tip
**Repetición**
Si repite un número en su índice, R devolverá los valores correspondientes más de una vez en su "subconjunto". Este código devolverá la primera fila de `mazo` dos veces:
```
mazo[c(1, 1), c(1, 2, 3)]
## cara palo valor
## rey picas 13
## rey picas 13
```
:::
```{r}
#| label: fig-positive
#| echo: FALSE
#| fig.cap: >
#| R usa el sistema de notación _ij_ del álgebra lineal. Los comandos
#| de esta figura devolverán los valores sombreados.
knitr::include_graphics("images/hopr_0401.png")
```
El sistema de notación de R no se limita a tramas de datos. Puede usar la misma sintaxis para seleccionar valores en cualquier objeto de R, siempre que proporcione un índice para cada dimensión del objeto. Entonces, por ejemplo, puede crear un subconjunto de un vector (que tiene una dimensión) con un solo índice:
``` r
vec <- c(6, 1, 3, 6, 10, 5)
vec[1:3]
## 6 1 3
```
::: callout-tip
**La indexación comienza en 1**
En algunos lenguajes de programación, la indexación comienza con 0. Esto significa que 0 devuelve el primer elemento de un vector, 1 devuelve el segundo elemento y así sucesivamente.
Este no es el caso con R. La indexación en R se comporta como la indexación en álgebra lineal. El primer elemento siempre está indexado por 1. ¿Por qué R es diferente? Tal vez porque fue escrito para matemáticos. Aquellos de nosotros que aprendimos a indexar en un curso de álgebra lineal nos preguntamos por qué los programadores de computadoras comienzan con 0.
:::
::: callout-tip
**drop = FALSE**
Si selecciona dos o más columnas de un data frame, R devolverá un nuevo data frame:
```
mazo[1:2, 1:2]
## cara palo
## rey picas
## reina picas
```
Sin embargo, si selecciona una sola columna, R devolverá un vector:
```
mazo[1:2, 1]
## "rey" "reina"
```
Si prefiere un data frame, puede agregar el argumento opcional `drop = FALSE` entre corchetes:
```
mazo[1:2, 1, drop = FALSE]
## cara
## rey
## reina
```
Este método también funciona para seleccionar una sola columna de una matriz o arreglo.
:::
### Números Enteros Negativos
Los enteros negativos hacen exactamente lo contrario de los enteros positivos al indexar. R devolverá todos los elementos *excepto* los elementos en un índice negativo. Por ejemplo, `mazo[-1, 1:3]` devolverá todo *menos* la primera fila de `mazo`. `mazo[-(2:52), 1:3]` devolverá la primera fila (y excluirá todo lo demás):
``` r
mazo[-(2:52), 1:3]
## cara palo valor
## rey picas 13
```
Los enteros negativos son una forma más eficaz de crear subconjuntos que los enteros positivos si desea incluir la mayoría de las filas o columnas de un data frame.
R devolverá un error si intenta emparejar un entero negativo con un entero positivo en el *mismo* índice:
``` r
mazo[c(-1, 1), 1]
## Error in xj[i] : only 0's may be mixed with negative subscripts
```
Sin embargo, puede usar enteros negativos y positivos para crear subconjuntos de un objeto si los usa en índices *diferentes* (por ejemplo, si usa uno en el índice de filas y otro en el índice de columnas, como `mazo[-1, 1]`) .
### Cero
¿Qué pasaría si usaras el cero como índice? El cero no es ni un entero positivo ni un entero negativo, pero R aún lo usará para hacer un tipo de subconjunto. R no devolverá nada de una dimensión cuando use cero como índice. Esto crea un objeto vacío:
``` r
mazo[0, 0]
## data frame with 0 columns and 0 rows
```
Para ser honesto, la indexación con cero no es muy útil.
### Espacios en Blanco
Puede usar un espacio en blanco para decirle a R que extraiga *todos* los valores en una dimensión. Esto le permite crear subconjuntos de un objeto en una dimensión pero no en las otras, lo cual es útil para extraer filas o columnas enteras de un data frame:
``` r
mazo[1, ]
## cara palo valor
## rey picas 13
```
### Valores Lógicos {#logic}
Si proporciona un vector de `TRUE` y `FALSE` como su índice, R hará coincidir cada `TRUE` y `FALSE` con una fila en su data frame (o una columna dependiendo de dónde coloque el índice). R luego devolverá cada fila que corresponda a un `TRUE`, Figura @fig-logicals.
Puede ser útil imaginar a R leyendo el data frame y preguntando: "¿Debería devolver la \_i_ésima fila de la estructura de datos?" y luego consultando el valor \_i_ésimo del índice para su respuesta. Para que este sistema funcione, su vector debe ser tan largo como la dimensión que está tratando de dividir en subconjuntos:
``` r
mazo[1, c(TRUE, TRUE, FALSE)]
## cara palo
## rey picas
filas <- c(TRUE, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F, F,
F, F, F, F, F, F, F, F, F, F, F, F, F, F)
mazo[filas, ]
## cara palo valor
## rey picas 13
```
```{r}
#| label: fig-logicals
#| echo: FALSE
#| fig.cap: >
#| Puede usar vectores de TRUE y FALSE para decirle a R exactamente qué
#| valores desea extraer y cuáles no. El comando devolvería solo
#| los números 1, 6 y 5.
knitr::include_graphics("images/hopr_0402.png")
```
Este sistema puede parecer extraño, ¿quién quiere escribir tantos `TRUE` y `FALSE`?, pero se volverá muy poderoso en [Modificación de Valores](#modify).
### Nombres
Finalmente, puede solicitar los elementos que desee por nombre, si su objeto tiene nombres (consulte [Nombres](#names)). Esta es una forma común de extraer las columnas de un data frame, ya que las columnas casi siempre tienen nombres:
``` r
mazo[1, c("cara", "palo", "valor")]
## cara palo valor
## rey picas 13
# toda la columna de valor
mazo[ , "valor"]
## 13 12 11 10 9 8 7 6 5 4 3 2 1 13 12 11 10 9 8
## 7 6 5 4 3 2 1 13 12 11 10 9 8 7 6 5 4 3 2
## 1 13 12 11 10 9 8 7 6 5 4 3 2 1
```
## Repartir una Carta
Ahora que conoce los conceptos básicos del sistema de notación de R, pongámoslo en práctica.
**Ejercicio 6.1 (Repartir una Carta)** Complete el siguiente código para crear una función que devuelva la primera fila de un data frame:
``` r
repartir <- function(cartas) {
# ?
}
```
*Solución.* Puede usar cualquiera de los sistemas que devuelven la primera fila de su data frame para escribir una función de `repartir`. Usaré enteros positivos y espacios en blanco porque creo que son fáciles de entender:
``` r
repartir <- function(cartas) {
cartas[1, ]
}
```
La función hace exactamente lo que quieres: reparte la carta superior de tu conjunto de datos. Sin embargo, la función se vuelve menos impresionante si ejecuta \`repartir\`\` una y otra vez:
``` r
repartir(mazo)
## cara palo valor
## rey picas 13
repartir(mazo)
## cara palo valor
## rey picas 13
repartir(mazo)
## cara palo valor
## rey picas 13
```
`repartir` siempre devuelve el rey de picas porque `mazo` no sabe que hemos repartido la carta. Por lo tanto, el rey de picas se queda donde está, en la parte superior de la baraja, listo para ser repartido nuevamente. Este es un problema difícil de resolver, y lo trataremos en [Entornos](#environment). Mientras tanto, puedes solucionar el problema barajando tu mazo después de cada vez que repartes. Entonces una nueva tarjeta siempre estará en la parte superior.
Barajar es un compromiso temporal: las probabilidades en juego en su mazo no coincidirán con las probabilidades que ocurren cuando juega un juego con un solo mazo de cartas. Por ejemplo, seguirá existiendo la probabilidad de que el rey de picas aparezca dos veces seguidas. Sin embargo, las cosas no son tan malas como pueden parecer. La mayoría de los casinos usan cinco o seis mazos a la vez en los juegos de cartas para evitar el conteo de cartas. Las probabilidades que encontraría en esas situaciones son muy parecidas a las que crearemos aquí.
## Barajar el Mazo
Cuando barajas una baraja de cartas real, reorganizas aleatoriamente el orden de las cartas. En su mazo virtual, cada carta es una fila en un data frame. Para barajar el mazo, debe reordenar aleatoriamente las filas en el data frame. Se puede hacer esto? ¡Apuesta! Y ya sabes todo lo que necesitas para hacerlo.
Esto puede sonar tonto, pero comience extrayendo cada fila en su data frame:
``` r
mazo2 <- mazo[1:52, ]
head(mazo2)
## cara palo valor
## rey picas 13
## reina picas 12
## jota picas 11
## diez picas 10
## nueve picas 9
## ocho picas 8
```
¿Qué obtienes? Un nuevo data frame cuyo orden no ha cambiado en absoluto. ¿Qué pasa si le pides a R que extraiga las filas en un orden diferente? Por ejemplo, podría pedir la fila 2, *luego* la fila 1 y luego el resto de las tarjetas:
``` r
mazo3 <- mazo[c(2, 1, 3:52), ]
head(mazo3)
## cara palo valor
## reina picas 12
## rey picas 13
## jota picas 11
## diez picas 10
## nueve picas 9
## ocho picas 8
```
R cumple. Obtendrá todas las filas de vuelta, y vendrán en el orden que las pida. Si desea que las filas aparezcan en un orden aleatorio, debe ordenar los números enteros del 1 al 52 en un orden aleatorio y usar los resultados como un índice de fila. ¿Cómo podrías generar una colección tan aleatoria de enteros? Con nuestra amigable función vecinal `sample`:
``` r
aleatorio <- sample(1:52, size = 52)
aleatorio
## 35 28 39 9 18 29 26 45 47 48 23 22 21 16 32 38 1 15 20
## 11 2 4 14 49 34 25 8 6 10 41 46 17 33 5 7 44 3 27
## 50 12 51 40 52 24 19 13 42 37 43 36 31 30
mazo4 <- mazo[aleatorio, ]
head(mazo4)
## cara palo valor
## cinco diamantes 5
## reina diamantes 12
## as diamantes 1
## cinco picas 5
## nueve trevoles 9
## jota diamantes 11
```
Ahora el nuevo conjunto está verdaderamente barajado. Terminará una vez que ajuste estos pasos en una función.
Ejercicio 6.2 (Barajar el Mazo) Usa las ideas anteriores para escribir una función `barajar`. `barajar` debe tomar un data frame y devolver una copia aleatoria del data frame.
*Solución.* Su función `barajar` se verá como la siguiente:
``` r
barajar <- function(cartas) {
aleatorio <- sample(1:52, size = 52)
cartas[aleatorio, ]
}
```
¡Buen trabajo! Ahora puedes barajar tus cartas entre cada vez que reparte:
``` r
repartir(mazo)
## cara palo valor
## rey picas 13
mazo2 <- barajar(mazo)
repartir(mazo2)
## cara palo valor
## jota treboles 11
```
## Signos de Dólar y Corchetes Dobles
Dos tipos de objetos en R obedecen a un segundo sistema opcional de notación. Puede extraer valores de data frame y listas con la sintaxis `$`. Encontrará la sintaxis `$` una y otra vez como programador de R, así que examinemos cómo funciona.
Para seleccionar una columna de un data frame, escriba el nombre del data frame y el nombre de la columna separados por un `$`. Tenga en cuenta que no se deben colocar comillas alrededor del nombre de la columna:
``` r
mazo$valor
## 13 12 11 10 9 8 7 6 5 4 3 2 1 13 12 11 10 9 8 7
## 6 5 4 3 2 1 13 12 11 10 9 8 7 6 5 4 3 2 1 13
## 12 11 10 9 8 7 6 5 4 3 2 1
```
R devolverá todos los valores de la columna como un vector. Esta notación `$` es increíblemente útil porque a menudo almacenará las variables de sus conjuntos de datos como columnas en un data frame.De vez en cuando, querrá ejecutar una función como `mean` o `median` en los valores de una variable. En R, estas funciones esperan un vector de valores como entrada, y `mazo$valor` entrega tus datos en el formato correcto:
``` r
mean(mazo$valor)
## 7
median(mazo$valor)
## 7
```
Puede usar la misma notación `$` con los elementos de una lista, si tienen nombres. Esta notación también tiene una ventaja con las listas. Si crea subconjuntos de una lista de la manera habitual, R devolverá una lista *nueva* que tiene los elementos que solicitó. Esto es cierto incluso si solo solicita un solo elemento.
Para ver esto, haz una lista:
``` r
lst <- list(numeros = c(1, 2), logicos = TRUE, caracteres = c("a", "b", "c"))
lst
## $numeros
## [1] 1 2
## $logicos
## [1] TRUE
## $caracteres
## [1] "a" "b" "c"
```
Y luego obten el subconjunto:
``` r
lst[1]
## $numeros
## [1] 1 2
```
El resultado es una *lista* más pequeña con un elemento. Ese elemento es el vector `c(1, 2)`. Esto puede resultar molesto porque muchas funciones de R no funcionan con listas. Por ejemplo, `sum(lst[1])` devolverá un error. Sería horrible si una vez que almacena un vector en una lista, solo pudiera recuperarlo como una lista:
``` r
sum(lst[1])
## Error in sum(lst[1]) : invalid 'type' (list) of argument
```
Cuando usa la notación `$`, R devolverá los valores seleccionados tal como son, sin una estructura de lista a su alrededor:
``` r
lst$numeros
## 1 2
```
Luego puede enviar inmediatamente los resultados a una función:
``` r
sum(lst$numeros)
## 3
```
Si los elementos de su lista no tienen nombres (o no desea usar los nombres), puede usar dos corchetes, en lugar de uno, para crear un subconjunto de la lista. Esta notación hará lo mismo que la notación `$`:
``` r
lst[[1]]
## 1 2
```
En otras palabras, si crea un subconjunto de una lista con notación de corchete único, R devolverá una lista más pequeña. Si crea un subconjunto de una lista con notación de corchetes dobles, R devolverá solo los valores que estaban dentro de un elemento de la lista. Puede combinar esta función con cualquiera de los métodos de indexación de R:
``` r
lst["numeros"]
## $numeros
## [1] 1 2
lst[["numeros"]]
## 1 2
```
Esta diferencia es sutil pero importante. En la comunidad de R, hay una forma popular y útil de pensar en ello, Figure @fig-trains. Imagina que cada lista es un tren y cada elemento es un vagón de tren. Cuando usa corchetes individuales, R selecciona vagones de tren individuales y los devuelve como un tren nuevo. Cada vagón mantiene su contenido, pero ese contenido todavía está dentro de un vagón de tren (es decir, una lista). Cuando usa corchetes dobles, R en realidad descarga el automóvil y le devuelve el contenido.
```{r}
#| label: fig-trains
#| echo: FALSE
#| fig.cap: >
#| Puede ser útil pensar en su lista como un tren. Use corchetes simples
#| para seleccionar vagones de tren, corchetes dobles para seleccionar el
#| contenido dentro de un vagón.
knitr::include_graphics("images/hopr_0403.png")
```
::: callout-important
**Nunca use attach**
En los primeros días de R, se hizo popular usar `attach()` en un conjunto de datos una vez que lo tenía cargado. ¡No hagas esto! `attach` recrea un entorno informático similar a los utilizados en otras aplicaciones estadísticas como Stata y SPSS, que gustaron a los usuarios cruzados. Sin embargo, R no es Stata o SPSS. R está optimizado para usar el entorno informático de R, y ejecutar `attach()` puede causar confusión con algunas funciones de R.
¿Qué hace `attach()`? En la superficie, `attach` te ahorra escribir. Si adjunta el conjunto de datos `mazo`, puede hacer referencia a cada una de sus variables por nombre; en lugar de escribir `mazo$cara`, puedes simplemente escribir `cara`. Pero escribir no está mal. Te da la oportunidad de ser explícito, y en la programación de computadoras, explícito es bueno. Adjuntar un conjunto de datos crea la posibilidad de que R confunda dos nombres de variables. Si esto ocurre dentro de una función, es probable que obtenga resultados inutilizables y un mensaje de error inútil para explicar lo que sucedió.
:::
Ahora que es un experto en recuperar valores almacenados en R, resumamos lo que ha logrado.
## Resumen
Ha aprendido a acceder a los valores que se han almacenado en R. Puede recuperar una copia de los valores que viven dentro de un data frame y usar las copias para nuevos cálculos.
De hecho, puede usar el sistema de notación de R para acceder a valores en cualquier objeto R. Para usarlo, escriba el nombre de un objeto seguido de corchetes e índices. Si su objeto es unidimensional, como un vector, solo necesita proporcionar un índice. Si es bidimensional, como un data frame, debe proporcionar dos índices separados por una coma. Y, si es *n*-dimensional, debe proporcionar *n* índices, cada uno separado por una coma.
En [Modificación de Valores](#modify), llevará este sistema un paso más allá y aprenderá a cambiar los valores reales que se almacenan dentro de su data frame. Todo esto se suma a algo especial: control completo de sus datos. Ahora puede almacenar sus datos en su computadora, recuperar valores individuales a voluntad y usar su computadora para realizar cálculos correctos con esos valores.
¿Suena esto básico? Puede serlo, pero también es poderoso y esencial para una ciencia de datos eficiente. Ya no necesitas memorizar todo en tu cabeza, ni preocuparte por hacer mal el cálculo mental. Este control de bajo nivel sobre sus datos también es un requisito previo para programas de R más eficientes, el tema del [Proyecto 3: Máquina Tragamonedas](#slots).