Capítulo 23 Extrayendo información: estadística descriptiva con cualitativas

Scripts usados:

Como ya hemos comentado, algunas de las grandes fortalezas de R frente a Python están relacionadas con la disponibilidad de herramientas para generar informes y herramientas de visualización y procesamiento de datos. Pero si R es tan conocido en ciencias es por ser un lenguaje de programación sencillo para aplicar estadística y Machine Learning, con una inmensa cantidad de paquetes relacionados con ello. En este bloque vamos a introducirnos a la estadística descriptiva en varios niveles:

  • Análisis univariante de variables categóricas: una sola variable que representa una categoría o cualidad.
  • Análisis univariante de variables continuas: una sola variable que representa una variable cuantificable numéricamente.
  • Análisis multivariante de distintos tipos de variables: varias variables a la vez

23.1 ¿Qué es la estadística descriptiva?

Una buena definición de estadística puede ser la encontrada en wikipedia:

La estadística (la forma femenina del término alemán Statistik, derivado a su vez del italiano statista, “hombre de Estado”), es la rama de la matemática que estudia la variabilidad, colección, organización, análisis, interpretación, y presentación de los datos, así como el proceso aleatorio que los genera siguiendo las leyes de la probabilidad.

La estadística descriptiva es la herramienta fundamental para obtener esa información, para obtener valor de los datos generados a partir de los sucesos de estudio mediante la descripción, visualización y agregación de los mismos.

23.1.1 Población y muestra

El uso primigenio de la estadística, y de donde deriva etimológicamente la palabra, es el estudio y organización del Estado, por lo que muchos términos hacen referencia a dicho origen. Vamos a introducir algunos términos usando como ejemplo el conjunto que ya conocemos de starwars, en el que tenemos datos de 87 personajes.

  • Población: el conjunto o colección de individuos a estudiar (por ejemplo, los cientos de miles de habitantes de los mundos de Star Wars). En general llamaremos población al conjunto de posibles elementos o eventos de los que podríamos tener observaciones. El problema es que esa población normalmente es inaccesible su totalidad (en el caso de obtener estadísticas de la estatura de toda la población española, sería inviable medir a los 47 millones de habitantes).

  • Individuo: cada uno de los elementos sobre los que vamos a medir una característica (normalmente las filas de una tabla)

  • Muestra: una selección «representativa» de la población total (teórica). En nuestro caso, de los miles de habitantes de los mundos de Star Wars hemos extraído una muestra de 87 personas. La rama de la estadística que se encarga del estudio de cómo obtener esas muestras, en función del objetivo que se tenga, se conoce como muestreo.

library(tidyverse)

# Nuestra muestra
starwars
## # A tibble: 87 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>
  • Tamaño muestral: comunmente denotado como \(n\), es el número de individuos seleccionados de nuestra población (normalmente el número de filas de nuestros conjuntos de datos).
# Tamaño muestral
nrow(starwars)
## [1] 87

23.1.2 Incursión en lo aleatorio

Un experimento aleatorio es aquel cuyo resultado inmediato, para cada individuo, no es posible saber con certeza, aunque eso no impida definir y conocer su patrón a nivel poblacional (teórico). No sabemos que saldrá en el dado en cada tirada pero sí la probabilidad de cada dígito, pudiendo incluso inferir resultados de nuestra población en función de nuestras muestras (tiradas): si de 1 millón de veces que tiramos el dado, en el 90% de ellas sale un 1, usando el conocimiento probabilístico de que la probabilidad de cada número debería ser de \(1/6\), podríamos inferir que nuestro dado podría estar trucado.

La forma más sencilla de generar una muestra aleatoria en R es con la función sample(), indicándole los siguientes argumentos:

  • x: los valores posibles (lo que se conoce como dominio). Por ejemplo, en el caso de una moneda, serán "cara" y "cruz".

  • size: el número de veces que realizamos la extracción aleatoria (el número de tiradas).

  • replace: en caso de estar TRUE, permitirá que puedan salir valores repetidos (por ejemplo, cara, cara). En caso de ser FALSE, es como una urna de bolas de forma que tras cada extracción la bola nunca vuelve a la urna: solo se podrá realizar el experimento un máximo de veces igual al número de elementos en x

valores_moneda <- c("cara", "cruz")
sample(x = valores_moneda, size = 30, replace = TRUE)
##  [1] "cruz" "cruz" "cruz" "cruz" "cara" "cara" "cruz" "cruz" "cruz" "cara"
## [11] "cruz" "cara" "cara" "cruz" "cara" "cara" "cara" "cruz" "cruz" "cara"
## [21] "cruz" "cara" "cruz" "cara" "cruz" "cara" "cruz" "cara" "cara" "cruz"

Si lo ejecutamos de nuevo, al ser aleatorio, saldrán otras 30 tiradas distintas.

sample(x = valores_moneda, size = 30, replace = TRUE)
##  [1] "cara" "cara" "cruz" "cara" "cara" "cruz" "cruz" "cara" "cara" "cruz"
## [11] "cara" "cruz" "cruz" "cruz" "cruz" "cruz" "cara" "cruz" "cruz" "cruz"
## [21] "cara" "cara" "cara" "cruz" "cara" "cara" "cara" "cara" "cruz" "cara"

En el caso de la moneda es un experimento en el que los posibles sucesos son equiprobables: ambos tienen la mismas opciones de salir. Si tuviésemos el equivalente a una moneda trucada, donde por ejemplo "cara" tuviese un 0.7 de probabilidad y "cruz" un 0.3 (la suma debe ser siempre igual 1), obtendríamos tiradas muy diferentes. Para indicarle que las probabilidades asociadas al experimento no son iguales, usaremos el argumento probs, pasándole un vector con dichas probabilidades

tirada_equi <- sample(x = valores_moneda, size = 500, replace = TRUE)
tirada_trucada <- sample(x = valores_moneda, size = 500, prob = c(0.7, 0.3), replace = TRUE)

# Número de caras (de 500 tiradas)
sum(tirada_equi == "cara")
## [1] 259
sum(tirada_trucada == "cara")
## [1] 352

23.1.3 Características y modalidades

En estadística llamamos caracteres a cada una de las variables o columnas de una tabla, cada una de las características o cualidades que se miden/estudian para cada uno de los individuos seleccionados en la muestra. En el caso de starwars, podemos extraer su nomnbre con names()

# Número de características medidas
ncol(starwars)
## [1] 14
# Características
names(starwars)
##  [1] "name"       "height"     "mass"       "hair_color" "skin_color"
##  [6] "eye_color"  "birth_year" "sex"        "gender"     "homeworld" 
## [11] "species"    "films"      "vehicles"   "starships"

Y para cada una de ellas llamaremos modalidades a los diferentes valores que puede adoptar una característica o variable. En el caso por ejemplo del fichero de starwars tenemos distintas opciones:

  • sex: tiene 4 modalidades female, hermaphroditic, male, none (podemos extraerlas con unique(), que nos dará los valores únicos de una variable).
unique(starwars$sex)
## [1] "male"           "none"           "female"         "hermaphroditic"
## [5] NA
  • mass: su modalidad son todos los números reales positivos hasta un peso máximo (es lo que se conoce como una variable continua).

En estadística, como en probabilidad, podemos distinguir las variables en función de las modalidades permitidas en dos grandes categorías:

  • Variables categóricas (nominales o cualitativas). Son variables que representan categorías o cualidades. Ejemplos: color, forma, estado civil, religión, etc. Estas variables las podemos subdividir en función de si admiten o no un orden:
    • Cualitativas ordinales: aunque representen cualidades, tienen una jerarquía de orden. Ejemplos: suspenso-aprobado-notable, sano - herido leve - grave, etc.
    • Cualitativas nominales: no admiten (salvo problemas nuestros) una jerarquía de orden. Ejemplos: ateo-católico-musulmán, soltero-casado, homber-mujer, etc.
  • Variables cuantitativas: representan una cantidad numérica medible, una característica cuantificable matemáticamente. A su vez se pueden subdividir en dos grupos.
    • Cuantitativas discretas: se pueden contar y enumerar (aunque sean infinitos), detrás de un valor puedo saber cuál viene después (personas, granos de arena, etc).
    • Cuantitativas continuas: no solo toman infinitos valores sino que entre dos valores cualesquiera, también hay infinitos términos, no se puede determinar el siguiente valor a uno dado (estaturas, pesos, temperatura, etc).

