Tabla de contingencia

Introducción

Una tabla de contingencia es una tabla en la que se resume la distribución de frecuencias conjuntas de dos variables. En esta tabla, cada fila representa una categoría de la variable \(X\) y cada columna representa una ctegoría de la variable \(Y\).

Cada celda de la tabla representa la frecuencia con la que aparecen conjuntamente las categorías de la fila y la columna en la que está ubicada. Por ejemplo, la celda en la fila 2 con columna 3, es la frecuencia de la categoría 2 de las filas, y la categoría 3 de las columnas.

Las tablas de contingencia también son conocidas como tablas cruzadas, tablas de frecuencias de doble entrada o tablas de frecuencias bidimensionales.

Estuve buscando en internet si existía alguna notación estándar para las tablas de contingencia y no conseguí nada. Sin embargo, utilicé una notación que me pareció lógica y bastante parecida a la que me enseñaron en la universidad.

Datos

Vamos a cargar el conjunto de datos Cars93 del paquete MASS. Al igual que como hemos hecho con iris, vamos a limpiar los nombres y además le ponemos un nombre más alineado con la guía de estilos del tidyverse.

library(dplyr)
library(janitor)

data("Cars93", package = "MASS")
(cars <- Cars93 %>% as_tibble() %>% clean_names())
## # A tibble: 93 x 27
##    manufacturer model type  min_price price max_price mpg_city mpg_highway
##    <fct>        <fct> <fct>     <dbl> <dbl>     <dbl>    <int>       <int>
##  1 Acura        Inte… Small      12.9  15.9      18.8       25          31
##  2 Acura        Lege… Mids…      29.2  33.9      38.7       18          25
##  3 Audi         90    Comp…      25.9  29.1      32.3       20          26
##  4 Audi         100   Mids…      30.8  37.7      44.6       19          26
##  5 BMW          535i  Mids…      23.7  30        36.2       22          30
##  6 Buick        Cent… Mids…      14.2  15.7      17.3       22          31
##  7 Buick        LeSa… Large      19.9  20.8      21.7       19          28
##  8 Buick        Road… Large      22.6  23.7      24.9       16          25
##  9 Buick        Rivi… Mids…      26.3  26.3      26.3       19          27
## 10 Cadillac     DeVi… Large      33    34.7      36.3       16          25
## # … with 83 more rows, and 19 more variables: air_bags <fct>,
## #   drive_train <fct>, cylinders <fct>, engine_size <dbl>, horsepower <int>,
## #   rpm <int>, rev_per_mile <int>, man_trans_avail <fct>,
## #   fuel_tank_capacity <dbl>, passengers <int>, length <int>, wheelbase <int>,
## #   width <int>, turn_circle <int>, rear_seat_room <dbl>, luggage_room <int>,
## #   weight <int>, origin <fct>, make <fct>

Ejemplo

Podemos utilizar la función table para obtener una tabla de contingencia:

cars %>% 
  select(type, air_bags) %>% 
  table()
##          air_bags
## type      Driver & Passenger Driver only None
##   Compact                  2           9    5
##   Large                    4           7    0
##   Midsize                  7          11    4
##   Small                    0           5   16
##   Sporty                   3           8    3
##   Van                      0           3    6

Según este resultado, 11 de los automóviles son de tipo Midsize y a su vez tienen bolsa de aire (air bag) en el asiento del conductor sólamente (Driver only), mientras que ningún auto tipo Small tiene air bag en los asientos del conductor y del pasajero.

El resultado de la función table es un objeto de tipo matrix, una matriz. Aunque estos objetos tienen algunas cosas en común con los data.frame, tienen ciertas diferencias que te pueden causar problemas si no sabes bien lo que estás haciendo.

Pregunta

¿Cuántos autos son de tipo Sporty y no tienen bolsa de aire (None)?


Mostrar respuesta

La frecuencia de la fila \(i\) fila y la columna \(j\) se denota por \(f_{ij}\) y se lee “efe sub i, jota”, similar a la notación de las tablas de frecuencia unidimensionales, sólo que con dos índices, uno para las filas y uno para las columnas. Cuando substituímos \(ij\) por números, podemos agregar una coma para separar los índices y evitar confusiones. En el ejemplo anterior diríamos que \(f_{3,2}=11\).

Si sumamos todas las frecuencias conjuntas obtenemos el tamaño de la muestra:

