-
Notifications
You must be signed in to change notification settings - Fork 1
/
Subsetting.qmd
725 lines (475 loc) · 29.5 KB
/
Subsetting.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
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
# Subconjunto {#sec-subsetting}
```{r setup, include = FALSE}
source("common.R")
rownames(mtcars) <- NULL
```
## Introducción
\index{subsetting}
Los operadores de subconjuntos de R son rápidos y potentes. Dominarlos le permite realizar operaciones complejas de manera sucinta de una manera que pocos otros idiomas pueden igualar. La creación de subconjuntos en R es fácil de aprender pero difícil de dominar porque necesita interiorizar una serie de conceptos interrelacionados:
- Hay seis formas de crear subconjuntos de vectores atómicos.
- Hay tres operadores de subconjuntos, `[[`, `[` y `$`.
- Los operadores de creación de subconjuntos interactúan de manera diferente con diferentes tipos de vectores (por ejemplo, vectores atómicos, listas, factores, matrices y data frames).
- La creación de subconjuntos se puede combinar con la asignación.
La creación de subconjuntos es un complemento natural de `str()`. Mientras que `str()` le muestra todas las piezas de cualquier objeto (su estructura), la creación de subconjuntos le permite extraer las piezas que le interesan. Para objetos grandes y complejos, recomiendo usar el RStudio Viewer interactivo, que puedes activar con `View(my_object)`.
### Prueba {.unnumbered}
Responda este breve cuestionario para determinar si necesita leer este capítulo. Si las respuestas le vienen a la mente rápidamente, puede saltarse cómodamente este capítulo. Comprueba tus respuestas en la @sec-subsetting-answers.
1. ¿Cuál es el resultado de subdividir un vector con enteros positivos, enteros negativos, un vector lógico o un vector de caracteres?
2. ¿Cuál es la diferencia entre `[`, `[[` y `$` cuando se aplica a una lista?
3. ¿Cuándo debería usar `drop = FALSE`?
4. Si `x` es una matriz, ¿qué hace `x[] <- 0`? ¿En qué se diferencia de `x <- 0`?
5. ¿Cómo puede usar un vector con nombre para volver a etiquetar variables categóricas?
### Estructura {.unnumbered}
- La @sec-subset-multiple comienza enseñándote acerca de `[`. Aprenderá las seis formas de crear subconjuntos de vectores atómicos. Luego aprenderá cómo actúan esas seis formas cuando se usan para crear subconjuntos de listas, matrices y data frames.
- La @sec-subset-single amplía su conocimiento de los operadores de subconjuntos para incluir `[[` y `$` y se centra en los principios importantes de simplificar frente a preservar.
- La @sec-subassignment aprenderá el arte de la subasignación, que combina subconjuntos y asignación para modificar partes de un objeto.
- La @sec-applications llo guía a través de ocho aplicaciones importantes, pero no obvias, de subconjuntos para resolver problemas que a menudo encuentra en el análisis de datos.
## Selección de varios elementos {#sec-subset-multiple}
\index{[}
Utilice `[` para seleccionar cualquier número de elementos de un vector. Para ilustrar, aplicaré `[` a vectores atómicos 1D, y luego mostraré cómo esto se generaliza a objetos más complejos y más dimensiones.
### Vectores atómicos
\index{subsetting!atomic vectors} \index{atomic vectors!subsetting}
Exploremos los diferentes tipos de subconjuntos con un vector simple, `x`.
```{r}
x <- c(2.1, 4.2, 3.3, 5.4)
```
Tenga en cuenta que el número después del punto decimal representa la posición original en el vector.
Hay seis cosas que puede usar para crear subconjuntos de un vector:
- **Los enteros positivos** devuelven elementos en las posiciones especificadas:
```{r}
x[c(3, 1)]
x[order(x)]
# Los índices duplicados duplicarán los valores
x[c(1, 1)]
# Los números reales se truncan silenciosamente a enteros
x[c(2.1, 2.9)]
```
- **Los enteros negativos** excluyen elementos en las posiciones especificadas:
```{r}
x[-c(3, 1)]
```
Tenga en cuenta que no puede mezclar números enteros positivos y negativos en un solo subconjunto:
```{r, error = TRUE}
x[c(-1, 2)]
```
- **Los vectores lógicos** seleccionan elementos donde el valor lógico correspondiente es `TRUE`. Este es probablemente el tipo de subconjunto más útil porque puede escribir una expresión que usa un vector lógico:
```{r}
x[c(TRUE, TRUE, FALSE, FALSE)]
x[x > 3]
```
\index{recycling} En `x[y]`, ¿qué sucede si `x` e `y` tienen longitudes diferentes? El comportamiento está controlado por las **reglas de reciclaje**, donde el más corto de los dos se recicla al largo del más largo. Esto es conveniente y fácil de entender cuando uno de `x` e `y` tiene la longitud uno, pero recomiendo evitar el reciclaje para otras longitudes porque las reglas se aplican de manera inconsistente en la base R.
```{r}
x[c(TRUE, FALSE)]
# Equivalent to
x[c(TRUE, FALSE, TRUE, FALSE)]
```
Tenga en cuenta que un valor faltante en el índice siempre produce un valor faltante en la salida:
```{r}
x[c(TRUE, TRUE, NA, FALSE)]
```
- **Nada** devuelve el vector original. Esto no es útil para vectores 1D, pero, como verá en breve, es muy útil para matrices, data frames y arreglos. También puede ser útil junto con la asignación.
```{r}
x[]
```
- **Zero** devuelve un vector de longitud cero. Esto no es algo que normalmente haga a propósito, pero puede ser útil para generar datos de prueba.
```{r}
x[0]
```
- Si el vector tiene nombre, también puede usar **vectores de caracteres** para devolver elementos con nombres coincidentes.
```{r}
(y <- setNames(x, letters[1:4]))
y[c("d", "c", "a")]
# Al igual que los índices enteros, puede repetir índices
y[c("a", "a", "a")]
# Al crear un subconjunto con [, los nombres siempre coinciden exactamente
z <- c(abc = 1, def = 2)
z[c("a", "d")]
```
NB: Los factores no se tratan de manera especial cuando se subdividen. Esto significa que la creación de subconjuntos utilizará el vector entero subyacente, no los niveles de caracteres. Esto suele ser inesperado, por lo que debe evitar subconjuntos con factores:
```{r}
y[factor("b")]
```
### Listas
\index{lists!subsetting} \index{subsetting!lists}
La creación de subconjuntos de una lista funciona de la misma manera que la creación de subconjuntos de un vector atómico. Usar `[` siempre devuelve una lista; `[[` y `$`, como se describe en la @sec-subset-single, le permiten extraer elementos de una lista.
### Matrices y arreglos {#sec-matrix-subsetting}
\index{subsetting!arrays} \index{arrays!subsetting}
Puede crear subconjuntos de estructuras de dimensiones superiores de tres maneras:
- Con múltiples vectores.
- Con un solo vector.
- Con una matriz.
La forma más común de crear subconjuntos de matrices (2D) y arreglos (\>2D) es una generalización simple de subconjuntos 1D: proporcione un índice 1D para cada dimensión, separados por una coma. El subconjunto en blanco ahora es útil porque le permite mantener todas las filas o todas las columnas.
```{r}
a <- matrix(1:9, nrow = 3)
colnames(a) <- c("A", "B", "C")
a[1:2, ]
a[c(TRUE, FALSE, TRUE), c("B", "A")]
a[0, -2]
```
Por defecto, `[` simplifica los resultados a la dimensionalidad más baja posible. Por ejemplo, las dos expresiones siguientes devuelven vectores 1D. Aprenderá cómo evitar "reducir" el número de dimensiones en la @sec-simplify-preserve:
```{r}
a[1, ]
a[1, 1]
```
Debido a que tanto las matrices como los arreglos son solo vectores con atributos especiales, puede crear subconjuntos con un solo vector, como si fueran un vector 1D. Tenga en cuenta que las matrices en R se almacenan en orden de columna principal:
```{r}
vals <- outer(1:5, 1:5, FUN = "paste", sep = ",")
vals
vals[c(4, 15)]
```
También puede crear subconjuntos de estructuras de datos de mayor dimensión con una matriz de enteros (o, si se nombra, una matriz de caracteres). Cada fila de la matriz especifica la ubicación de un valor y cada columna corresponde a una dimensión de la matriz. Esto significa que puede usar una matriz de 2 columnas para crear un subconjunto de una matriz, una matriz de 3 columnas para crear un subconjunto de una matriz 3D, etc. El resultado es un vector de valores:
```{r}
select <- matrix(ncol = 2, byrow = TRUE, c(
1, 1,
3, 1,
2, 4
))
vals[select]
```
### Data frames y tibbles {#sec-df-subsetting}
\index{subsetting!data frames} \index{data frames!subsetting}
Data frames tienen las características tanto de listas como de matrices:
- Cuando se subdividen con un solo índice, se comportan como listas e indexan las columnas, por lo que `df[1:2]` selecciona las dos primeras columnas.
- Al crear subconjuntos con dos índices, se comportan como matrices, por lo que `df[1:3, ]` selecciona las primeras tres *filas* (y todas las columnas)[^subsetting-1].
[^subsetting-1]: Si viene de Python, es probable que esto sea confuso, ya que probablemente esperaría que `df[1:3, 1:2]` seleccione tres columnas y dos filas. Generalmente, R "piensa" en las dimensiones en términos de filas y columnas, mientras que Python lo hace en términos de columnas y filas.
```{r}
df <- data.frame(x = 1:3, y = 3:1, z = letters[1:3])
df[df$x == 2, ]
df[c(1, 3), ]
# Hay dos formas de seleccionar columnas de un data frame
# Como una lista
df[c("x", "z")]
# Como una matriz
df[, c("x", "z")]
# Hay una diferencia importante si selecciona una sola
# columna: el subconjunto de la matriz se simplifica de forma predeterminada, el
# subconjunto de la lista no
str(df["x"])
str(df[, "x"])
```
Subdividir un tibble con `[` siempre devuelve un tibble:
```{r}
df <- tibble::tibble(x = 1:3, y = 3:1, z = letters[1:3])
str(df["x"])
str(df[, "x"])
```
### Preservando la dimensionalidad {#sec-simplify-preserve}
\index{drop = FALSE}
\index{subsetting!simplifying} \index{subsetting!preserving}
De forma predeterminada, subdividir una matriz o data frame con un solo número, un solo nombre o un vector lógico que contenga un solo `TRUE` simplificará la salida devuelta, es decir, devolverá un objeto con menor dimensionalidad. Para conservar la dimensionalidad original, debe usar `drop = FALSE`.
- Para matrices y arreglos, se eliminarán todas las dimensiones con longitud 1:
```{r}
a <- matrix(1:4, nrow = 2)
str(a[1, ])
str(a[1, , drop = FALSE])
```
- Data frames con una sola columna devolverán solo el contenido de esa columna:
```{r}
df <- data.frame(a = 1:2, b = 1:2)
str(df[, "a"])
str(df[, "a", drop = FALSE])
```
El comportamiento predeterminado `drop = TRUE` es una fuente común de errores en las funciones: verifica su código con un data frame o matriz con varias columnas, y funciona. Seis meses después, usted (u otra persona) lo usa con un data frame de una sola columna y falla con un error desconcertante. Al escribir funciones, acostúmbrese a usar siempre `drop = FALSE` al subdividir un objeto 2D. Por esta razón, los tibbles tienen por defecto `drop = FALSE`, y `[` siempre devuelve otro tibble.
El subconjunto de factores también tiene un argumento `drop`, pero su significado es bastante diferente. Controla si se conservan o no los niveles (en lugar de las dimensiones), y su valor predeterminado es `FALSE`. Si encuentra que está usando `drop = TRUE` mucho, a menudo es una señal de que debería estar usando un vector de caracteres en lugar de un factor.
```{r}
z <- factor(c("a", "b"))
z[1]
z[1, drop = TRUE]
```
### Ejercicios
1. Solucione cada uno de los siguientes errores comunes de creación de subconjuntos de data frames:
```{r, eval = FALSE}
mtcars[mtcars$cyl = 4, ]
mtcars[-1:4, ]
mtcars[mtcars$cyl <= 5]
mtcars[mtcars$cyl == 4 | 6, ]
```
2. ¿Por qué el siguiente código arroja cinco valores faltantes? (Pista: ¿por qué es diferente de `x[NA_real_]`?)
```{r}
x <- 1:5
x[NA]
```
3. ¿Qué devuelve `upper.tri()`? ¿Cómo funciona el subconjunto de una matriz con él? ¿Necesitamos reglas adicionales de creación de subconjuntos para describir su comportamiento?
```{r, eval = FALSE}
x <- outer(1:5, 1:5, FUN = "*")
x[upper.tri(x)]
```
4. ¿Por qué `mtcars[1:20]` devuelve un error? ¿En qué se diferencia de los `mtcars[1:20, ]` similares?
5. Implemente su propia función que extraiga las entradas diagonales de una matriz (debería comportarse como `diag(x)` donde `x` es una matriz).
6. ¿Qué hace `df[is.na(df)] <- 0`? ¿Como funciona?
## Selección de un solo elemento {#sec-subset-single}
\index{subsetting!lists} \index{lists!subsetting}
Hay otros dos operadores de subconjuntos: `[[` y `$`. `[[` se usa para extraer elementos individuales, mientras que `x$y` es una abreviatura útil para `x[["y"]]`.
### `[[`
\index{[[}
`[[` es más importante cuando se trabaja con listas porque subdividir una lista con `[` siempre devuelve una lista más pequeña. Para ayudar a que esto sea más fácil de entender, podemos usar una metáfora:
> Si la lista `x` es un tren que transporta objetos, entonces `x[[5]]` es el objeto en el vagón 5; `x[4:6]` es un tren de vagones 4-6.
>
> --- \@RLangTip, <https://twitter.com/RLangTip/status/268375867468681216>
Usemos esta metáfora para hacer una lista simple:
```{r}
x <- list(1:3, "a", 4:6)
```
```{r, echo = FALSE, out.width = NULL}
knitr::include_graphics("diagrams/subsetting/train.png")
```
Al extraer un solo elemento, tiene dos opciones: puede crear un tren más pequeño, es decir, menos vagones, o puede extraer el contenido de un vagón en particular. Esta es la diferencia entre `[` y `[[`:
```{r, echo = FALSE, out.width = NULL}
knitr::include_graphics("diagrams/subsetting/train-single.png")
```
Al extraer elementos múltiples (¡o incluso cero!), debe hacer un tren más pequeño:
```{r, echo = FALSE, out.width = NULL}
knitr::include_graphics("diagrams/subsetting/train-multiple.png")
```
Debido a que `[[` solo puede devolver un solo elemento, debe usarlo con un solo entero positivo o una sola cadena. Si usa un vector con `[[`, se subdividirá recursivamente, es decir, `x[[c(1, 2)]]` es equivalente a `x[[1]][[2]]`. Esta es una característica peculiar que pocos conocen, así que recomiendo evitarla en favor de `purrr::pluck()`, sobre la cual aprenderá en la @sec-subsetting-oob.
Si bien debe usar `[[` cuando trabaja con listas, también recomendaría usarlo con vectores atómicos siempre que desee extraer un solo valor. Por ejemplo, en lugar de escribir:
```{r, eval = FALSE}
for (i in 2:length(x)) {
out[i] <- fun(x[i], out[i - 1])
}
```
Es mejor escribir:
```{r, eval = FALSE}
for (i in 2:length(x)) {
out[[i]] <- fun(x[[i]], out[[i - 1]])
}
```
Si lo hace, refuerza la expectativa de que está recibiendo y estableciendo valores individuales.
### `$`
\index{\$}
`$` es un operador abreviado: `x$y` es aproximadamente equivalente a `x[["y", exact = FALSE]]`. A menudo se usa para acceder a variables en un data frame, como en `mtcars$cyl` o `diamonds$carat`. Un error común con `$` es usarlo cuando tienes el nombre de una columna almacenada en una variable:
```{r, include = FALSE}
options(warnPartialMatchDollar = FALSE)
```
```{r}
var <- "cyl"
# No funciona - mtcars$var traducido a mtcars[["var"]]
mtcars$var
# En su lugar use [[
mtcars[[var]]
```
La única diferencia importante entre `$` y `[[` es que `$` automáticamente hace (de izquierda a derecha) coincidencias parciales sin previo aviso:
```{r}
x <- list(abc = 1)
x$a
x[["a"]]
```
\index{options!warnPartialMatchDollar@\texttt{warnPartialMatchDollar}} Para ayudar a evitar este comportamiento, recomiendo configurar la opción global `warnPartialMatchDollar` en `TRUE`:
```{r}
options(warnPartialMatchDollar = TRUE)
x$a
```
(Para data frames, también puede evitar este problema usando tibbles, que nunca hacen coincidencias parciales.)
### Índices faltantes y fuera de los límites {#sec-subsetting-oob}
\index{subsetting!with NA \& NULL} \index{subsetting!out of bounds} \index{pluck()} \index{chuck()}
Es útil comprender lo que sucede con `[[` cuando usa un índice "no válido". La siguiente tabla resume lo que sucede cuando crea un subconjunto de un vector lógico, una lista y un `NULL` con un objeto de longitud cero (como `NULL` o `logical()`), valores fuera de los límites (OOB) o un valor faltante (por ejemplo, `NA_integer_`) con `[[`. Cada celda muestra el resultado de dividir la estructura de datos nombrada en la fila por el tipo de índice descrito en la columna. Solo he mostrado los resultados para vectores lógicos, pero otros vectores atómicos se comportan de manera similar, devolviendo elementos del mismo tipo (NB: int = entero; chr = carácter).
| `row[[col]]` | Longitud cero | OOB (int) | OOB (chr) | Faltante |
|--------------|---------------|-----------|-----------|----------|
| Atómico | Error | Error | Error | Error |
| Lista | Error | Error | `NULL` | `NULL` |
| `NULL` | `NULL` | `NULL` | `NULL` | `NULL` |
```{r, eval = FALSE, echo = FALSE}
logical()[[1]]
logical()[["x"]]
logical()[[NA_real_]]
logical()[[NULL]]
list()[[1]]
list()[["x"]]
list()[[NA_real_]]
list()[[NULL]]
NULL[[1]]
NULL[["x"]]
NULL[[NA_real_]]
NULL[[NULL]]
```
Si se nombra el vector que se indexa, los nombres de los componentes OOB, faltantes o `NULL` serán `<NA>`.
Las inconsistencias en la tabla anterior llevaron al desarrollo de `purrr::pluck()` y `purrr::chuck()`. Cuando falta el elemento, `pluck()` siempre devuelve `NULL` (o el valor del argumento `.default`) y `chuck()` siempre arroja un error. El comportamiento de `pluck()` lo hace ideal para la indexación en estructuras de datos profundamente anidadas donde el componente que desea puede no existir (como es común cuando se trabaja con datos JSON de API web). `pluck()` también te permite mezclar índices enteros y de caracteres, y proporciona un valor predeterminado alternativo si un elemento no existe:
```{r}
x <- list(
a = list(1, 2, 3),
b = list(3, 4, 5)
)
purrr::pluck(x, "a", 1)
purrr::pluck(x, "c", 1)
purrr::pluck(x, "c", 1, .default = NA)
```
### `@` y `slot()`
Hay dos operadores de subconjuntos adicionales, que son necesarios para los objetos de S4: `@` (equivalente a `$`) y `slot()` (equivalente a `[[`). `@` es más restrictivo que `$` ya que devolverá un error si la ranura no existe. Estos se describen con más detalle en el @sec-s4.
### Ejercicios
1. Piense en tantas formas como sea posible para extraer el tercer valor de la variable `cyl` en el conjunto de datos `mtcars`.
2. Dado un modelo lineal, por ejemplo, `mod <- lm(mpg ~ wt, data = mtcars)`, extraiga los grados de libertad residuales. Luego extraiga la R al cuadrado del resumen del modelo (`summary (mod)`)
<!-- FIXME: más ejemplos -->
## Subconjunto y asignación {#sec-subassignment}
\index{subsetting!subassignment} \index{assignment!subassignment} \index{lists!removing an element}
Todos los operadores de subconjunto se pueden combinar con la asignación para modificar los valores seleccionados de un vector de entrada: esto se denomina subasignación. La forma básica es `x[i] <- valor`:
```{r}
x <- 1:5
x[c(1, 2)] <- c(101, 102)
x
```
Te recomiendo que te asegures de que `length(valor)` sea lo mismo que `length(x[i])`, y que `i` sea único. Esto se debe a que, si bien R reciclará si es necesario, esas reglas son complejas (particularmente si `i` contiene valores faltantes o duplicados) y pueden causar problemas.
Con las listas, puede usar `x[[i]] <- NULL` para eliminar un componente. Para agregar un literal `NULL`, use `x[i] <- list(NULL)`:
```{r}
x <- list(a = 1, b = 2)
x[["b"]] <- NULL
str(x)
y <- list(a = 1, b = 2)
y["b"] <- list(NULL)
str(y)
```
La creación de subconjuntos sin nada puede ser útil con la asignación porque conserva la estructura del objeto original. Compara las siguientes dos expresiones. En el primero, `mtcars` sigue siendo un data frame porque solo está cambiando el contenido de `mtcars`, no `mtcars` en sí. En el segundo, `mtcars` se convierte en una lista porque está cambiando el objeto al que está vinculado.
```{r, mtcars}
mtcars[] <- lapply(mtcars, as.integer)
is.data.frame(mtcars)
mtcars <- lapply(mtcars, as.integer)
is.data.frame(mtcars)
```
```{r, dependson = "mtcars", include = FALSE}
rm(mtcars)
```
## Aplicaciones {#sec-applications}
Los principios descritos anteriormente tienen una amplia variedad de aplicaciones útiles. A continuación se describen algunos de los más importantes. Si bien muchos de los principios básicos de creación de subconjuntos ya se han incorporado en funciones como `subset()`, `merge()` y `dplyr::arrange()`, será valioso comprender mejor cómo se han implementado esos principios. cuando se encuentra con situaciones en las que las funciones que necesita no existen.
### Tablas de búsqueda (subconjunto de caracteres) {#sec-lookup-tables}
\index{lookup tables}
La coincidencia de caracteres es una forma poderosa de crear tablas de búsqueda. Digamos que quieres convertir abreviaturas:
```{r}
x <- c("m", "f", "u", "f", "f", "m", "m")
lookup <- c(m = "Male", f = "Female", u = NA)
lookup[x]
```
Tenga en cuenta que si no quiere nombres en el resultado, use `unname()` para eliminarlos.
```{r}
unname(lookup[x])
```
### Coincidencia y fusión a mano (subconjunto de enteros) {#sec-matching-merging}
\index{matching and merging} \index{match()}
También puede tener tablas de búsqueda más complicadas con múltiples columnas de información. Por ejemplo, supongamos que tenemos un vector de grados enteros y una tabla que describe sus propiedades:
```{r}
grades <- c(1, 2, 2, 3, 1)
info <- data.frame(
grade = 3:1,
desc = c("Excellent", "Good", "Poor"),
fail = c(F, F, T)
)
```
Entonces, digamos que queremos duplicar la tabla `info` para que tengamos una fila para cada valor en `grade`. Una forma elegante de hacer esto es combinando `match()` y un subconjunto de enteros (`match(aguja, pajar)` devuelve la posición donde se encuentra cada `aguja` en el `pajar`).
```{r}
id <- match(grades, info$grade)
id
info[id, ]
```
Si está haciendo coincidir varias columnas, primero deberá colapsarlas en una sola columna (con, por ejemplo, `interaction ()`). Sin embargo, normalmente es mejor cambiar a una función diseñada específicamente para unir varias tablas como `merge()` o `dplyr::left_join()`.
### Muestras aleatorias y bootstraps (subconjunto de enteros)
\index{sampling} \index{bootstrapping}
Puede usar índices enteros para muestrear o arrancar aleatoriamente un vector o un data frame. Simplemente use `sample(n)` para generar una permutación aleatoria de `1:n`, y luego use los resultados para dividir los valores:
```{r}
df <- data.frame(x = c(1, 2, 3, 1, 2), y = 5:1, z = letters[1:5])
# Reordenar aleatoriamente
df[sample(nrow(df)), ]
# Seleccionar aleatoriamente 3 filas
df[sample(nrow(df), 3), ]
# Seleccione 6 réplicas de arranque
df[sample(nrow(df), 6, replace = TRUE), ]
```
Los argumentos de `sample()` controlan el número de muestras a extraer, y también si el muestreo se realiza con o sin reemplazo.
### Ordenación (subconjunto de enteros)
\index{order()}
\index{sorting}
`order()` toma un vector como entrada y devuelve un vector entero que describe cómo ordenar el vector dividido en subconjuntos[^subsetting-2]:
[^subsetting-2]: Estos son índices de "extracción", es decir, `order(x)[i]` es un índice de dónde se encuentra cada `x[i]`. No es un índice de dónde debe enviarse `x[i]`.
```{r}
x <- c("b", "c", "a")
order(x)
x[order(x)]
```
Para desempatar, puede proporcionar variables adicionales a `order()`. También puede cambiar el orden de ascendente a descendente utilizando `decreasing = TRUE`. De forma predeterminada, cualquier valor que falte se colocará al final del vector; sin embargo, puede eliminarlos con `na.last = NA` o ponerlos al frente con `na.last = FALSE`.
Para dos o más dimensiones, `order()` y el subconjunto de enteros facilita el orden de las filas o las columnas de un objeto:
```{r}
# Reordenar al azar df
df2 <- df[sample(nrow(df)), 3:1]
df2
df2[order(df2$x), ]
df2[, order(names(df2))]
```
Puede ordenar los vectores directamente con `sort()`, o de manera similar `dplyr::arrange()`, para ordenar un data frame.
### Expansión de recuentos agregados (subconjunto de enteros)
A veces obtiene un data frame donde filas idénticas se han colapsado en una y se ha agregado una columna de conteo. `rep()` y el subconjunto de enteros hacen que sea fácil de descomprimir, porque podemos aprovechar la vectorización de `rep()`: `rep(x, y)` repite `x[i]` `y[i]` veces .
```{r}
df <- data.frame(x = c(2, 4, 1), y = c(9, 11, 6), n = c(3, 5, 1))
rep(1:nrow(df), df$n)
df[rep(1:nrow(df), df$n), ]
```
### Eliminar columnas de data frames (caracteres \mbox{subsetting})
Hay dos formas de eliminar columnas de un data frame. Puede establecer columnas individuales para `NULL`:
```{r}
df <- data.frame(x = 1:3, y = 3:1, z = letters[1:3])
df$z <- NULL
```
O puede extraer un subconjunto para devolver solo las columnas que desea:
```{r}
df <- data.frame(x = 1:3, y = 3:1, z = letters[1:3])
df[c("x", "y")]
```
Si solo conoce las columnas que no desea, use las operaciones de configuración para determinar qué columnas conservar:
```{r}
df[setdiff(names(df), "z")]
```
### Selección de filas en función de una condición (subconjunto lógico)
\index{subsetting!with logical vectors} \index{subset()}
Debido a que el subconjunto lógico le permite combinar fácilmente condiciones de varias columnas, es probablemente la técnica más utilizada para extraer filas de un data frame.
```{r}
mtcars[mtcars$gear == 5, ]
mtcars[mtcars$gear == 5 & mtcars$cyl == 4, ]
```
Recuerde utilizar los operadores booleanos vectoriales `&` y `|`, no los operadores escalares de cortocircuito `&&` y `||`, que son más útiles dentro de las sentencias if. Y no olvide [las leyes de De Morgan](http://en.wikipedia.org/wiki/De_Morgan's_laws), que pueden ser útiles para simplificar las negaciones:
- `!(X & Y)` es lo mismo que `!X | !Y`
- `!(X | Y)` es lo mismo que `!X & !Y`
Por ejemplo, `!(X & !(Y | Z))` se simplifica en `!X | !!(Y|Z)`, y luego a `!X | Y | Z`.
### Álgebra booleana versus conjuntos (lógicos y enteros \mbox{subsetting})
\index{Boolean algebra} \index{set algebra} \index{which()}
Es útil ser consciente de la equivalencia natural entre las operaciones con conjuntos (subconjuntos enteros) y el álgebra booleana (subconjuntos lógicos). El uso de operaciones de conjuntos es más efectivo cuando:
- Quieres encontrar el primero (o el último) `TRUE`.
- Tienes muy pocos `TRUE` y muchos `FALSE`; una representación establecida puede ser más rápida y requerir menos almacenamiento.
`which()` le permite convertir una representación booleana en una representación entera. No hay una operación inversa en la base R, pero podemos crear una fácilmente:
```{r}
x <- sample(10) < 4
which(x)
unwhich <- function(x, n) {
out <- rep_len(FALSE, n)
out[x] <- TRUE
out
}
unwhich(which(x), 10)
```
Creemos dos vectores lógicos y sus equivalentes enteros, y luego exploremos la relación entre las operaciones booleanas y de conjuntos.
```{r}
(x1 <- 1:10 %% 2 == 0)
(x2 <- which(x1))
(y1 <- 1:10 %% 5 == 0)
(y2 <- which(y1))
# X & Y <-> intersect(x, y)
x1 & y1
intersect(x2, y2)
# X | Y <-> union(x, y)
x1 | y1
union(x2, y2)
# X & !Y <-> setdiff(x, y)
x1 & !y1
setdiff(x2, y2)
# xor(X, Y) <-> setdiff(union(x, y), intersect(x, y))
xor(x1, y1)
setdiff(union(x2, y2), intersect(x2, y2))
```
Al aprender subconjuntos por primera vez, un error común es usar `x[which(y)]` en lugar de `x[y]`. Aquí, `which()` no logra nada: cambia de subconjunto lógico a entero, pero el resultado es exactamente el mismo. En casos más generales, hay dos diferencias importantes.
- Cuando el vector lógico contiene `NA`, el subconjunto lógico reemplaza estos valores con `NA` mientras que `which()` simplemente elimina estos valores. No es raro usar `which()` para este efecto secundario, pero no lo recomiendo: nada sobre el nombre "cuál" implica la eliminación de valores faltantes.
- `x[-which(y)]` **no** es equivalente a `x[!y]`: si `y` es todo FALSO, `which(y)` será `integer(0)` y `-integer(0)` sigue siendo `integer(0)`, por lo que no obtendrá valores, en lugar de todos los valores.
En general, evite cambiar de subconjunto lógico a entero a menos que desee, por ejemplo, el primer o último valor `TRUE`.
### Ejercicios
1. ¿Cómo permutarías aleatoriamente las columnas de un data frame? (Esta es una técnica importante en los bosques aleatorios). ¿Puede permutar simultáneamente las filas y las columnas en un solo paso?
2. ¿Cómo seleccionaría una muestra aleatoria de `m` filas de un data frame? ¿Qué pasaría si la muestra tuviera que ser contigua (es decir, con una fila inicial, una fila final y todas las filas intermedias)?
3. ¿Cómo podría poner las columnas en un data frame en orden alfabético?
## Respuestas de la {#sec-subsetting-answers}
1. Los enteros positivos seleccionan elementos en posiciones específicas, los enteros negativos descartan elementos; los vectores lógicos mantienen los elementos en las posiciones correspondientes a `TRUE`; los vectores de caracteres seleccionan elementos con nombres coincidentes.
2. `[` selecciona sub-listas: siempre devuelve una lista. Si lo usa con un solo entero positivo, devuelve una lista de longitud uno. `[[` selecciona un elemento dentro de una lista. `$` es una abreviatura conveniente: `x$y` es equivalente a `x[["y"]]`.
3. Use `drop = FALSE` si está creando subconjuntos de una matriz, un arreglo o un data frame y desea conservar las dimensiones originales. Casi siempre deberías usarlo cuando hagas subconjuntos dentro de una función.
4. Si `x` es una matriz, `x[] <- 0` reemplazará cada elemento con 0, manteniendo el mismo número de filas y columnas. Por el contrario, `x <- 0` reemplaza completamente la matriz con el valor 0.
5. Un vector de caracteres con nombre puede actuar como una tabla de búsqueda simple: `c(x = 1, y = 2, z = 3)[c("y", "z", "x")]`