23.2 Análisis univariante: cualitativas nominales

Como hemos comentado, vamos a empezar introduciendo algunas técnicas de análisis estadística para variables cualitativas o categóricas de manera univariante, variable a variable, analizando cada una de las columnas de un dataset de manera independiente del resto.

23.2.1 Factores

En el caso de las variables cualitativa, llamaremos niveles o modalidades a los diferentes valores que pueden tomar estos datos. Por ejemplo, en el caso de la variable sex del conjunto starwars, tenemos 4 niveles permitidos: female, hermaphroditic, male y none (amén de datos ausentes). Como ya hemos comentado en algunos apartados anteriores, este tipo de variables se conocen en R como factores en R. Y el paquete fundamental para tratarlos es forcats (del entorno tidyverse). Este paquete nos permite fijar los niveles (guardados internamente como levels) que toma una determinada variable categórica para que no puedan generarse equivocaciones, errores en la recolección y generación de datos. Además hace que su análisis sea menos costoso computacionalmente a la hora de hacer búsquedas y comparativas.

Veamos un ejempo sencillo definiendo una variable estado que tome los valores "sano", "leve" y "grave" de la siguiente manera.

estado <-
  c("grave", "leve", "sano", "sano", "sano", "grave",
    "grave", "leve", "grave", "sano", "sano")
estado
##  [1] "grave" "leve"  "sano"  "sano"  "sano"  "grave" "grave" "leve"  "grave"
## [10] "sano"  "sano"

La variable estado actualmente es de tipo texto, de tipo chr, algo que podemos comprobar con class(estado).

class(estado)
## [1] "character"

Desde un punto de vista estadístico y computacional, para R esta variable ahora mismo sería equivalente una variable de nombres. Pero estadísticamente no es lo mismo una variable con nombres (que identifican muchas veces el registro) que una variable categórica como estado que solo puede tomar esos 3 niveles.

¿Cómo convertir una variable a cualitativa o factor? Haciendo uso de la función as_factor del paquete forcats.

library(tidyverse)
estado_fct <- as_factor(estado)
estado_fct
##  [1] grave leve  sano  sano  sano  grave grave leve  grave sano  sano 
## Levels: grave leve sano
class(estado_fct)
## [1] "factor"

No solo ha cambiado la clase de la variable sino que ahora, debajo del valor guardado, nos aparece la frase Levels: grave leve sano: son las modalidades o niveles de nuestra cualitativa. Imagina que ese día en el hospital no tuviésemos a nadie en estado grave: aunque ese día nuestra variable no tome dicho valor, el estado grave es un nivel permitido que podríamos tener, así que aunque lo eliminemos, por ser un factor, el nivel permanece (no lo tenemos ahora pero es un nivel permitido).-

estado_fct[estado_fct %in% c("sano", "leve")]
## [1] leve sano sano sano leve sano sano
## Levels: grave leve sano

Si queremos indicarle que elimine un nivel no usado en ese momento podemos hacerlo con fct_drop()

fct_drop(estado_fct[estado_fct %in% c("sano", "leve")])
## [1] leve sano sano sano leve sano sano
## Levels: leve sano

Al igual que podemos eliminar niveles podemos ampliar los niveles existentes (aunque no existan datos de ese nivel en ese momento) con fct_expand()

fct_expand(estado_fct, c("UCI", "fallecido"))
##  [1] grave leve  sano  sano  sano  grave grave leve  grave sano  sano 
## Levels: grave leve sano UCI fallecido

Aunque luego veremos como usar el count(), para variables de tipo factor podemos contar los elementos de cada nivel de una manera sencilla con fct_count()

fct_count(estado_fct)
## # A tibble: 3 × 2
##   f         n
##   <fct> <int>
## 1 grave     4
## 2 leve      2
## 3 sano      5

Si te fijas el orden de los niveles es por orden de aparición en la variable, pero podemos ordenarlos por aparición con fct_infreq()

fct_infreq(estado_fct)
##  [1] grave leve  sano  sano  sano  grave grave leve  grave sano  sano 
## Levels: sano grave leve

A veces queremos agrupar niveles, por ejemplo, no permitiendo niveles que no sucedan un mínimo de veces. Con fct_lump_min(estado_fct, min = 4) le indicaremos que para que exista el nivel debe de suceder al menos 4 veces (las observaciones que no lo cumplan irán a un nivel genérico llamado Other, aunque se puede cambiar con el argumento other_level). Podemos hacer algo equivalente pero en función de su frecuencia relativa con fct_lump_prop().

fct_lump_min(estado_fct, min = 3)
##  [1] grave Other sano  sano  sano  grave grave Other grave sano  sano 
## Levels: grave sano Other
fct_lump_min(estado_fct, min = 5)
##  [1] Other Other sano  sano  sano  Other Other Other Other sano  sano 
## Levels: sano Other
fct_lump_min(estado_fct, min = 5, other_level = "otros")
##  [1] otros otros sano  sano  sano  otros otros otros otros sano  sano 
## Levels: sano otros

Como ya hemos usado (y veremos en detalle un poco más adelante), con count() también podemos los valores de una variable asociados a cada modalidad. Por ejemplo, vamos a calcular la cantidad de personajes de cada especie (filtrando los ausentes).

starwars %>%
  filter(!is.na(species)) %>%
  count(species)
## # A tibble: 37 × 2
##    species       n
##    <chr>     <int>
##  1 Aleena        1
##  2 Besalisk      1
##  3 Cerean        1
##  4 Chagrian      1
##  5 Clawdite      1
##  6 Droid         6
##  7 Dug           1
##  8 Ewok          1
##  9 Geonosian     1
## 10 Gungan        3
## # … with 27 more rows

Ahora que sabemos hacerlo podemos dibujar un diagrama de barras para visualizar el número de personajes de cada especie (con fill = n y scale_fill_continuous_tableau() asignaremos un gradiente de color en función de la frecuencia).

ggplot(starwars %>%
         filter(!is.na(species)) %>%
         count(species),
       aes(y = species, x = n, fill = n)) +
  geom_col() +
  scale_fill_continuous_tableau() +
  labs(fill = "Frecuencia absoluta",
       x = "Número de personajes", y = "Especies")

Al tener muchos niveles de species con muy pocos elementos, tenemos un gráfico poco claro, así que vamos a indicarle que nos convierta species a factor, y que nos agrupe aquellas niveles que tengan menos de 2 personajes.

ggplot(starwars %>%
         filter(!is.na(species)) %>%
         mutate(species =
                  fct_lump_min(species, min = 3,
                               other_level = "Otras especies")) %>%
         count(species),
       aes(y = species, x = n, fill = n)) +
  geom_col() +
  scale_fill_continuous_tableau() +
  labs(fill = "Frecuencia absoluta",
       x = "Número de personajes", y = "Especies")

Por último vamos a aplicar algunos ajustes del tema que ya hemos visto en apartados anteriores para terminar nuestro gráfico.

library(showtext)
font_add_google(family = "Roboto", name = "Roboto")
showtext_auto()