\[ \sum_{i=1}^{c}\sum_{j=1}^{r}{f_{ij}} = n \]

Lo comprobamos en R:

cars %>% 
  select(type, air_bags) %>% 
  table() %>% 
  sum()
## [1] 93
nrow(cars)
## [1] 93

Tabla de frecuencias relativas

Nos preguntamos ahora ¿qué proporción de estos autos tienen bolsa de aire en ambos asientos (Driver & Passenger) y son grandes (Large)?. Tendríamos entonces que dividir esa frecuencia conjunta entre el total de datos: \(4/93=0.043\). En otras palabras, \(4.3\)% de los autos tienen simultáneamente las dos características que nos interesan.

En una tabla de frecuencias relativas, cada celda representa la proporción de elementos que pertenecen a la fila y columna en la que está. La frecuencia relativa de la fila \(i\) y la columna \(j\) se denota por \(h_{ij}\) y se calcula de la siguiente manera:

\[ h_{ij}=\frac{f_{ij}}{n} \]

Para enfatizar la diferencia entre \(f_{ij}\) y \(h_{ij}\), diremos que \(f_{ij}\) es la frecuencia absoluta de la fila \(i\) y la columna \(j\).

Podemos obtener una tabla de frecuencias relativas calculando la tabla de frecuencias absolutas y usando la función prop.table:

abs_table <- cars %>% 
  select(type, air_bags) %>% 
  table()

(rel_table <- prop.table(abs_table))
##          air_bags
## type      Driver & Passenger Driver only       None
##   Compact         0.02150538  0.09677419 0.05376344
##   Large           0.04301075  0.07526882 0.00000000
##   Midsize         0.07526882  0.11827957 0.04301075
##   Small           0.00000000  0.05376344 0.17204301
##   Sporty          0.03225806  0.08602151 0.03225806
##   Van             0.00000000  0.03225806 0.06451613

Propiedad

La suma de todas las frecuencias relativas es igual a 1

\[ \sum_{i=1}^{c}\sum_{j=1}^{r}{h_{ij}} = 1 \]

Lo comprobamos en R:

sum(rel_table)
## [1] 1

Frecuencias marginales

En una tabla de frecuencias, llamamos frecuencias a la suma de las frecuencias de cada fila o columna.

  • La frecuencia marginal de la fila \(i\) se denota por \(f_{i.}\) y se lee efe sub i, punto
  • La frecuencia marginal de la columna \(j\) se denota por \(f_{.j}\) y se lee efe sub punto, jota

Para calcular la frecuencia marginal de la fila \(i\), sumamos en esa fila todas las columnas \[ f_{i.} = \sum_{j=1}^{c}{f_{ij}} \]

Para calcular la frecuencia marginal de la columna \(j\), sumamos en esa columna todas las filas \[ f_{.j} = \sum_{i=1}^{r}{f_{ij}} \]

Recordemos que la tabla de frecuencias absolutas se ve así:

abs_table
##          air_bags
## type      Driver & Passenger Driver only None
##   Compact                  2           9    5
##   Large                    4           7    0
##   Midsize                  7          11    4
##   Small                    0           5   16
##   Sporty                   3           8    3
##   Van                      0           3    6

Ahora podemos usar la función margin.table con 1 para las filas y 2 para las columnas:

(row_marginals <- margin.table(abs_table, 1))
## type
## Compact   Large Midsize   Small  Sporty     Van 
##      16      11      22      21      14       9

Si sumamos las frecuencias en la fila Compact obtenemos el resultado \(16 = 2 + 9 + 5\).

(col_marginals <- margin.table(abs_table, 2))
## air_bags
## Driver & Passenger        Driver only               None 
##                 16                 43                 34

Si sumamos las frecuencias de la columna Driver only obtenemos el resultado \(43 = 9 + 7 + 11 + 5 + 8 + 3\).

Frecuencias relativas marginales

También podemos calcular Frecuencias relativas marginales, dividiendo las frecuencias marginales entre el tamaño de la muestra o sumando las frecuencias relativas por filas o columnas:

\[ h_{i.}=\frac{f_{i.}}{n}=\sum_{j=1}^{c}{h_{ij}} \]

\[ h_{.j}=\frac{f_{.j}}{n}=\sum_{i=1}^{r}{h_{ij}} \]

Y ahora en R podemos usar la función margin.table esta vez con la tabla de frecuencias relativas:

