En este recurso revisaremos cuales son las diferentes formas de organización que pueden tener conjuntos de datos dominados por variables categóricas, como se visualizan en R y como podemos, a partir de dichos formatos, generar conteos o tablas de contingencia en R.
- Datos de prueba
- Formatos de organización
- Generando conteos y tablas de contigencia
- Resumen de comandos
- Ejercicios
- Referencias
- Para saber más
Datos de prueba
Tipos de Sangre
Carmona-Fonseca (2006) reporta datos de tipos sanguíneos para una muestra de 827 trabajadores afiliados al Seguro Social en el valle de Aburrá y en el cercano oriente antioqueño. Su objetivo fue aportar al conocimiento de la distribución de los tipos sanguíneos en la región. Los datos se encuentran el archivo carmonaABO.csv en el cual cada fila es un sujeto y cada columna es una variable y las columnas están separadas por una coma. Importe los datos con el siguiente código:
carmona <- read.csv(file = "carmonaABO.csv") # se importan los datos
str(carmona) # se imprime su estructura'data.frame': 827 obs. of 4 variables:
$ sujeto: int 1 2 3 4 5 6 7 8 9 10 ...
$ region: chr "aburra" "aburra" "aburra" "aburra" ...
$ rh : chr "pos" "pos" "pos" "pos" ...
$ abo : chr "O" "O" "A" "O" ...
Plantas raras
Un estudio citado por Gotelli & Ellison (2013) pretendió evaluar factores asociados con el estatus de conservación de 73 poblaciones de plantas raras en Nueva Inglaterra. A cada población se le identificó si estaba declinando en tamaño y si estaba legalmente protegida entre otras variables. Los datos se encuentran en el archivo rare_plants_agrup.txt donde cada columna es una variable y cada fila es una combinación de categorías de las variables observadas. Las columnas están separadas por un espacio sencillo.
plantas <- read.csv(file = "rare_plants_agrup.txt", sep = "") # se importa el archivo
str(plantas) # se imprime su estructura'data.frame': 40 obs. of 5 variables:
$ decline : chr "yes" "yes" "yes" "yes" ...
$ invaded : chr "no" "no" "no" "no" ...
$ protected: chr "no" "no" "no" "no" ...
$ light : int 0 1 2 3 4 0 1 2 3 4 ...
$ frequency: int 2 0 2 2 0 1 0 0 7 4 ...
Formatos de organización
Un conjunto de datos con dominancia de variables categóricas puede presentarse bajo tres formatos de organización:
Datos crudos
Donde cada fila es un sujeto (o un registro). De esta manera se encuentran los datos descargados del archivo carmonaABO.csv.
head(carmona, 3) # tres primeras filas del data.frame carmona sujeto region rh abo
1 1 aburra pos O
2 2 aburra pos O
3 3 aburra pos A
Observe que tenemos una columna que indica el sujeto. En este formato, la cantidad total de sujetos se corresponde con el número de filas de la tabla.
Datos agrupados en formato largo
Donde cada fila es una combinación de categorías de las variables observadas. Debe existir una columna que indica la frecuencia de cada combinación. Los datos del archivo rare_plants_agrup.txt están bajo este formato:
head(plantas, 3) # tres primeras filas del data.frame plantas decline invaded protected light frequency
1 yes no no 0 2
2 yes no no 1 0
3 yes no no 2 2
Note que en este problema el “sujeto” es una población de plantas de modo que existen 73 “sujetos” . Sin embargo, cada fila en la tabla plantas no significa una población o “sujeto” si no una combinación de categorías de las variables observadas a cada población. La columna frequency representa la cantidad de “sujetos” o poblaciones con dicha combinación. Por ejemplo, de la 1era. fila concluimos que existen 2 poblaciones (de las 73 en total) que tienen decline = yes; invaded = no; protected = no y light = 0. Así, la cantidad total de “sujetos” en este formato corresponde la suma de la columna de frecuencia.
Datos agrupados en una tabla de contingencia
En este caso el conteo para cada combinación de categorías se organiza en una tabla de contingencia. Esta tabla puede ser plana como se muestra enseguida para los datos de carmona:
abo A AB B O
rh neg pos neg pos neg pos neg pos
region
aburra 15 118 1 7 6 34 29 205
oriente 10 118 0 3 1 20 31 229
Pero también puede ser una tabla de mutiples dimensiones (una dimensión por cada variable). Los datos de carmona tienen tres variables que generarían una tabla de contingencia de tres dimensiones, por ejemplo de 2 (rh) x 4 (abo) x 2 (regiones), como se observa enseguida:
, , region = aburra
abo
rh A AB B O
neg 15 1 6 29
pos 118 7 34 205
, , region = oriente
abo
rh A AB B O
neg 10 0 1 31
pos 118 3 20 229
Generando conteos y tablas de contigencia
El análisis inicial con variables categóricas es generar conteos (frec. absolutas) de sus categorías. Estos conteos se organizan tipicamente en tablas de contigencia (planas o multidimensionales). Algunos comandos de R que permiten hacer esto son xtabs y ftable.
Usando xtabs
El comando xtabs permite generar tablas de conteos de una o más dimensiones usando la dupla de argumentos formula y data. Aquí algunos ejemplos desde los datos crudos del data.frame carmona:
xtabs(~ region, data = carmona) # tabla de conteos de una variableregion
aburra oriente
415 412
xtabs(~ region + abo, data = carmona) # tabla de conteos de dos variables abo
region A AB B O
aburra 133 8 40 234
oriente 128 3 21 260
xtabs(~ rh + abo + region, data = carmona) # tabla de conteos de tres variables, , region = aburra
abo
rh A AB B O
neg 15 1 6 29
pos 118 7 34 205
, , region = oriente
abo
rh A AB B O
neg 10 0 1 31
pos 118 3 20 229
Desde datos agrupados en formato largo también se puede usar xtabs. Por ejemplo, una tabla de contigencia entre las variables protected y decline del data.frame plantas se obtiene como:
xtabs(frequency ~ protected + decline, data = plantas) decline
protected no yes
no 15 18
yes 32 8
En el comando xtabs, el orden de las variables ubicadas en la fórmula a la derecha de la virgulilla (~) equivale al orden en el cual se acomodarán las variables en las dimensiones del arreglo generado por xtabs. La 1era. variable en la fórmula se ubicará en al 1era. dimensión (filas), la 2da. variable en la fórmula quedará en la 2da. dimensión (columnas), y así sucesivamente.
Usando xtabs y as.data.frame
El comando as.data.frame convierte el objeto que entrega xtabs en un data.frame con los datos agrupados en formato largo:
f <- xtabs(frequency ~ protected + decline, data = plantas)
as.data.frame(f) protected decline Freq
1 no no 15
2 yes no 32
3 no yes 18
4 yes yes 8
Por defecto, el comando as.data.frame crea una columna llamada Freq con los conteos para cada combinación de categorías de las variables en la tabla de contigencia.
Agregando totales marginales
El comando addmargins recibe el objeto que entrega xtabs y adiciona totales en las margenes1 de la tabla:
f <- xtabs(~ region, data = carmona) # tabla de conteos de una variable
addmargins(f) # agregando totales en las margenesregion
aburra oriente Sum
415 412 827
f <- xtabs(~ rh + abo, data = carmona) # tabla de conteos de dos variables
addmargins(f) # agregando totales en las margenes abo
rh A AB B O Sum
neg 25 1 7 60 93
pos 236 10 54 434 734
Sum 261 11 61 494 827
Calculando proporciones
El comando prop.table recibe el objeto que entrega xtabs y convierte los conteos (frec. absolutas) en frecuencias relativas o proporciones:
f <- xtabs(~ rh + abo, data = carmona) # tabla de conteos de una variable
prop.table(f) # calculando proporciones (divide por el gran total) abo
rh A AB B O
neg 0.030229746 0.001209190 0.008464329 0.072551391
pos 0.285368803 0.012091898 0.065296252 0.524788392
Por defecto el comando prop.table divide por el gran total. Sin embargo le podemos indicar que genere las proporciones dividiendo por el total de filas:
prop.table(f, margin = 1) # proporciones por el total de fila abo
rh A AB B O
neg 0.26881720 0.01075269 0.07526882 0.64516129
pos 0.32152589 0.01362398 0.07356948 0.59128065
O que produzca las proporciones dividiendo por el total de columna2:
prop.table(f, margin = 2) # proporciones por el total de columna abo
rh A AB B O
neg 0.09578544 0.09090909 0.11475410 0.12145749
pos 0.90421456 0.90909091 0.88524590 0.87854251
De manera opcional, use el comando round para redonder las proporciones a la cantidad de decimales deseada y puede multiplicar por 100 el objeto que entrega prop.table para ver los resultados como porcentajes.
Usando ftable
El comando ftable produce tablas de contigencia planas desde un data.frame con datos crudos o desde objetos generados por el comando xtabs. En ftable también se utiliza la dupla de argumentos de formula y data, si embargo, la aplicación de la fórmula es diferente a xtabs. Considere el siguiente ejemplo:
# Tabla de contingencia plana desde datos crudos:
ftable(abo ~ region + rh, data = carmona) abo A AB B O
region rh
aburra neg 15 1 6 29
pos 118 7 34 205
oriente neg 10 0 1 31
pos 118 3 20 229
En la fórmula de ftable, las categorías de las variables ubicadas a la izquierda de la virgulilla (~) quedan en las columnas de la tabla de contingencia, mientras que las categorías de las variables a la derecha de la virgulilla (~) conformarán las filas de la tabla de contingencia.
El comando ftable también crea una tabla plana desde un objeto creado por xtabs, es decir, desde una tabla de múltiples dimensiones. Considere el siguiente ejemplo:
# Se guarda tabla de tres dimensiones
f <- xtabs(~ rh + abo + region, data = carmona)
# Se genera tabla de contingencia plana desde tabla de tres dimensiones
ftable(abo ~ region + rh, data = f) abo A AB B O
region rh
aburra neg 15 1 6 29
pos 118 7 34 205
oriente neg 10 0 1 31
pos 118 3 20 229
Resumen de comandos
La siguiente tabla presenta una lista de los comandos revisados arriba.
| Comando | Paquete | Requiere instalación | Descripción |
|---|---|---|---|
xtabs
|
stats | no |
Genera tablas de contingencia multidimensionales mediante la dupla de argumentos: formula y data. Requiere datos crudos o agrupados en formato largo. Entrega un objeto de clase table.
|
ftable
|
stats | no |
Genera tablas de contingencia planas mediante la dupla de argumentos: formula y data. Requiere datos crudos o agrupados en tablas multidimensionales. Entrega un objeto de clase ftable.
|
addmargins
|
stats | no | Agrega totales en las margenes de una tabla multidimensional |
as.data.frame
|
base | no | Convierte una tabla multidimensional en un data.frame con los datos agrupados en formato largo. |
prop.table
|
base | no | Cálcula proporciones desde una tabla multidimensional. Puede usar como denominador el gran total, o el total de alguna de las dimensiones de la tabla. |
Ejercicios
Escriba código R que le permita, a partir de los datos del data.frame
carmona:- generar una tabla de conteos de la
region(filas) contra elrh(columnas) - convertir la tabla generada en (a) en una de proporciones donde el divisor sea el total por
region.
- generar una tabla de conteos de la
Escriba código R que le permita, a partir del data.frame
plantas:- Crear una arreglo de conteos de tres dimensiones con las siguientes variables:
invaded,declineyprotecteden dicho orden. - Convertir el arreglo obtenido en (a) en datos agrupados en formato largo.
- Exportar los datos generados en (b) a un archivo separado por comas.
- Crear una arreglo de conteos de tres dimensiones con las siguientes variables:
El archivo artritisFa.csv contiene los datos de un ensayo clínico donde participaron un total de 84 sujetos. En el ensayo se comparó un tratamiento contra la artritis con un placebo. La respuesta o desenlace es una variable categórica ordinal llamada
Improved(“mejora”). Se cuenta, además, con la edad y el género. Los datos están agrupados en formato largo. Importe el archivo a R y realice las siguientes actividades:- Realiza una tabla de contingencia de dos dimensiones que compare las categorías de la respuesta entre los dos tratamientos. ¿Cuántos sujetos mostraron ninguna mejora y estuvieron en el grupo placebo?
- Use la tabla generada en (a) para estimar la probabilidad de observar cada una de las categorías de la respuesta condicionado al tratamiento. ¿Existe mayor probabilidad de presentar una mejora marcada si se es tratado que si no? Explique.
En un artículo reportan una tabla de contigencia que relaciona los tipos de transtornos del sueño y el género en 165 estudiantes de medicina. Vaya a la Tabla 2 del artículo y escriba código R que le permita digitar los datos en dicha tabla de manera agrupada en formato largo. Luego use el comando
xtabspara crear la tabla mostrada en el artículo.
Referencias
Para saber más
Filtrar antes de contar con xtabs
El comando xtabs tiene un tercer argumento, subset, que permite aplicar un filtro lógico a las filas del data.frame antes de realizar el conteo. Por ejemplo, considere el siguiente ejemplo:
# Tabla de conteos entre 'protected' y 'decline' para cada nivel de 'invaded'.
# Resulta una tabla de tres dimensiones:
xtabs(frequency ~ protected + decline + invaded, data = plantas), , invaded = no
decline
protected no yes
no 6 6
yes 20 3
, , invaded = yes
decline
protected no yes
no 9 12
yes 12 5
# Tabla de conteos entre 'protected' y 'decline' solo para el
# nivel 'yes' de 'invaded'. Resulta una tabla de dos dimensiones:
xtabs(frequency ~ protected + decline, data = plantas, subset = invaded == "yes") decline
protected no yes
no 9 12
yes 12 5
Desde datos agrupados a datos crudos
Para convertir datos agrupados en formato largo a datos crudos podemos usar el comando expand.dft o expand.table (paquete: vcdExtra). En el ejemplo siguiente pasamos la tabla plantas a un data.frame como datos crudos:
library(vcdExtra) # se carga la libreria (debe verificar que la tenga instalada)
plantas0 <- expand.dft(x = plantas, freq = "frequency") # se hace la conversion
str(plantas0) # se observa la estructura de la nueva tabla para verificar'data.frame': 73 obs. of 4 variables:
$ decline : Factor w/ 2 levels "no","yes": 2 2 2 2 2 2 2 2 2 2 ...
$ invaded : Factor w/ 2 levels "no","yes": 1 1 1 1 1 1 2 2 2 2 ...
$ protected: Factor w/ 2 levels "no","yes": 1 1 1 1 1 1 1 1 1 1 ...
$ light : int 0 0 2 2 3 3 0 3 3 3 ...
En el comando expand.dft, el argumento freq es una cadena de texto que indica el nombre de la columna que contiene la frecuencia de cada fila en la tabla. Observe que en la nueva tabla plantas0, cada fila representa un “sujeto,” en este caso, una población de plantas. ¿Porqué lo podemos saber?
El comando expand.dft también recibe una tabla de contingencia generada, por ejemplo, desde xtabs. Considere el siguiente ejemplo:
f <- xtabs(~ rh + abo + region, data = carmona) # tabla de contiengencia de tres dimensiones
carmona0 <- expand.dft(x = f) # se hace la conversion
str(carmona0) # se revisa la estructura de la nueva tabla'data.frame': 827 obs. of 3 variables:
$ rh : Factor w/ 2 levels "neg","pos": 1 1 1 1 1 1 1 1 1 1 ...
$ abo : Factor w/ 4 levels "A","AB","B","O": 1 1 1 1 1 1 1 1 1 1 ...
$ region: Factor w/ 2 levels "aburra","oriente": 1 1 1 1 1 1 1 1 1 1 ...
Entrando desde teclado una tabla de contingencia
Para digitar una tabla de contingencia directamente, entramos los conteos en una matriz o arreglo y asignamos nombres a cada categoría y variable usando el comando matrix (si hay dos variables) o el comando array (si hay dos o más variables).
Ejemplo: Considere el siguiente ejemplo presentado por Zar (2014)
Suponga que usted quiere digitar la tabla de contingencia propuesta. El siguiente código permite realizar la entrada:
# Entrando tabla de contingencia de 2 x 2 x 2 desde teclado:
zar8 <- array(
data = c(44,28,12,22, 38,20,10,18),
dim = c(2,2,2),
dimnames = list(
sp = c("sp1", "sp2"),
loc = c("loc1", "loc2"),
disease = c("present", "absent")
)
)
# Se asigna la clase `table` al arreglo creado
zar8 <- as.table(zar8)El último paso donde se actualiza la clase del arreglo creado es importante para garantizar que otros comandos que operan sobre objetos de clase table o xtabs pueden funcionar. Ahora imprimamos la tabla o arreglo para verificar la digitación:
addmargins(zar8, margin = 1:2), , disease = present
loc
sp loc1 loc2 Sum
sp1 44 12 56
sp2 28 22 50
Sum 72 34 106
, , disease = absent
loc
sp loc1 loc2 Sum
sp1 38 10 48
sp2 20 18 38
Sum 58 28 86
Reordenando o colapsando dimensiones
Considere la tabla (o arreglo) de contingencia zar8 de tres dimensiones, 2 x 2 x 2, creada en la sección anterior. Suponga que quisieramos colapsar la tabla zar8 para explorar sólo la relación entre la especie y la enfermedad. Para esto usamos el comando margin.table:
# Colapsando (sumando) categorias de la variable en la dimension 2
zar8.sp.d <- margin.table(x = zar8, margin = c(1,3))
zar8.sp.d disease
sp present absent
sp1 56 48
sp2 50 38
El argumento margin del comando margin.table permite indicar cuales dimensiones se quiere que permanezcan. Aquellas dimensiones ausentes en este argumento serán colapsadas (sumadas) y desaparecerán en el arreglo resultante.
Al argumento margin también le podemos indicar todas las dimensiones, pero en un nuevo orden. En este caso, el arreglo resultante tendrá el nuevo orden en sus dimensiones:
# Cambiando el orden de las dimensiones del arreglo original
# La variable en la dimension 3 se pasa a la dimension 2 y viceversa
zar8r <- margin.table(x = zar8, margin = c(1,3,2))
zar8r, , loc = loc1
disease
sp present absent
sp1 44 38
sp2 28 20
, , loc = loc2
disease
sp present absent
sp1 12 10
sp2 22 18
En estadística, a estos totales en las margenes se les llama la distribución marginal de la variable en cada margen (o dimensión) de la tabla↩︎
Cuando se divide por el total de fila o de columna estamos calculando una proporción condicional, es decir una proporción de la forma \(p(\text{A dado B}) = p(A | B)\)↩︎