ggplot(starwars %>%
         filter(!is.na(species)) %>%
         mutate(species =
                  fct_lump_min(species, min = 3,
                               other_level = "Otras especies")) %>%
         count(species),
       aes(y = species, x = n, fill = n)) +
  geom_col() +
  scale_fill_continuous_tableau() +
  labs(fill = "Frecuencia absoluta",
       x = "Número de personajes", y = "Especies") +
  theme(panel.background = element_rect(fill = "white"),
        plot.background = element_rect(fill = "white", 
                                       color = "white"),
        panel.grid.major.y =
          element_line(size = 0.05, color = "black"),
        panel.grid.major.x =
          element_line(size = 0.1, color = "black"),
        text = element_text(size = 13),
        axis.title =
          element_text(family = "Roboto", size = 23),
        axis.text.x = element_text(family = "Roboto",
                                   size = 15),
        axis.text.y = element_text(family = "Roboto",
                                   size = 15))

23.2.2 Tablas de frecuencias

Una de las primeras cosas que uno aprende en la asignatura de estadística descriptiva es saber resumir nuestros datos cuando tenemos muchos, y además muchos valores repetidos. Vamos a construir la tabla de frecuencias de la variable n_films de nuestro ya conocido conjunto starwars (una variable que vamos a crear contando en cuántas películas aparece cada personaje con map_int() para aplicar la función length() a cada lista de cada personaje guardada en films)

library(purrr)
library(tidyverse)
starwars_nueva <- starwars %>%
  mutate(n_films = map_int(films, length))

Y lo primero es contar: ¿cuántos registros hay de cada una de las clases? Para ello usaremos la función count()

starwars_nueva %>% count()
## # A tibble: 1 × 1
##       n
##   <int>
## 1    87

Como ves si aplicamos la función directamente simplemente nos cuenta el número de filas de la tabla, así que vamos a usar la misma función pero indicándole la variable creada.

tabla_freq <- starwars_nueva %>% count(n_films)
tabla_freq
## # A tibble: 7 × 2
##   n_films     n
##     <int> <int>
## 1       1    46
## 2       2    18
## 3       3    13
## 4       4     2
## 5       5     5
## 6       6     2
## 7       7     1

Esa columna n es lo que conocemos como frecuencias absolutas, el número de veces que la variable toma dicha valor.

  • Frecuencias absolutas: contar cuántos elementos aparecen de un determinado nivel. Suele ser denotada como \(n_i\), para cada nivel \(i\).
  • Frecuencias relativas: contar que proporción del total representa dicha cantidad. Suele ser denotada como \(f_i\), para cada nivel \(i\).

Esa frecuencia absoluta también podemos calcularla haciendo uso de table(), aplicada a la variable extraída de la tabla.

table(starwars_nueva$n_films)
## 
##  1  2  3  4  5  6  7 
## 46 18 13  2  5  2  1
class(table(starwars_nueva$n_films))
## [1] "table"

El formato de la salida de table() es lo que se conoce como una tabla de contingencia. Aplicada a una sola variable es una tabla unidimensional con una fila compuesto por los niveles y una segunda con la frecuencia absoluta de cada uno de ellos. Si lo aplicamos a dos variables el resultado será una tabla de contigencia bidimensional: la primera variable en las filas, la segunda en las columnas, y realizará el conteo bidimensional \(n_ij\) para cada par de niveles \((i, j)\)

table(starwars_nueva %>% select(sex, n_films))
##                 n_films
## sex               1  2  3  4  5  6  7
##   female          8  4  3  0  1  0  0
##   hermaphroditic  0  0  1  0  0  0  0
##   male           32 12  9  2  4  1  0
##   none            3  1  0  0  0  1  1

Una vez que tenemos las frecuencias absolutas, las relativas se pueden calcular de forma sencilla con prop.table(), que nos calcula la proporción que representa dicho valor.

# Construimos de cero
tabla_freq <- starwars_nueva %>%
  # n: frecuencia absoluta
  count(n_films) %>%
  # f_i: frecuencia relativa
  mutate(f_i = prop.table(n))
tabla_freq
## # A tibble: 7 × 3
##   n_films     n    f_i
##     <int> <int>  <dbl>
## 1       1    46 0.529 
## 2       2    18 0.207 
## 3       3    13 0.149 
## 4       4     2 0.0230
## 5       5     5 0.0575
## 6       6     2 0.0230
## 7       7     1 0.0115

Es importanten observar que prop.table() debe aplicarse sobre una tabla de contingencia. No nos da error al aplicarlo sobre un vector númerico, pero lo que nos dará no son la frecuencias relativas del vector numérico si no de un vector que tuviera esos números como frecuencias absolutas.

prop.table(starwars_nueva$n_films)
##  [1] 0.028901734 0.034682081 0.040462428 0.023121387 0.028901734 0.017341040
##  [7] 0.017341040 0.005780347 0.005780347 0.034682081 0.017341040 0.011560694
## [13] 0.028901734 0.023121387 0.005780347 0.017341040 0.017341040 0.005780347
## [19] 0.028901734 0.028901734 0.017341040 0.005780347 0.005780347 0.011560694
## [25] 0.005780347 0.011560694 0.005780347 0.005780347 0.005780347 0.005780347
## [31] 0.005780347 0.017341040 0.005780347 0.011560694 0.005780347 0.005780347
## [37] 0.005780347 0.011560694 0.005780347 0.005780347 0.011560694 0.005780347
## [43] 0.005780347 0.017341040 0.005780347 0.005780347 0.005780347 0.017341040
## [49] 0.017341040 0.017341040 0.011560694 0.011560694 0.011560694 0.005780347
## [55] 0.017341040 0.011560694 0.005780347 0.005780347 0.005780347 0.011560694
## [61] 0.011560694 0.005780347 0.005780347 0.011560694 0.011560694 0.005780347
## [67] 0.005780347 0.005780347 0.005780347 0.005780347 0.005780347 0.005780347
## [73] 0.011560694 0.005780347 0.005780347 0.011560694 0.005780347 0.005780347
## [79] 0.011560694 0.011560694 0.005780347 0.005780347 0.005780347 0.005780347
## [85] 0.005780347 0.005780347 0.017341040

También es posible calcular frecuencias absolutas bidimensionales por varias variables a la vez dentro de {tidvyerse} con el propio count(), pasándole dos variables diferentes.

starwars_nueva %>% count(sex, gender)
## # A tibble: 6 × 3
##   sex            gender        n
##   <chr>          <chr>     <int>
## 1 female         feminine     16
## 2 hermaphroditic masculine     1
## 3 male           masculine    60
## 4 none           feminine      1
## 5 none           masculine     5
## 6 <NA>           <NA>          4

Podemos también añadir una columna \(N_i\) de frecuencia absoluta acumulada, una columna que nos diga el número de personajes que han hecho un número de películas igual o menor que el indicado (usando la función cumsum() que nos realiza esa suma acumulada).

# Construimos de cero
tabla_freq <-
  starwars_nueva %>%
  # n: frecuencia absoluta
  count(n_films) %>%
  mutate(f_i = prop.table(n),
         N_i = cumsum(n)) # cumsum calcula la suma acumulada
tabla_freq
## # A tibble: 7 × 4
##   n_films     n    f_i   N_i
##     <int> <int>  <dbl> <int>
## 1       1    46 0.529     46
## 2       2    18 0.207     64
## 3       3    13 0.149     77
## 4       4     2 0.0230    79
## 5       5     5 0.0575    84
## 6       6     2 0.0230    86
## 7       7     1 0.0115    87

Y al igual que sucedía con \(n_i\), la frecuencia absoluta acumulada \(N_i\) podremos también convertirla a frecuencia relativa acumulada.

# Construimos de cero
tabla_freq <- starwars_nueva %>%
  count(n_films) %>%
  mutate(N_i = cumsum(n)) %>% # cumsum calcula la suma acumulada
  mutate(f_i = prop.table(n), # prop.table nos devuelve proporciones
         F_i = cumsum(f_i))