(row_rel_marginals <- margin.table(rel_table, 1))
## type
##    Compact      Large    Midsize      Small     Sporty        Van 
## 0.17204301 0.11827957 0.23655914 0.22580645 0.15053763 0.09677419
(col_rel_marginals <- margin.table(rel_table, 2))
## air_bags
## Driver & Passenger        Driver only               None 
##          0.1720430          0.4623656          0.3655914

Si tenemos la data desagregada, podemos usar count o una combinación de group_by y summarise, como hicimos en el artículo de tablas de frecuencia. La ventaja es que el resultado es un tibble o data.frame en lugar de un matrix.

cars %>% count(type)
## # A tibble: 6 x 2
##   type        n
##   <fct>   <int>
## 1 Compact    16
## 2 Large      11
## 3 Midsize    22
## 4 Small      21
## 5 Sporty     14
## 6 Van         9
cars %>% count(air_bags)
## # A tibble: 3 x 2
##   air_bags               n
##   <fct>              <int>
## 1 Driver & Passenger    16
## 2 Driver only           43
## 3 None                  34

Frecuencias condicionales

Si nos interesa saber qué proporción de una fila o una columna representa una frecuencia conjunta, debemos calcular una Frecuencia condicional.

  • La frecuencia condicional de la fila \(i\) dada la columna \(j\) se denota por \(f_{i|c=j}\) y se lee efe sub i dado jota \[ f_{i|c=j}=\frac{f_{ij}}{f_{.j}}=\frac{h_{ij}}{h_{.j}} \]

  • La frecuencia condicional de la columna \(j\) dada la fila \(i\) se denota por \(f_{j|r=i}\) y se lee efe sub i dado jota \[ f_{j|r=i}=\frac{f_{ij}}{f_{i.}}=\frac{h_{ij}}{h_{i.}} \]

Podemos calcular tablas de frecuencias condicionales con la función prop.table con 1 para condicionar por filas y 2 para condicionar las columnas:

Por filas:

prop.table(abs_table, 1)
##          air_bags
## type      Driver & Passenger Driver only      None
##   Compact          0.1250000   0.5625000 0.3125000
##   Large            0.3636364   0.6363636 0.0000000
##   Midsize          0.3181818   0.5000000 0.1818182
##   Small            0.0000000   0.2380952 0.7619048
##   Sporty           0.2142857   0.5714286 0.2142857
##   Van              0.0000000   0.3333333 0.6666667

Por columnas:

prop.table(abs_table, 2)
##          air_bags
## type      Driver & Passenger Driver only       None
##   Compact         0.12500000  0.20930233 0.14705882
##   Large           0.25000000  0.16279070 0.00000000
##   Midsize         0.43750000  0.25581395 0.11764706
##   Small           0.00000000  0.11627907 0.47058824
##   Sporty          0.18750000  0.18604651 0.08823529
##   Van             0.00000000  0.06976744 0.17647059

También obtenemos el mismo resultado si hacemos los cálculos con las frecuencias relativas:

Por filas:

prop.table(rel_table, 1)
##          air_bags
## type      Driver & Passenger Driver only      None
##   Compact          0.1250000   0.5625000 0.3125000
##   Large            0.3636364   0.6363636 0.0000000
##   Midsize          0.3181818   0.5000000 0.1818182
##   Small            0.0000000   0.2380952 0.7619048
##   Sporty           0.2142857   0.5714286 0.2142857
##   Van              0.0000000   0.3333333 0.6666667

Por columnas:

prop.table(rel_table, 2)
##          air_bags
## type      Driver & Passenger Driver only       None
##   Compact         0.12500000  0.20930233 0.14705882
##   Large           0.25000000  0.16279070 0.00000000
##   Midsize         0.43750000  0.25581395 0.11764706
##   Small           0.00000000  0.11627907 0.47058824
##   Sporty          0.18750000  0.18604651 0.08823529
##   Van             0.00000000  0.06976744 0.17647059

Pregunta

¿Sabes por qué obtienes el mismo resultado haciendo el cálculo con frecuencias absolutas o relativas?

Propiedad

Cada fila de la tabla de frecuencias relativas por fila suma 1, y cada columna de la tabla de frecuencias relativas por columnas suma 1.

rowSums(prop.table(abs_table, 1))
## Compact   Large Midsize   Small  Sporty     Van 
##       1       1       1       1       1       1
colSums(prop.table(abs_table, 2))
## Driver & Passenger        Driver only               None 
##                  1                  1                  1