forked from hadley/r4ds
-
Notifications
You must be signed in to change notification settings - Fork 130
/
15-factors.qmd
328 lines (230 loc) · 13.4 KB
/
15-factors.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
# Factores
## Introducción
En R, los factores se usan para trabajar con variables categóricas, es decir, variables que tienen un conjunto fijo y conocido de valores posibles. También son útiles cuando quieres mostrar vectores de caracteres en un orden no alfabético.
Históricamente, los factores eran más sencillos de trabajar que los caracteres. Como resultado, muchas de las funciones de R base automáticamente convierten los caracteres a factores. Esto significa que, a menudo, los factores aparecen en lugares donde no son realmente útiles. Afortunadamente, no tienes que preocuparte de eso en el tidyverse y puedes concentrarte en situaciones en las que los factores son genuinamente útiles.
### Prerrequisitos
Para trabajar con factores, vamos a usar el paquete __forcats__, que es parte del __tidyverse__. Este paquete provee herramientas para lidiar con variables **cat**egóricas (¡y es un anagrama de _factores_ en inglés!) y ofrece un amplio rango de ayudas para trabajar con factores.
```{r setup, message = FALSE}
library(tidyverse)
library(datos)
```
### Aprendiendo más
Si quieres aprender más sobre factores, te recomendamos leer el artículo de Amelia McNamara y Nicholas Horton, [_Wrangling categorical data in R_](https://peerj.com/preprints/3163/) (el nombre significa _Domando/Manejando Datos Categóricos en R_). Este artículo cuenta parte de la historia discutida en [_stringsAsFactors: An unauthorized biography_](http://simplystatistics.org/2015/07/24/stringsasfactors-an-unauthorized-biography/) (del inglés _cadenasComoFactores: Una Biografía No Autorizada_) y [_stringsAsFactors = \<sigh\>_](http://notstatschat.tumblr.com/post/124987394001/stringsasfactors-sigh) (del inglés _cadenasComoFactores = \<suspiro\>_), y compara las propuestas _tidy_ para los datos categóricos demostrados en este libro, en comparación a los métodos de R base. Una versión temprana de este artículo ayudó a motivar y definir el alcance del paquete __forcats__. ¡Gracias Amelia y Nick!
## Creando factores
Imagina que tienes una variable que registra meses:
```{r}
x1 <- c("Dic", "Abr", "Ene", "Mar")
```
Usar una cadena de caracteres (o _string_, en inglés) para guardar esta variable tiene dos problemas:
1. Solo hay doce meses posibles y no hay nada que te resguarde de errores de tipeo:
```{r}
x2 <- c("Dic", "Abr", "Eme", "Mar")
```
2. No se ordena de una forma útil:
```{r}
sort(x1)
```
Puedes solucionar ambos problemas con un factor. Para crearlo, debes empezar definiendo una lista con los __niveles__ válidos:
```{r}
niveles_meses <- c(
"Ene", "Feb", "Mar", "Abr", "May", "Jun",
"Jul", "Ago", "Sep", "Oct", "Nov", "Dic"
)
```
Ahora puedes crear un factor:
```{r}
y1 <- factor(x1, levels = niveles_meses)
y1
sort(y1)
```
Cualquier valor no fijado en el conjunto será convertido a `NA` de forma silenciosa:
```{r}
y2 <- factor(x2, levels = niveles_meses)
y2
```
Si quieres una advertencia, puedes usar `readr::parse_factor()` (_segmentar un factor_, en inglés):
```{r}
y2 <- parse_factor(x2, levels = niveles_meses)
```
Si omites los niveles, se van a definir a partir de los datos en orden alfabético:
```{r}
factor(x1)
```
A veces es preferible que el orden de los niveles se corresponda con su primera aparición en los datos. Puedes hacer esto cuando creas el factor, al definir los niveles con `unique(x)` _único_) o después con `fct_inorder()` (_factores en orden_):
```{r}
f1 <- factor(x1, levels = unique(x1))
f1
f2 <- x1 %>% factor() %>% fct_inorder()
f2
```
Si alguna vez necesitas acceso directo al conjunto de niveles válidos, puedes hacerlo con `levels()` (_niveles_):
```{r}
levels(f2)
```
## Encuesta Social General
Por el resto del capítulo, nos vamos a concentrar en `datos::encuesta`. Esta es la versión traducida al español de un conjunto de datos de ejemplo de la [General Social Survey](http://gss.norc.org), una encuesta realizada en Estados Unidos desde hace mucho tiempo, conducida por la organización de investigación independiente llamada NORC, en la Universidad de Chicago. La encuesta tiene miles de preguntas, así que en el conjunto de datos hemos seleccionado aquellas que ilustran algunos de los desafíos comunes que encontrarás al trabajar con factores.
```{r}
encuesta
```
(Recuerda que como este conjunto de datos está provisto por un paquete, puedes obtener más información de las variables con `?encuesta`.)
Cuando los factores están almacenados en un _tibble_ no puedes ver sus niveles tan fácilmente. Una forma de verlos es con `count()` (_contar_):
```{r}
encuesta %>%
count(raza)
```
O con un gráfico de barras:
```{r}
ggplot(encuesta, aes(raza)) +
geom_bar()
```
Por defecto, __ggplot2__ retira los niveles que no tienen valores. Puedes forzarlos para que se visualicen con:
```{r}
ggplot(encuesta, aes(raza)) +
geom_bar() +
scale_x_discrete(drop = FALSE)
```
Estos niveles representan valores válidos que simplemente no tuvieron ocurrencias en este dataset. Para mostrarlos, en `dplyr::count()` agrega el argumento `.drop` con la opción `FALSE`.
```{r}
encuesta %>%
count(raza,
.drop = FALSE)
```
Cuando se trabaja con factores, las dos operaciones más comunes son cambiar el orden de los niveles y cambiar sus valores. Estas operaciones se describen en las siguientes secciones.
### Ejercicios
1. Explora la distribución de `ingreso`. ¿Qué hace que el gráfico de barras por defecto sea tan difícil de comprender? ¿Cómo podrías mejorarlo?
1. ¿Cuál es la `religion` más común en esta encuesta? ¿Cuál es el `partido` más común?
1. ¿A qué `religion` se aplica cada `denominacion`? ¿Cómo puedes descubrirlo con una tabla? ¿Cómo lo puedes descubrir con una visualización?
## Modificar el orden de los factores
A menudo resulta útil cambiar el orden de los niveles de factores en una visualización. Por ejemplo, imagina que quieres explorar el número promedio de horas consumidas mirando televisión por día, para cada religión:
```{r}
resumen_religion <- encuesta %>%
group_by(religion) %>%
summarise(
edad = mean(edad, na.rm = TRUE),
horas_tv = mean(horas_tv, na.rm = TRUE),
n = n()
)
ggplot(resumen_religion, aes(horas_tv, religion)) + geom_point()
```
Este gráfico resulta dificil de interpretar porque no hay un patrón general. Podemos mejorarlo al ordenar los niveles de `religion` usando `fct_reorder()` ( _reordenar factores_). `fct_reorder()` requiere tres argumentos:
* `f`, el factor cuyos niveles quieres modificar.
* `x`, un vector numérico que quieres usar para reordenar los niveles.
* Opcionalmente, `fun`, una función que se usa si hay múltiples valores de `x` para cada valor de `f`. El valor por defecto es `median` (_mediana_).
```{r}
ggplot(resumen_religion, aes(horas_tv, fct_reorder(religion, horas_tv))) +
geom_point()
```
Reordenar la columna religión (`religion`) hace que sea más sencillo ver que las personas en la categoría "No sabe" ven más televisión, mientras que "Hinduismo" y "Otra religión oriental" ven mucho menos.
Cuando haces transformaciones más complicadas, recomendamos que las remuevas de `aes()` hacia un paso de transformación separado usando `mutate()`. Por ejemplo, puedes reescribir el gráfico anterior de la siguiente forma:
```{r, eval = FALSE}
resumen_religion %>%
mutate(religion = fct_reorder(religion, horas_tv)) %>%
ggplot(aes(horas_tv, religion)) +
geom_point()
```
¿Qué sucede si creamos un gráfico para observar cómo varía la edad promedio para cada ingreso reportado?
```{r}
resumen_ingreso <- encuesta %>%
group_by(ingreso) %>%
summarise(
edad = mean(edad, na.rm = TRUE),
horas_tv = mean(horas_tv, na.rm = TRUE),
n = n()
)
ggplot(resumen_ingreso, aes(edad, fct_reorder(ingreso, edad))) + geom_point()
```
En ete caso, ¡reordenar los niveles arbitrariamente no es una buena idea! Eso es porque `ingreso` ya tiene un orden basado en un principio determinado, con el cual no deberíamos meternos. Reserva `fct_reorder()` para factores cuyos niveles están ordenados arbitrariamente.
Sin embargo, sí tiene sentido mover "No Aplica" al frente, junto a los otros niveles especiales. Puedes usar `fct_relevel()` (_cambiar niveles_). Esta función recibe como argumento un factor, `f` y luego cualquier número de niveles que quieras mover al principio de la línea.
```{r}
ggplot(resumen_ingreso, aes(edad, fct_relevel(ingreso, "No aplica"))) +
geom_point()
```
¿Por qué crees que la edad promedio para "No aplica" es tan alta?
Es otro el tipo de reordenamiento que resulta útil cuando estás coloreando las líneas de un gráfico. `fct_reorder2()` reordena el factor mediante los valores `y` asociados con los valores `x` más grandes. Esto hace que el gráfico sea más sencillo de leer, porque los colores de líneas se ajustan con la leyenda.
```{r, fig.align = "default", out.width = "50%", fig.width = 4}
por_edad <- encuesta %>%
filter(!is.na(edad)) %>%
count(edad, estado_civil) %>%
group_by(edad) %>%
mutate(prop = n / sum(n))
ggplot(por_edad, aes(edad, prop, colour = estado_civil)) +
geom_line(na.rm = TRUE)
ggplot(por_edad, aes(edad, prop, colour = fct_reorder2(estado_civil, edad, prop))) +
geom_line() +
labs(colour = "estado_civil")
```
Finalmente, para los gráficos de barra puedes usar `fct_infreq()` (_frecuencia incremental de factores_) para ordenar los niveles incrementalmente según su frecuencia: este es el ordenamiento más sencillo porque no requiere de variables adicionales. Puedes querer combinarlo con `fct_rev()` (_invertir factores_).
```{r}
encuesta %>%
mutate(estado_civil = estado_civil %>% fct_infreq() %>% fct_rev()) %>%
ggplot(aes(estado_civil)) +
geom_bar()
```
### Ejercicios
1. Hay algunos números sospechosamente grandes en `horas_tv`. ¿Es la media un buen resumen?
1. Identifica para cada factor en `encuesta` si el orden de los niveles es arbitrario o responde a algún principio.
1. ¿Por qué mover "No aplica" al inicio de los niveles lo llevó al final del gráfico?
## Modificar los niveles de los factores
Más poderoso que cambiar el orden de los niveles es cambiar sus valores. Esto te permite clarificar etiquetas para publicación y colapsar niveles para visualizaciones de alto nivel. La herramienta más general y más poderosa es `fct_recode()` (_recodificar factores_). Esta función te permite recodificar o cambiar el valor de cada nivel. Por ejemplo, toma la columna `encuesta$partido`:
```{r}
encuesta %>% count(partido)
```
Los niveles son concisos e inconsistentes. Modifiquémoslos un poco para que sean más largos y para poder usar una construcción paralela.
```{r}
encuesta %>%
mutate(partido = fct_recode(partido,
"Republicano duro" = "Fuertemente republicano",
"Republicano moderado" = "No fuertemente republicano",
"Independiente pro republicano" = "Ind, pro rep",
"Independiente pro demócrata" = "Ind, pro dem",
"Demócrata moderado" = "No fuertemente demócrata",
"Demócrata duro" = "Fuertemente demócrata"
)) %>%
count(partido)
```
`fct_recode()` no modificará los niveles que no han sido mencionados explícitamente y te advertirá si accidentalmente te refieres a un nivel que no existe.
Para combinar grupos, puedes asignar múltiples niveles viejos al mismo nivel nuevo:
```{r}
encuesta %>%
mutate(partido = fct_recode(partido,
"Republicano duro" = "Fuertemente republicano",
"Republicano moderado" = "No fuertemente republicano",
"Independiente pro republicano" = "Ind, pro rep",
"Independiente pro demócrata" = "Ind, pro dem",
"Demócrata moderado" = "No fuertemente demócrata",
"Demócrata duro" = "Fuertemente demócrata",
"Otro" = "Sin respuesta",
"Otro" = "No sabe",
"Otro" = "Otro partido"
)) %>%
count(partido)
```
Debes usar esta técnica con cuidado: si agrupas categorías que son realmente diferentes, obtendrás resultados confusos y/o engañosos.
Si quieres colapsar muchos niveles, `fct_collapse()` (_colapsar factores_) es una variante muy útil de `fct_recode()`. Para cada nueva variable puedes proveer un vector de niveles viejos:
```{r}
encuesta %>%
mutate(partido = fct_collapse(partido,
otro = c("Sin respuesta", "No sabe", "Otro partido"),
republicano = c("Fuertemente republicano", "No fuertemente republicano"),
independiente = c("Ind, pro rep", "Independiente", "Ind, pro dem"),
demócrata = c("No fuertemente demócrata", "Fuertemente demócrata")
)) %>%
count(partido)
```
A veces, simplemente quieres agrupar todos los grupos pequeños para simplificar un gráfico o tabla. Ese es un trabajo para `fct_lump()` (_agrupar factores_):
```{r}
encuesta %>%
mutate(religion = fct_lump(religion, other_level = "Otra")) %>%
count(religion)
```
El comportamiento por defecto es agrupar los grupos pequeños de forma progresiva, asegurando que la agregación continúa siendo el grupo más pequeño. En este caso, esto no resulta demasiado útil: es cierto que la mayoría de los estadounidenses en esta encuesta son protestantes, pero probablemente hemos colapsado en exceso.
En cambio, podemos usar el parámetro `n` para especificar cuántos grupos (excluyendo _otros_) queremos colapsar:
```{r}
encuesta %>%
mutate(religion = fct_lump(religion, n = 10, other_level = "Otra")) %>%
count(religion, sort = TRUE) %>%
print(n = Inf)
```
### Ejercicios
1. ¿Cómo han cambiado en el tiempo las proporciones de personas que se identifican como demócratas, republicanas e independientes?
1. ¿Cómo podrías colapsar `ingreso` en un grupo más pequeño de categorías?