tabla_freq
## # A tibble: 7 × 5
##   n_films     n   N_i    f_i   F_i
##     <int> <int> <int>  <dbl> <dbl>
## 1       1    46    46 0.529  0.529
## 2       2    18    64 0.207  0.736
## 3       3    13    77 0.149  0.885
## 4       4     2    79 0.0230 0.908
## 5       5     5    84 0.0575 0.966
## 6       6     2    86 0.0230 0.989
## 7       7     1    87 0.0115 1
# otra forma
tabla_freq <- starwars_nueva %>%
  count(n_films) %>%
  mutate(N_i = cumsum(n)) %>%
  mutate(f_i = prop.table(n),
         F_i = prop.table(N_i))
tabla_freq
## # A tibble: 7 × 5
##   n_films     n   N_i    f_i    F_i
##     <int> <int> <int>  <dbl>  <dbl>
## 1       1    46    46 0.529  0.0880
## 2       2    18    64 0.207  0.122 
## 3       3    13    77 0.149  0.147 
## 4       4     2    79 0.0230 0.151 
## 5       5     5    84 0.0575 0.161 
## 6       6     2    86 0.0230 0.164 
## 7       7     1    87 0.0115 0.166

Esas frecuencias relativas quizás querramos tenerlas expresadas en porcentajes en lugar de en proporciones, lo que podemos fácilmente multiplicando esas proporciones por 100.

# Pasamos frecuencias relativas a porcentajes
tabla_freq %>% mutate(f_i = f_i * 100,
                      F_i = F_i * 100)
## # A tibble: 7 × 5
##   n_films     n   N_i   f_i   F_i
##     <int> <int> <int> <dbl> <dbl>
## 1       1    46    46 52.9   8.80
## 2       2    18    64 20.7  12.2 
## 3       3    13    77 14.9  14.7 
## 4       4     2    79  2.30 15.1 
## 5       5     5    84  5.75 16.1 
## 6       6     2    86  2.30 16.4 
## 7       7     1    87  1.15 16.6

Por último, con la tabla tabla_freq expresando las frecuencias relativas en proporciones, vamos a cambiar el nombre de las columnas para que tengan los típicos nombres de una tabla de frecuencias.

# Renombramos
tabla_freq <- tabla_freq %>%
  rename(n_i = n, x_i = n_films)
tabla_freq
## # A tibble: 7 × 5
##     x_i   n_i   N_i    f_i    F_i
##   <int> <int> <int>  <dbl>  <dbl>
## 1     1    46    46 0.529  0.0880
## 2     2    18    64 0.207  0.122 
## 3     3    13    77 0.149  0.147 
## 4     4     2    79 0.0230 0.151 
## 5     5     5    84 0.0575 0.161 
## 6     6     2    86 0.0230 0.164 
## 7     7     1    87 0.0115 0.166

Con el paquete DT se pueden visualizar tablas en documentos .Rmd

 

¿Cuál es el color de pelo que más aparece en starwars?

Vamos a construir su tabla de frecuencias, primero conviertiendo hair_color a factor y viendo los niveles de dicha variable, y quedándonos solo con la variables name y hair_color. Además los niveles ausentes los vamos a convertir a "none", nivel que ya existe, con fct_explicit_na()

starwars_hair <-
  starwars %>%
  mutate(hair_color = fct_explicit_na(hair_color, na_level = "none")) %>%
  select(c(name, hair_color))
starwars_hair
## # A tibble: 87 × 2
##    name               hair_color   
##    <chr>              <fct>        
##  1 Luke Skywalker     blond        
##  2 C-3PO              none         
##  3 R2-D2              none         
##  4 Darth Vader        none         
##  5 Leia Organa        brown        
##  6 Owen Lars          brown, grey  
##  7 Beru Whitesun lars brown        
##  8 R5-D4              none         
##  9 Biggs Darklighter  black        
## 10 Obi-Wan Kenobi     auburn, white
## # … with 77 more rows

Tras el preprocesamiento construimos nuestra tabla de frecuencias.

tabla_freq <- starwars_hair %>%
  count(hair_color) %>%
  mutate(N_i = cumsum(n)) %>% # cumsum calcula la suma acumulada
  mutate(f_i = prop.table(n), # prop.table nos devuelve proporciones
         F_i = cumsum(f_i))
tabla_freq
## # A tibble: 12 × 5
##    hair_color        n   N_i    f_i    F_i
##    <fct>         <int> <int>  <dbl>  <dbl>
##  1 auburn            1     1 0.0115 0.0115
##  2 auburn, grey      1     2 0.0115 0.0230
##  3 auburn, white     1     3 0.0115 0.0345
##  4 black            13    16 0.149  0.184 
##  5 blond             3    19 0.0345 0.218 
##  6 blonde            1    20 0.0115 0.230 
##  7 brown            18    38 0.207  0.437 
##  8 brown, grey       1    39 0.0115 0.448 
##  9 grey              1    40 0.0115 0.460 
## 10 none             42    82 0.483  0.943 
## 11 unknown           1    83 0.0115 0.954 
## 12 white             4    87 0.0460 1
# En porcentaje
tabla_freq <- 
  tabla_freq %>% mutate(f_i = 100 * f_i,
                        F_i = 100 * F_i)
tabla_freq 
## # A tibble: 12 × 5
##    hair_color        n   N_i   f_i    F_i
##    <fct>         <int> <int> <dbl>  <dbl>
##  1 auburn            1     1  1.15   1.15
##  2 auburn, grey      1     2  1.15   2.30
##  3 auburn, white     1     3  1.15   3.45
##  4 black            13    16 14.9   18.4 
##  5 blond             3    19  3.45  21.8 
##  6 blonde            1    20  1.15  23.0 
##  7 brown            18    38 20.7   43.7 
##  8 brown, grey       1    39  1.15  44.8 
##  9 grey              1    40  1.15  46.0 
## 10 none             42    82 48.3   94.3 
## 11 unknown           1    83  1.15  95.4 
## 12 white             4    87  4.60 100

En este caso las frecuencias acumuladas no nos aportan ningún significado ya que la variable que estamos resumiendo es cualitativa nominal, no hay un orden entre los colores (cómo si lo había entre el número de películas, que era una variable cuantitativa discreta).

23.2.3 Análisis gráfico

El principal diagrama para variables cualitativas (o cuantitativas discretas) es el diagrama de barras que ya hemos aprendido a generar y personalizar.

library(showtext)
font_add_google(family = "Roboto", name = "Roboto")
showtext_auto()

ggplot(starwars %>%
         filter(!is.na(species)) %>%
         mutate(species =
                  fct_lump_min(species, min = 3,
                               other_level = "Otras especies")) %>%
         count(species),
       aes(y = species, x = n, fill = n)) +
  geom_col() +
  scale_fill_continuous_tableau() +
  labs(fill = "Frecuencia absoluta",
       x = "Número de personajes", y = "Especies") +
  theme(panel.background = element_rect(fill = "white"),
        plot.background = element_rect(fill = "white", 
                                       color = "white"),
        panel.grid.major.y =
          element_line(size = 0.05, color = "black"),
        panel.grid.major.x =
          element_line(size = 0.1, color = "black"),
        text = element_text(size = 13),
        axis.title =
          element_text(family = "Roboto", size = 23),
        axis.text.x = element_text(family = "Roboto",
                                   size = 15),
        axis.text.y = element_text(family = "Roboto",
                                   size = 15))

Otra opción con variables cualitativas, en especial para visualizar palabras en textos y documentos son precisamente las word cloud o nubes de palabras. Con el paquete wordcloud2 podrmeos visualizar las nubes de términos, por ejemplo de las especies de starwars (una vez filtrados los datos dusentes y reagrupado aquellas especies con solo un personaje). Con los parámetros size y color le indicaremos el tamaño base de las palabras y el patrón de colores elegir. Las palabras las visualizará en un tamaño relativo al número de veces que aparece.

# install.packages("wordcloud2)
library(wordcloud2)
wordcloud2(starwars %>% drop_na(species) %>%
             mutate(species =
                      fct_lump_min(species, min = 2,
                                   other_level = "otras")) %>% 
             count(species),
           size = 0.8, color= 'random-dark')

Otra opción habitual son los mosaicos o treemaps, representando en una cuadrícula cada una de las categorías, cuya área sea proprocional a las veces que aparece. Lo haremos con treemapify, dentro de la lógica ggplot2: los parámetros dentro de aes() serán area (asociado a la frecuencia absoluta), fill (color del relleno) y label (nombre). Usaremos una de las paletas de colores de cuadros vistas, en este caso MetBrewer::met.brewer("Renoir").

# install.packages("treemapify")
library(treemapify)
ggplot(starwars %>% drop_na(species) %>%
         mutate(species =
                      fct_lump_min(species, min = 2,
                                   other_level = "otras")) %>%
         count(species),
       aes(area = n, fill = species, label = species)) +
  geom_treemap() +
  scale_fill_manual(values = MetBrewer::met.brewer("Renoir")) +
  labs(fill = "Especies")

Con geom_treemap_text() podemos además escribir el nombre de los niveles, pudiendo eliminar la leyenda.

ggplot(starwars %>% drop_na(species) %>%
         mutate(species =
                      fct_lump_min(species, min = 2,
                                   other_level = "otras")) %>%
         count(species),
       aes(area = n, fill = species, label = species)) +
  geom_treemap() +
  geom_treemap_text(colour = "white", place = "centre",
                    size = 17) +
  scale_fill_manual(values = MetBrewer::met.brewer("Renoir")) +
  labs(fill = "Especies") +
  guides(fill = "none")

Por último vamos a probar el paquete ggparliament (puedes ver su documentación), para la representación de escaños (los partidos que se presentan a unas elecciones son variables cualitativas, y sus escaños sus frecuencias absolutas.

Para ilustrarlo vamos a usar el conjunto election_data de dicho paquete, que contiene los datos electorales de Rusia, Australia, Alemnia, UK y Estados Unidos

# install.packages("ggparliament")
library(ggparliament)
election_data
##     year   country           house                         party_long
## 1   2016       USA          Senate                         Republican
## 2   2014       USA          Senate                         Republican
## 3   2012       USA          Senate                         Democratic
## 4   2016       USA Representatives                         Republican
## 5   2014       USA Representatives                         Republican
## 6   2012       USA Representatives                         Republican
## 7   2017        UK         Commons                             Labour
## 8   2017        UK         Commons            Scottish National Party
## 9   2017        UK         Commons                  Liberal Democrats
## 10  2017        UK         Commons                Democratic Unionist
## 11  2017        UK         Commons                          Sinn Fein
## 12  2017        UK         Commons                        Plaid Cymru
## 13  2017        UK         Commons                              Green
## 14  2017        UK         Commons                        Independent
## 15  2015        UK         Commons                             Labour
## 16  2015        UK         Commons            Scottish National Party
## 17  2015        UK         Commons                  Liberal Democrats
## 18  2015        UK         Commons                Democratic Unionist
## 19  2015        UK         Commons                          Sinn Fein
## 20  2015        UK         Commons                        Plaid Cymru
## 21  2015        UK         Commons         Social Democratic & Labour
## 22  2015        UK         Commons                    Ulster Unionist
## 23  2015        UK         Commons              UK Independence Party
## 24  2015        UK         Commons                              Green
## 25  2015        UK         Commons                        Independent
## 26  2010        UK         Commons                             Labour
## 27  2010        UK         Commons                Democratic Unionist
## 28  2010        UK         Commons            Scottish National Party
## 29  2010        UK         Commons                          Sinn Fein
## 30  2010        UK         Commons                        Plaid Cymru
## 31  2010        UK         Commons         Social Democratic & Labour
## 32  2010        UK         Commons                              Green
## 33  2010        UK         Commons                           Alliance
## 34  2010        UK         Commons                        Independent
## 35  2016 Australia Representatives                             Labour
## 36  2016 Australia Representatives                             Greens
## 37  2016 Australia Representatives                      Xenophon Team
## 38  2016 Australia Representatives                        Independent
## 39  2016 Australia Representatives                Katter's Australian
## 40  2013 Australia Representatives                             Labour
## 41  2013 Australia Representatives                        Independent
## 42  2013 Australia Representatives                             Greens
## 43  2013 Australia Representatives                      Palmer United
## 44  2013 Australia Representatives                Katter's Australian
## 45  2010 Australia Representatives                            Liberal
## 46  2010 Australia Representatives                   Liberal National
## 47  2010 Australia Representatives                           National
## 48  2010 Australia Representatives                             Greens
## 49  2010 Australia Representatives                       Nationals WA
## 50  2010 Australia Representatives                    Country Liberal
## 51  2016 Australia          Senate                             Labour
## 52  2016 Australia          Senate                             Greens
## 53  2016 Australia          Senate                Hanson's One Nation
## 54  2016 Australia          Senate                      Xenophon Team
## 55  2016 Australia          Senate                  Liberal Democrats
## 56  2016 Australia          Senate                       Family First
## 57  2016 Australia          Senate                     Lambie Network
## 58  2016 Australia          Senate              Hinch's Justice Party
## 59  2013 Australia          Senate                             Labour
## 60  2013 Australia          Senate                             Greens
## 61  2013 Australia          Senate                      Palmer United
## 62  2013 Australia          Senate                      Xenophon Team
## 63  2013 Australia          Senate                  Liberal Democrats
## 64  2013 Australia          Senate                       Family First
## 65  2013 Australia          Senate                  Democratic Labour
## 66  2013 Australia          Senate                Motoring Enthusiast
## 67  2010 Australia          Senate                             Labour
## 68  2010 Australia          Senate                             Greens
## 69  2010 Australia          Senate                  Democratic Labour
## 70  2010 Australia          Senate                        Independent
## 71  2016    Russia            Duma                          Communist
## 72  2016    Russia            Duma Liberal Democratic Party of Russia
## 73  2016    Russia            Duma                      A Just Russia
## 74  2016    Russia            Duma                             Rodina
## 75  2016    Russia            Duma                     Civic Platform
## 76  2016    Russia            Duma                        Independent
## 77  2011    Russia            Duma                          Communist
## 78  2011    Russia            Duma                      A Just Russia
## 79  2011    Russia            Duma Liberal Democratic Party of Russia
## 80  2007    Russia            Duma                          Communist
## 81  2007    Russia            Duma Liberal Democratic Party of Russia
## 82  2007    Russia            Duma                      A Just Russia
## 83  2016       USA          Senate                         Democratic
## 84  2016       USA          Senate                        Independent
## 85  2014       USA          Senate                         Democratic
## 86  2014       USA          Senate                        Independent
## 87  2012       USA          Senate                         Republican
## 88  2012       USA          Senate                        Independent
## 89  2016       USA Representatives                         Democratic
## 90  2014       USA Representatives                         Democratic
## 91  2012       USA Representatives                         Democratic
## 92  2005   Germany       Bundestag            Social Democratic Party
## 93  2017   Germany       Bundestag            Social Democratic Party
## 94  2009   Germany       Bundestag              Free Democratic Party
## 95  1990   Germany       Bundestag              Free Democratic Party
## 96  2009   Germany       Bundestag                           The Left
## 97  2017   Germany       Bundestag                           The Left
## 98  2009   Germany       Bundestag             Alliance 90/The Greens
## 99  2017   Germany       Bundestag             Alliance 90/The Greens
## 100 2013   Germany       Bundestag                           The Left
## 101 2013   Germany       Bundestag             Alliance 90/The Greens
## 102 2005   Germany       Bundestag              Free Democratic Party
## 103 2002   Germany       Bundestag  Christian Social Union in Bavaria
## 104 2013   Germany       Bundestag  Christian Social Union in Bavaria
## 105 2002   Germany       Bundestag             Alliance 90/The Greens
## 106 2005   Germany       Bundestag                           The Left
## 107 2005   Germany       Bundestag             Alliance 90/The Greens
## 108 1990   Germany       Bundestag  Christian Social Union in Bavaria
## 109 1994   Germany       Bundestag  Christian Social Union in Bavaria
## 110 1994   Germany       Bundestag             Alliance 90/The Greens
## 111 2002   Germany       Bundestag              Free Democratic Party
## 112 1998   Germany       Bundestag  Christian Social Union in Bavaria
## 113 1998   Germany       Bundestag             Alliance 90/The Greens
## 114 1994   Germany       Bundestag              Free Democratic Party
## 115 2017   Germany       Bundestag  Christian Social Union in Bavaria
## 116 2005   Germany       Bundestag  Christian Social Union in Bavaria
## 117 1998   Germany       Bundestag              Free Democratic Party
## 118 1998   Germany       Bundestag      Party of Democratic Socialism
## 119 1994   Germany       Bundestag      Party of Democratic Socialism
## 120 1990   Germany       Bundestag      Party of Democratic Socialism
## 121 1990   Germany       Bundestag             Alliance 90/The Greens
## 122 2002   Germany       Bundestag      Party of Democratic Socialism
## 123 2017        UK         Commons                       Conservative
## 124 2015        UK         Commons                       Conservative
## 125 2010        UK         Commons                       Conservative
## 126 2010        UK         Commons                  Liberal Democrats
## 127 2016 Australia Representatives                            Liberal
## 128 2016 Australia Representatives                   Liberal National
## 129 2016 Australia Representatives                           National
## 130 2013 Australia Representatives                            Liberal
## 131 2013 Australia Representatives                   Liberal National
## 132 2013 Australia Representatives                           National
## 133 2013 Australia Representatives                    Country Liberal
## 134 2010 Australia Representatives                        Independent
## 135 2010 Australia Representatives                             Labour
## 136 2016 Australia          Senate                            Liberal
## 137 2016 Australia          Senate                   Liberal National
## 138 2016 Australia          Senate                           National
## 139 2016 Australia          Senate                    Country Liberal
## 140 2013 Australia          Senate                            Liberal
## 141 2010 Australia          Senate                            Liberal
## 142 2010 Australia          Senate                   Liberal National
## 143 2010 Australia          Senate                           National
## 144 2010 Australia          Senate                    Country Liberal
## 145 2016    Russia            Duma                      United Russia
## 146 2011    Russia            Duma                      United Russia
## 147 2007    Russia            Duma                      United Russia
## 148 1998   Germany       Bundestag            Social Democratic Party
## 149 1990   Germany       Bundestag         Christian Democratic Union
## 150 2013   Germany       Bundestag         Christian Democratic Union
## 151 1994   Germany       Bundestag            Social Democratic Party
## 152 2002   Germany       Bundestag            Social Democratic Party
## 153 1994   Germany       Bundestag         Christian Democratic Union
## 154 1990   Germany       Bundestag            Social Democratic Party
## 155 2005   Germany       Bundestag         Christian Democratic Union
## 156 2017   Germany       Bundestag         Christian Democratic Union
## 157 1998   Germany       Bundestag         Christian Democratic Union
## 158 2009   Germany       Bundestag         Christian Democratic Union
## 159 2013   Germany       Bundestag            Social Democratic Party
## 160 2002   Germany       Bundestag         Christian Democratic Union
## 161 2009   Germany       Bundestag            Social Democratic Party
## 162 2017   Germany       Bundestag            Alternative for Germany
## 163 2017   Germany       Bundestag              Free Democratic Party
## 164 2009   Germany       Bundestag  Christian Social Union in Bavaria
##     party_short seats government  colour
## 1           GOP    52          1 #E81B23
## 2           GOP    54          1 #E81B23
## 3           Dem    53          1 #3333FF
## 4           GOP   241          1 #E81B23
## 5           GOP   247          1 #E81B23
## 6           GOP   234          1 #E81B23
## 7           Lab   262          0 #DC241F
## 8           SNP    35          0 #FEF987
## 9        LibDem    12          0 #FAA61A
## 10          DUP    10          1 #D46A4C
## 11           SF     7          0 #008800
## 12           PC     4          0 #008142
## 13        Green     1          0 #6AB023
## 14          Ind     1          0 #B4B4B4
## 15          Lab   232          0 #DC241F
## 16          SNP    56          0 #FEF987
## 17       LibDem     8          0 #FAA61A
## 18          DUP     8          0 #D46A4C
## 19           SF     4          0 #008800
## 20           PC     3          0 #008142
## 21         SDLP     3          0 #99FF66
## 22          UUP     2          0 #9999FF
## 23         UKIP     1          0 #70147A
## 24        Green     1          0 #6AB023
## 25          Ind     1          0 #B4B4B4
## 26          Lab   258          0 #DC241F
## 27          DUP     8          0 #D46A4C
## 28          SNP     6          0 #FEF987
## 29           SF     5          0 #008800
## 30           PC     3          0 #008142
## 31         SDLP     3          0 #99FF66
## 32        Green     1          0 #6AB023
## 33     Alliance     1          0 #F6CB2F
## 34          Ind     1          0 #B4B4B4
## 35          Lab    69          0 #F00011
## 36        Green     1          0 #10C25B
## 37     Xenophon     1          0 #FF6300
## 38          Ind     2          0 #B8B8B8
## 39       Katter     1          0 #FE6F5E
## 40          Lab    55          0 #F00011
## 41          Ind     2          0 #B8B8B8
## 42        Green     1          0 #10C25B
## 43       Palmer     1          0 #FFED00
## 44       Katter     1          0 #FE6F5E
## 45          Lib    44          0 #080CAB
## 46          LNP    21          0 #1456F1
## 47          Nat     6          0 #008000
## 48        Green     1          0 #10C25B
## 49       Nat WA     1          0 #008000
## 50          CLP     1          0 #FF7F00
## 51          Lab    26          0 #F00011
## 52        Green     9          0 #10C25B
## 53          HON     4          0 #F8F16F
## 54     Xenophon     3          0 #FF6300
## 55       LibDem     1          0 #F9E518
## 56           FF     1          0 #00CCFF
## 57       Lambie     1          0 #FFFF00
## 58        Hinch     1          0 #002F5D
## 59          Lab    25          0 #F00011
## 60        Green    10          0 #10C25B
## 61       Palmer     3          0 #FFED00
## 62     Xenophon     1          0 #FF6300
## 63       LibDem     1          0 #F9E518
## 64           FF     1          0 #00CCFF
## 65      Dem Lab     1          0 #008080
## 66     Motoring     1          0 #191970
## 67          Lab    31          0 #F00011
## 68        Green     9          0 #10C25B
## 69      Dem Lab     1          0 #008080
## 70          Ind     1          0 #B4B4B4
## 71         CPRF    42          0 #D50000
## 72         LDPR    39          0 #2862B3
## 73           JR    23          0 #FAB512
## 74       Rodina     1          0 #EA484A
## 75          CPI     1          0 #641263
## 76          Ind     1          0 #B4B4B4
## 77         CPRF    92          0 #D50000
## 78           JR    64          0 #FAB512
## 79         LDPR    56          0 #2862B3
## 80         CPRF    57          0 #D50000
## 81         LDPR    40          0 #2862B3
## 82           JR    38          0 #FAB512
## 83          Dem    46          0 #3333FF
## 84          Ind     2          0 #B4B4B4
## 85          Dem    44          0 #3333FF
## 86          Ind     2          0 #B4B4B4
## 87          GOP    45          0 #E81B23
## 88          Ind     2          0 #B4B4B4
## 89          Dem   194          0 #3333FF
## 90          Dem   188          0 #3333FF
## 91          Dem   201          0 #3333FF
## 92          SDP   180          0 #EB001F
## 93          SDP   153          0 #EB001F
## 94          FDP    93          0 #FFED00
## 95          FDP    79          0 #FFED00
## 96        LINKE    76          0 #BE3075
## 97        LINKE    69          0 #BE3075
## 98        GRUNE    68          0 #64A12D
## 99        GRUNE    67          0 #64A12D
## 100       LINKE    64          0 #BE3075
## 101       GRUNE    63          0 #64A12D
## 102         FDP    61          0 #FFED00
## 103         CSU    58          0 #008AC5
## 104         CSU    56          0 #008AC5
## 105       GRUNE    55          0 #64A12D
## 106       LINKE    54          0 #BE3075
## 107       GRUNE    51          0 #64A12D
## 108         CSU    51          0 #008AC5
## 109         CSU    50          0 #008AC5
## 110       GRUNE    49          0 #64A12D
## 111         FDP    47          0 #FFED00
## 112         CSU    47          0 #008AC5
## 113       GRUNE    47          0 #64A12D
## 114         FDP    47          0 #FFED00
## 115         CSU    46          0 #008AC5
## 116         CSU    46          0 #008AC5
## 117         FDP    43          0 #FFED00
## 118         PDS    36          0 #CD1811
## 119         PDS    30          0 #CD1811
## 120         PDS    17          0 #CD1811
## 121       GRUNE     8          0 #64A12D
## 122         PDS     2          0 #CD1811
## 123         Con   317          1 #0087DC
## 124         Con   330          1 #0087DC
## 125         Con   306          1 #0087DC
## 126      LibDem    57          1 #FAA61A
## 127         Lib    45          1 #080CAB
## 128         LNP    21          1 #1456F1
## 129         Nat    10          1 #008000
## 130         Lib    58          1 #080CAB
## 131         LNP    22          1 #1456F1
## 132         Nat     9          1 #008000
## 133         CLP     1          1 #FF7F00
## 134         Ind     4          0 #B8B8B8
## 135         Lab    72          1 #F00011
## 136         Lib    21          1 #080CAB
## 137         LNP     5          1 #1456F1
## 138         Nat     3          1 #008000
## 139         CLP     1          1 #FF7F00
## 140         Lib    33          1 #080CAB
## 141         Lib    24          1 #080CAB
## 142         LNP     6          1 #1456F1
## 143         Nat     3          1 #008000
## 144         CLP     1          1 #FF7F00
## 145          UR   343          1 #0C2C84
## 146          UR   238          1 #0C2C84
## 147          UR   315          1 #0C2C84
## 148         SDP   298          1 #EB001F
## 149         CDU   268          1 #000000
## 150         CDU   255          1 #000000
## 151         SDP   252          1 #EB001F
## 152         SDP   251          1 #EB001F
## 153         CDU   244          1 #000000
## 154         SDP   239          1 #EB001F
## 155         CDU   222          1 #000000
## 156         CDU   200          1 #000000
## 157         CDU   198          1 #000000
## 158         CDU   194          1 #000000
## 159         SDP   193          1 #EB001F
## 160         CDU   190          1 #000000
## 161         SDP   146          1 #EB001F
## 162         AFD    94          1 #009EE0
## 163         FDP    80          1 #FFED00
## 164         CSU    45          1 #008AC5

Vamos a filtrar solo los resultados de Rusia del 2016

rusia <- election_data %>%
  filter(country == "Russia" & year == 2016)

Con la función parliament_data construiremos los datos, preparados para ser visualizados en formato hemiciclo (con parl_rows le indicamos las fila del parlamento).

rusia_parlamento <-
  as_tibble(parliament_data(election_data = rusia, type = "semicircle",
                            parl_rows = 9, party_seats = rusia$seats))
rusia_parlamento
## # A tibble: 450 × 12
##     year country house party_long party_short seats government colour      x
##    <int> <chr>   <chr> <chr>      <chr>       <int>      <int> <chr>   <dbl>
##  1  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -2   
##  2  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -1.88
##  3  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -1.75
##  4  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -1.62
##  5  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -1.5 
##  6  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -1.38
##  7  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -1.25
##  8  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -1.12
##  9  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -1   
## 10  2016 Russia  Duma  Communist  CPRF           42          0 #D50000 -2.00
## # … with 440 more rows, and 3 more variables: y <dbl>, row <int>, theta <dbl>

Fíjate que por defecto ha incluido ya un color por partido (que podríamos cambiar en ggplot2. Tras ello, y dentro de la filosofía ggplot2, podremos representar los datos de nuestro parlamento.

parlamento <-
  ggplot(rusia_parlamento,
       aes(x = x, y = y, colour = party_short)) +
  geom_parliament_seats()
parlamento

Podemos añadir las etiquetas de los partidos así como el número total de escaños

parlamento +
  draw_partylabels(type = "semicircle",
                   party_names = party_long,
                   party_seats = seats,
                   party_colours = colour) + 
  draw_totalseats(n = 450, type = "semicircle")

Con theme_ggparliament() tenemos un tema ya preparado para la representación del hemiciclo, y añadimos títulos y colores.

parlamento +
  draw_totalseats(n = 450, type = "semicircle") +
  theme_ggparliament() +
  labs(color = "Partidos",
       title = "Resultados de las elecciones de Rusia 2016") +
  scale_colour_manual(values = rusia_parlamento$colour, 
                      limits = rusia_parlamento$party_short) +
  theme(plot.margin = margin(t = 4, r = 4, b = 4, l = 8, "pt"),
        plot.title = element_text(size = 30))

23.3 Análisis univariante: cualitativas ordinales

Ahora que sabemos describir datos cualitativos utilizando algunos conceptos fundamentales, vamos a profundizar en el análisis de un tipo de dato cuantitativo que son los datos ordinales.

Las variables ordinales son también cualidades no numéricas de objetos e individuos, pero en este caso tienen una jerarquía de orden que no solo permite ordenar sino acumular observaciones, es decir, estudiar cuantos elementos se acumulan por encima o por debajo de un nivel, qué variación hay de un nivel a otro, etc. Las variables ordinales no se caracterizan sólo por sus \(k\) niveles\(l_1,...l_k\), también se caracterizan por su relación de orden\(l_1 <...<l_k\)

Estos tipo de datos son muy frecuentes, por ejemplo, en estudios demográficos, en conceptos como nivel socioeconómico, nivel de estudios y en encuestas de opinión donde se suelen usar las escalas tipo Likert.

El concepto clave aquí es por lo tanto la frecuencia acumulada, que de nuevo podrá ser absoluta o relativa. La función cumsum() es la función fundamental para calcular estos valores sobre un conjunto de datos.

23.3.1 Tablas de frecuencias para ordinales

Vamos a construir la tabla de frecuencias acumuladas de la lista de notas del curso anterior, cuyos niveles son: "suspenso", "aprobado", "notable" y "sobresaliente". Su orden lo consideramos como: "suspenso" < "aprobado" < "notable" < "sobresaliente". Las calificaciones que han obtenido son las siguientes:

notas_curso <-
  c("aprobado", "aprobado", "notable", "suspenso",
    "suspenso", "aprobado", "notable", "sobresaliente",
    "aprobado", "aprobado", "suspenso", "suspenso",
    "suspenso", "aprobado", "sobresaliente", "notable",
    "notable", "sobresaliente", "suspenso", "aprobado")
notas_curso
##  [1] "aprobado"      "aprobado"      "notable"       "suspenso"     
##  [5] "suspenso"      "aprobado"      "notable"       "sobresaliente"
##  [9] "aprobado"      "aprobado"      "suspenso"      "suspenso"     
## [13] "suspenso"      "aprobado"      "sobresaliente" "notable"      
## [17] "notable"       "sobresaliente" "suspenso"      "aprobado"

Lo primero será ver cuántos registros hay de cada clase y con cuanta frecuencia aparecen. Para eso vamos a utilizar de nuevo la función count() tras pasar nuestros datos a formato tibble.

notas_curso <- tibble("notas" = notas_curso)

# Frecuencias absolutas + relativas
notas_freq <-
  notas_curso %>%
  count(notas) %>%
  mutate(f = n / sum(n))
notas_freq
## # A tibble: 4 × 3
##   notas             n     f
##   <chr>         <int> <dbl>
## 1 aprobado          7  0.35
## 2 notable           4  0.2 
## 3 sobresaliente     3  0.15
## 4 suspenso          6  0.3

Observamos que tenemos las siguientes calificaciones: 6 suspensos, 7 aprobados, 4 notables y 3 sobresalientes. Sin embargo, esto no responde a la pregunta de cuántos de los alumnos han obtenido una calificación inferior a notable. Para ello vamos a calcular la tabla de frecuencias acumuladas:

  • \(n_i\): frecuencia absoluta
  • \(N_i\): absoluta acumulada
  • \(f_i\): relativa
  • \(F_i\): acumulada.

Para ello antes vamos a convertir la variable a factor, indicándole que es ordinal, con los niveles ordeandos y ordered = TRUE

notas_curso <- notas_curso %>%
  mutate(notas =
           factor(notas,
                  levels = c("suspenso", "aprobado",
                             "notable", "sobresaliente"),
                  ordered = TRUE))
notas_curso %>% pull(notas)
##  [1] aprobado      aprobado      notable       suspenso      suspenso     
##  [6] aprobado      notable       sobresaliente aprobado      aprobado     
## [11] suspenso      suspenso      suspenso      aprobado      sobresaliente
## [16] notable       notable       sobresaliente suspenso      aprobado     
## Levels: suspenso < aprobado < notable < sobresaliente
notas_freq_acum <-
  notas_curso %>%
  count(notas) %>%
  rename(n_i = n) %>%
  mutate(f_i = n_i / sum(n_i),
         N_i = cumsum(n_i),
         F_i = cumsum(N_i))
notas_freq_acum
## # A tibble: 4 × 5
##   notas           n_i   f_i   N_i   F_i
##   <ord>         <int> <dbl> <int> <int>
## 1 suspenso          6  0.3      6     6
## 2 aprobado          7  0.35    13    19
## 3 notable           4  0.2     17    36
## 4 sobresaliente     3  0.15    20    56

Podemos visualizar tanto las acmuladas como las no acumuladas con un diagrama de barras.

library(MetBrewer)
ggplot(notas_freq_acum,
       aes(x = notas, y = n_i,
           fill = as.factor(n_i))) +
  geom_col(alpha = 0.7) +
  scale_fill_manual(values = met.brewer("Klimt")) +
  guides(fill = "none") +
  labs(x = "Notas", y = "Frec. absolutas acumuladas",
       title = "FRECUENCIAS ABSOLUTAS Y RELATIVAS")
ggplot(notas_freq_acum,
       aes(x = notas, y = N_i,
           fill = as.factor(N_i))) +
  geom_col(alpha = 0.7) +
  scale_fill_manual(values = met.brewer("Klimt")) +
  guides(fill = "none") +
  labs(x = "Notas", y = "Frec. absolutas acumuladas",
       title = "FRECUENCIAS ABSOLUTAS Y RELATIVAS")
ggplot(notas_freq_acum,
       aes(x = notas, y = F_i,
           fill = as.factor(F_i))) +
  geom_col(alpha = 0.7) +
  scale_fill_manual(values = met.brewer("Klimt")) +
  guides(fill = "none") +
  labs(x = "Notas", y = "Frec. relativas acumuladas",
       title = "FRECUENCIAS ABSOLUTAS Y RELATIVAS")

Puedes ampliar sobre cómo recategorizar variables continuas en ordinales en 19 Depuración y transformación

23.4 📝 Ejercicios

(haz click en las flechas para ver soluciones)

📝Ejercicio 1: usando dplyr de {tidvyerse} calcula la tabla de frecuencias absolutas de la variableskin_color del conjunto de datos de starwars.

  • Solución:
library(tidyverse)
starwars %>% count(skin_color) 
## # A tibble: 31 × 2
##    skin_color              n
##    <chr>               <int>
##  1 blue                    2
##  2 blue, grey              2
##  3 brown                   4
##  4 brown mottle            1
##  5 brown, white            1
##  6 dark                    6
##  7 fair                   17
##  8 fair, green, yellow     1
##  9 gold                    1
## 10 green                   6
## # … with 21 more rows

 

📝Ejercicio 2: repite el ejercicio anterior pero reagrupa los niveles poco frecuentes (poco frecuente definido como que aparezca dos o menos veces) con fct_lump_min().

  • Solución:
starwars %>%
  mutate(fskin_color =
           fct_lump_min(skin_color, min = 3)) %>%
  count(fskin_color) 
## # A tibble: 8 × 2
##   fskin_color     n
##   <fct>       <int>
## 1 brown           4
## 2 dark            6
## 3 fair           17
## 4 green           6
## 5 grey            6
## 6 light          11
## 7 pale            5
## 8 Other          32

 

📝Ejercicio 3: añade a la tabla anterior la tabla de frecuencias relativas de la columna skin_color.

  • Solución:
freq_skin <-
  starwars %>%
  mutate(fskin_color =
           fct_lump_min(skin_color, min = 3)) %>%
  count(fskin_color) %>% 
  rename(ni = n) %>% 
  mutate(fi = ni / sum(ni))
freq_skin
## # A tibble: 8 × 3
##   fskin_color    ni     fi
##   <fct>       <int>  <dbl>
## 1 brown           4 0.0460
## 2 dark            6 0.0690
## 3 fair           17 0.195 
## 4 green           6 0.0690
## 5 grey            6 0.0690
## 6 light          11 0.126 
## 7 pale            5 0.0575
## 8 Other          32 0.368

 

📝Ejercicio 4: recategoriza la variable height usando la función cut con 6 cortes (filtrando antes los valores ausentes). Formatea esa nueva columna como un factor, calculando sus frecuencias y agrupa para no tener clases con menos de 7 valores.

  • Solución:
starwars_fheight <-
  starwars %>%
  filter(!is.na(height)) %>%
  mutate(fheight = factor(cut(height, breaks = 6))) 

# Frecuencias
fct_count(starwars_fheight$fheight)
## # A tibble: 6 × 2
##   f             n
##   <fct>     <int>
## 1 (65.8,99]     7
## 2 (99,132]      2
## 3 (132,165]    10
## 4 (165,198]    51
## 5 (198,231]     9
## 6 (231,264]     2
# Reagrupamos
starwars_fheight <-
  starwars_fheight %>%
  mutate(fheight =
           fct_lump_min(fheight, min = 7,
                        other_level = "otros"))
fct_count(starwars_fheight$fheight)
## # A tibble: 5 × 2
##   f             n
##   <fct>     <int>
## 1 (65.8,99]     7
## 2 (132,165]    10
## 3 (165,198]    51
## 4 (198,231]     9
## 5 otros         4

 

📝Ejercicio 5: genera las frecuencias acumuladas resultantes de la columna recategorizada del ejercicio anterior.

  • Solución:
freq_fheight <-
  starwars_fheight %>% 
  count(fheight) %>%
  rename(ni = n) %>% 
  mutate(fi = ni / sum(ni),
         Ni = cumsum(ni),
         Fi = cumsum(fi))
freq_fheight
## # A tibble: 5 × 5
##   fheight      ni     fi    Ni     Fi
##   <fct>     <int>  <dbl> <int>  <dbl>
## 1 (65.8,99]     7 0.0864     7 0.0864
## 2 (132,165]    10 0.123     17 0.210 
## 3 (165,198]    51 0.630     68 0.840 
## 4 (198,231]     9 0.111     77 0.951 
## 5 otros         4 0.0494    81 1