Práctica 2

Autor/a

Objetivos de la práctica

Al finalizar esta sesión, el alumnado será capaz de:

  • Conocer y definir las variables de tipo factor.
  • Importar una base de datos con formato .rds localizada en una dirección url.
  • Explorar la estructura de la base de datos importada.
  • Detectar la presencia de valores faltantes.
  • Revisar la coherencia de la codificación de las variables de una base de datos.
  • Obtener diagramas de frecuencias apropiados para cada tipo de variable.
  • Generar diagramas de frecuencias comparativos.
  • Obtener las medidas más habituales de síntesis estadística.
  • Relacionar los perfiles gráficos con los valores de las medidas de síntesis.

Una vez abierto RStudio, crea un nuevo script para desarrollar esta práctica:

Recuerda que:

  • Para crear un nuevo script hay que usar las opciones del menú: File / New file / R script
  • Podemos hacer comentarios explicativos en nuestro script si anteponemos el carácter #, y que si escribimos cuatro guiones al final del comentario, este se incorpora como título en el índice.
  • Para aludir a una variable contenida en un data frame, hay que indicar el nombre del data frame, el símbolo $ y el nombre de la variable. Por ejemplo: datos$edad permite el manejo de la variable edad que debe de estar definida en un data frame llamado datos.

✓   Pon el siguiente título en la primera línea del script:

# Práctica 2 ----

Antes de seguir: vamos a establecer la carpeta de trabajo donde guardaremos todo lo que vayamos creando.

  1. Accede al menú Session/Set Working Directory/Choose Directory... (también puedes usar el atajo ‘Ctrl’ + ‘Mayús’ + ‘H’).

  2. Cuando se abra el cuadro de diálogo, selecciona la ubicación que quieras usar como carpeta de trabajo. A partir de ese momento, todos los archivos que generemos se guardarán allí.
    Puede ser una buena idea traer un pen‑drive y utilizarlo como carpeta de trabajo para conservar tu progreso.

  3. Guarda ahora el script que acabas de comenzar a crear mediante File/Save (o con el atajo ‘Ctrl’ + ‘S’).

  4. A lo largo de la práctica, recuerda guardar el script de vez en cuando (‘Ctrl’ + ‘S’) para actualizar la copia guardada.

R A través de este enlace, puedes acceder a una página con los cuadros resumen de funciones de R.


1 Variables no numéricas

En la práctica anterior aprendimos a crear vectores numéricos y a combinarlos para formar un data frame. Sin embargo, además de los vectores numéricos, existen otros tipos de vectores muy útiles en el análisis de datos.

1.1 Variables de clase “factor”

Un tipo de datos muy importante lo constituye la clase factor, diseñada específicamente para representar variables cualitativas o categóricas. Trabajar con este tipo de variables como factores, y no como números, evita errores de interpretación y garantiza que R las trate correctamente como categorías, no como valores cuantitativos.

A continuación se resumen las funciones de R que permiten manipular factores. Revísalas y pasaremos a hacer algunos ejemplos.

Función Propósito Tipo Ejemplo
Creación
factor() Crear un factor a partir de un vector x factor factor(x)
ordered() Crear un factor ordenado factor ordered(f, levels = c("Leve","Moderado","Intenso"))
Niveles
levels() Mostrar los niveles del factor vector levels(f)
levels<- Asignar o modificar los niveles del factor vector levels(f) <- c("Hombre","Mujer")
Conversión de clases
as.factor() Convertir un objeto en factor factor as.factor(x)
as.character() Convertir un factor a texto carácter as.character(f)
as.numeric() Convertir un factor a números (códigos internos) numérico as.numeric(f)

Se dispone de la información del sexo, del grupo sanguineo y de la edad de ocho pacientes (elegimos un número pequeño para que sea manejable) que asistieron a la consulta de enfermería:

Tabla 1: Pacientes recibidos en la Consulta de Enfermería.

Crea un data frame con esta información.

# creamos un vector para cada variable:
paciente <- 1:8 # <-- genera valores correlativos de 1 a 8
edad <- c(45, 65, 53, 72, 48, 56, 47, 60)

sexo <- factor(
  c(1, 1, 2, 1, 2, 2, 2, 1), # <-- identificamos las categorías por un número
  levels = c(1, 2), # <-- indicamos los niveles para las etiquetas
  labels = c("Hombre", "Mujer")
) # <-- asignamos una etiqueta a cada categoría de levels


# observa que los valores pueden proceder de un vector previamente definido
grupos <- c(4, 1, 2, 3, 4, 4, 1, 2) # <-- hemos numerado los grupos sanguineos para el orden A, B, AB, 0
gs <- factor(grupos, levels = 1:4, labels = c("A", "B", "AB", "0"))

# unimos todo en un data.frame
pacientes <- data.frame(paciente, sexo, gs, edad)

# veamos el data.frame
pacientes
  paciente   sexo gs edad
1        1 Hombre  0   45
2        2 Hombre  A   65
3        3  Mujer  B   53
4        4 Hombre AB   72
5        5  Mujer  0   48
6        6  Mujer  0   56
7        7  Mujer  A   47
8        8 Hombre  B   60

Observa los siguientes detalles en el código que hemos usado:

  • En el ejemplo, a factor() le hemos pasado información numérica. Esto suele resultar más rápido y cómodo que escribir “hombre” y “mujer” repetidamente, y evita problemas derivados de diferencias en mayúsculas y minúsculas (por ejemplo, “HOMBRE” y “hombre” serían categorías distintas para R).

  • El argumento labels permite asignar a cada valor numérico su categoría correspondiente. No obstante, si usamos únicamente labels, R asignará las etiquetas siguiendo el orden en que los valores aparecen en el vector original, lo que puede generar errores si los datos no están ordenados. Por eso es más seguro especificar también el argumento levels, de modo que la correspondencia entre valores numéricos y categorías quede claramente definida, independientemente del orden en el que aparezcan en los datos.

Ahora podemos hacer el siguiente manejo

# Consulta los niveles del factor
levels(pacientes$sexo)
[1] "Hombre" "Mujer" 
levels(pacientes$gs)
[1] "A"  "B"  "AB" "0" 
# Consultamos los datos relativos al paciente número 3
pacientes[3, ]
  paciente  sexo gs edad
3        3 Mujer  B   53
# Inclusión de un nuevo paciente
# Comenzamos creando un nuevo registro,
# en donde es necesario definir los factores con
# sus detalles
nuevo <- data.frame(
  paciente = 9,
  sexo = factor(2, levels = c(1, 2), labels = c("Hombre", "Mujer")),
  gs = factor(3, levels = 1:4, labels = c("A", "B", "AB", "0")),
  edad = 52
)

# unimos esta nueva fila al data.frame pacientes
# y actualizamos pacientes
pacientes <- rbind(pacientes, nuevo)

# vemos el resultado
pacientes
  paciente   sexo gs edad
1        1 Hombre  0   45
2        2 Hombre  A   65
3        3  Mujer  B   53
4        4 Hombre AB   72
5        5  Mujer  0   48
6        6  Mujer  0   56
7        7  Mujer  A   47
8        8 Hombre  B   60
9        9  Mujer AB   52

1.2 Factores ordinales

R permite definir un tipo especial de factores que conservan la relación de orden entre sus categorías. Estos factores se llaman ordered y es el tipo adecuado para manejar variables ordinales.

Supongamos que los 9 pacientes de nuestro data frame tienen un estado de gravedad codificado como “leve”/“moderado”/“grave”. Los datos actualizados son

Tabla 2: Pacientes recibidos en la Consulta de Enfermería.

Vamos a actualizar el data frame con esta información.

# asignamos la intensidad de la gravedad como 1=leve, 2=moderado y 3=grave
# al vector de datos
estado <- c(1, 1, 3, 2, 1, 2, 3, 1, 2)

# definimos el factor ordenado
gravedad <- ordered(
  estado,
  levels = c(1, 2, 3),
  labels = c("leve", "moderado", "grave")
)

# observemos que este vector tiene dos clases: ordered y factor
class(gravedad)
[1] "ordered" "factor" 
# añadimos esta columna al data.frame
pacientes <- cbind(pacientes, gravedad)

# comprobamos el resultado
pacientes
  paciente   sexo gs edad gravedad
1        1 Hombre  0   45     leve
2        2 Hombre  A   65     leve
3        3  Mujer  B   53    grave
4        4 Hombre AB   72 moderado
5        5  Mujer  0   48     leve
6        6  Mujer  0   56 moderado
7        7  Mujer  A   47    grave
8        8 Hombre  B   60     leve
9        9  Mujer AB   52 moderado

Si en algún momento necesitamos tratar un objeto de tipo factor —o de tipo ordered— como un vector numérico, podemos hacerlo utilizando la función as.numeric(). Al realizar esta conversión, cada categoría se sustituye por su código interno, es decir, por un número entero (1, 2, 3, …) que corresponde al orden de los niveles del factor. Este tipo de conversión resultará útil para poder aplicar algunos procedimientos estadísticos.

1.3 Otros tipos de variable

Los factores son útiles para representar variables categóricas con un número limitado y bien definido de niveles. Sin embargo, en muchos registros clínicos aparece información poco estandarizada. Por ejemplo, una variable que recoja el medicamento administrado en el ingreso puede contener muchísimos nombres distintos, con variaciones en dosis o en la forma de escribirlos. En estos casos no es práctico crear un factor con tantos niveles: dificulta el manejo, aumenta los errores y no aporta utilidad analítica.

Por ello, este tipo de información suele almacenarse directamente como texto, es decir, como variables de clase character. El cometido de este tipo de variables suele consistir en que sean usadas para filtrar, buscar o crear nuevas variables derivadas (por ejemplo, “es analgésico: sí/no”) de forma flexible.

Veamos un breve ejemplo:

# Asignamos el tipo de medicamento a un vector.

medicamento <- c(
  "Paracetamol 1g",
  "Ibuprofeno 600mg",
  "Omeprazol 20mg",
  "Furosemida 20mg",
  "Enoxaparina 40mg",
  "Paracetamol 1g",
  "Amoxicilina 875 mg",
  "Levetiracetam 500 mg",
  "Metformina 850 mg"
)

# Comprobamos que este vector es de clase 'character'
class(medicamento)
[1] "character"
# Como ejercicio, añade 'medicamento' al data.frame pacientes

Además de variables numéricas, factores, factores ordenados y texto, un data frame puede incluir otros tipos de datos frecuentes en análisis clínicos, como fechas (clase date), valores lógicos (clase logical, con valores TRUE/FALSE) o incluso variables complejas como listas (esto no lo veremos aquí). Estos tipos permiten representar información habitual en registros sanitarios, como fechas de ingreso y alta, indicadores binarios (por ejemplo, “fumador: sí/no”) o resultados estructurados. No vemos ahora ejemplos de esto -se manejan igual que las anteriores, ya nos encontraremos con este tipo de variables más adelante.

Guarda el data frame pacientes que hemos elaborado para que puedas conservarlo

Hay muchas opciones de formato (revisa el final de la práctica anterior). La más simple puede ser el formato csv (separa las variables con comas y usa el punto como separador decimal).

archivo <- "pacientes.csv" # <-- Añadimos '.csv' para identificar el formato del archivo
write.csv(pacientes, archivo) # <-- se guarda como fichero de texto plano delimitado por comas

Si todo ha ido bien, podrás encontrar este archivo en la carpeta de trabajo que has definido al inicio de la práctica (compruébalo)

2 Análisis de una base de datos clínicos

Ahora vamos a trabajar con una base de datos que incluye varios indicadores relacionados con el metabolismo lipídico. El objetivo es familiarizarnos con los pasos necesarios para analizar un conjunto de datos más complejo que los pequeños data frame que hemos construido hasta ahora. En la práctica profesional, este tipo de situaciones es muy frecuente: necesitamos explorar y depurar información procedente de bases de datos institucionales o generadas por otros miembros del equipo de investigación.

El primer paso -imprescindible en cualquier análisis- es comprobar la integridad y coherencia de los datos. Una vez verificada la calidad del conjunto de datos, podremos aplicar distintos procedimientos descriptivos para empezar a caracterizar las variables y comprender su comportamiento.

2.1 Lectura de la base de datos desde un sitio web

Vamos a abrir la base de datos llamada colesterol01.rds. Observa que la extensión .rds corresponde a archivos con formato R.

En el código que se presenta debajo, verás que:

  • La localización de la base de datos se asigna a un objeto que hemos llamado url_datos (este nombre es arbitrario).
  • Abrimos la base de datos utilizando la función readRDS() (la apropiada para archivos con formato ‘.rds’)
  • El argumento de esta función es la dirección web almacenada en url_datos, pero hay que indicarle que esto es, efectivamente, una dirección web. Para ello, se usa la función url().
  • Finalmente, asignamos el resultado de la lectura a un objeto que llamamos datos.

Copia el código a tu script y ejecútalo:

## Lectura del archivo .rds desde una URL ----

url_datos <- "https://www.ugr.es/~pfemia/BSRLab/dat/colesterol_01.rds"
datos <- readRDS(url(url_datos))

Si todo ha ido bien, en la ventana del entorno debe aparecer lo siguiente:

Figura 1: Estado del entorno una vez leído el archivo de datos.

2.2 Exploración inicial de la base de datos

Para empezar a trabajar con un conjunto de datos, es importante familiarizarse con su contenido. La primera inspección es muy importante. Permite identificar de un vistazo los nombres de las variables, el tipo de datos que contienen (numéricos, factores, fechas…), y el formato de codificación empleado (por ejemplo, “1/0”, “Sí/No”, “H/M”). Es un paso esencial para comprobar que la importación del archivo se ha realizado correctamente y que los datos están en el formato adecuado para el análisis.

En el marco mostrado en la Figura 1 haz click con el ratón en “datos” y se creará una pestaña en la ventana de scripts mostrando el contenido del archivo leído. Debe aparecer algo así:

Figura 2: Visualización de los datos leidos.


En la vista del data frame puedes ver que cada columna muestra en su cabecera el nombre de la variable —que será el que utilicemos en el código— y, justo debajo, una etiqueta (label) descriptiva que aclara su significado. Además, en la ventana de la Figura 1 aparece -a la derecha del nombre del objeto- el número de observaciones (el tamaño muestral) y el número de variables que contiene.

Esta revisión inicial también puede hacerse directamente desde el código, a menudo es más práctico hacerlo así. Para ello, R ofrece las siguientes funciones:

Para un objeto de tipo data frame llamado datos:

Función Propósito Tipo Ejemplo
head() muestra las primeras filas data.frame head(datos)
tail() muestra las últimas filas data.frame tail(datos)
View() abre el visor de datos de RStudio View(datos)

Si escribimos solo el nombre del data frame y ejecutamos esa línea de código, R intentará mostrar en la consola el contenido completo del conjunto de datos. Cuando el número de casos (filas) es elevado, esta salida resulta muy poco práctica. Es más útil limitar la visualización de los datos a unas pocas filas, ya sean de la parte inicial o de la parte final. Para esto usamos head() y tail(), respectivamente.

Usando código, obtén el listado de las primeras y de las últimas filas de datos.

# primeras filas:
head(datos) # por defecto, devuelve las 6 primeras filas
# Podemos obtener un número personalizado de filas, por ejemplo 8:
head(datos, 8)

# últimas 4 filas
tail(datos, 4)

Dimensiones del data frame. A continuación revisamos las funciones que permiten recuperar y manejar las dimensiones de la tabla de datos así como los nombres de sus filas y columnas.

Para un objeto de tipo data frame llamado datos:

Función Propósito Tipo Ejemplo
Dimensiones
dim() número de filas y columnas vector dim(datos)
nrow() número de filas numérico nrow(datos)
ncol() número de columnas numérico ncol(datos)
Nombres
names()
colnames()
nombres de las columnas vector names(datos)
colnames(datos)
rownames() nombres de filas vector rownames(datos)

Usando código, obtén las dimensiones del data frame datos

## Exploración del archivo de datos ----
### Dimensiones ----
dim(datos) # <-- observa que da un vector con el número de filas y de columnas
nrow(datos) # <-- devuelve solo el número de filas
ncol(datos) # <-- devuelve solo el número de columnas

# observa que
dim(datos)[1]
# es equivalente a nrow(datos), y
dim(datos)[2]
# es equivalente a ncol(datos)
# IMPORTANTE: ¿entiendes el uso de los corchetes?

Usando código, obtén el listado con los nombres de las columnas de datos.

# podemos usar cualquiera de estas dos funciones:

names(datos)
colnames(datos)

2.3 Identificación de la clase de las variables

Como ya sabemos, un data frame es una estructura de datos formada por columnas heterogéneas: cada columna es un vector cuya clase puede diferir de la de las demás. Para identificar la clase de cada columna debemos utilizar la función class().

Usando código, comprueba qué la clase del objeto datos es efectivamente data.frame, e identifica las clases de las variables HDL (HDL), grupo sanguíneo (gs) y nivel de actividad física (nivel_AF) contenidas en él.

2.4 Detección de la presencia de valores faltantes

En cualquier base de datos es muy habitual encontrarse con información faltante. Esto ocurre cuando, por diferentes motivos, algunos casos no disponen de un valor registrado en alguna de las variables del data frame.

En estos casos, R no deja un espacio en blanco, sino que utiliza el código NA, que significa Not Available (no disponible). Este código es la forma estándar de indicar que falta información.

Pero, ¿por qué sucede esto tan a menudo? En el ámbito de las ciencias de la salud y la enfermería es completamente normal que algunos datos no puedan recogerse. Por ejemplo: una medición no pudo realizarse, una pregunta del cuestionario quedó sin responder, hubo un fallo en un dispositivo de monitorización o, simplemente, se produjo un error al introducir los datos en el ordenador.

Estos son procesos cotidianos en la práctica clínica, por lo que es fundamental aprender a identificar y manejar correctamente estos valores faltantes para evitar problemas posteriores.

En esta práctica vamos a centrarnos en cómo detectar la presencia de datos faltantes en un conjunto de datos. Más adelante, en la práctica 3, veremos cómo trabajar con ellos y a decidir qué hacer según la situación.

A continuación se muestran las principales funciones de R que permiten comprobar si existen valores faltantes en nuestras variables y cuántos hay en cada caso.

Para un objeto de tipo data frame llamado datos:

Función Propósito Tipo Ejemplo
anyNA() ¿Hay algún NA en el data frame lógico anyNA(datos)
is.na() Consulta, devuelve verdadero/falso si el argumento es NA vector lógico is.na(c(1,2,NA,4))
colSums(is.na())() Número de NA por columnas vector colSums(is.na(datos))

Averigua si existen valores faltantes en nuestro data frame datos

anyNA(datos) # indica si hay algún valor faltante en cualquiera de las variables

Ante un resultado positivo en la consulta anterior, debemos identificar en qué variable(s) hay falta de información.

Averigua en qué variables aparecen valores faltantes.

colSums(is.na(datos))

2.5 Exploración inicial de las variables

Hasta ahora hemos explorado las dimensiones y la estructura del data frame. El siguiente paso consiste en comprobar la integridad de sus variables. Esto implica, por un lado, conocer cómo están codificadas y, por otro, asegurarnos de que no existen errores lo suficientemente grandes como para detectarse de forma inmediata.

En el caso de los factores, es esencial revisar que las categorías sean coherentes y no existan variaciones inesperadas debidas a errores de codificación (por ejemplo, diferencias en mayúsculas y minúsculas).

Por otra parte, cuando trabajamos con variables cuantitativas, una forma rápida de identificar errores graves es examinar sus medidas de posición, especialmente los valores mínimos y máximos. Por ejemplo, un fallo al registrar la edad podría hacer que, en lugar de introducir “20”, se haya registrado “200”. Detectar y corregir estos valores anómalos desde el principio es fundamental: si persisten en la base de datos, cualquier análisis posterior quedará invalidado. Revisar que los valores extremos de cada variable sean razonables y acordes con la realidad es, por tanto, un paso imprescindible en la fase inicial de depuración.

La función que permite hacer esta revisión sobre todas las variables del data frame es summary().

Aplica la función summary() a los datos y comprueba

  • La diferencia en el formato del resumen de cada variable dependiendo de su tipo.
  • Que los recorridos (valores extremos) de las variables cuantitativas son coherentes.
  • La constatación de la presencia de valores faltantes allí donde ya los habíamos detectado.
summary(datos)

Para poder seguir, damos por válida la integridad de todas las variables. Más adelante veremos cómo tratar los valores claramente erróneos cuando los detectemos.

Ahora vamos a sacar algunas conclusiones sobre la distribución de los datos a partir del resumen obtenido.

A la vista de los resultados dados por summary(), responde a las siguientes cuestiones:

Es recomendable que anotes este tipo de consideraciones en tus apuntes

  1. ¿Cuál es la categoría modal en cada una de las variables no numéricas?
  2. ¿Cuánto pesan, como máximo, el 25% de los sujetos con menor peso?
  3. ¿Entre qué valores está el 50% central de la distribución del peso?
  4. Observa los valores que toman los tres cuartiles de la edad ¿Qué conclusión puedes sacar sobre los valores de esta variable?
  5. Observa los valores que toman la media y la mediana de la variable talla. ¿Crees que su distribución podría ser simétrica? Razona tu respuesta o explica, si lo consideras necesario, por qué no es posible determinarlo sin ver un diagrama de frecuencias.
  6. Haz lo mismo con la variable METs (esta variable representa el gasto energético en equivalentes metabólicos). Observa las medidas del resumen ¿qué puedes concluir acerca de su distribución?

3 Diagramas de frecuencias

Veamos ahora cómo resumir la información mediante diagramas de frecuencias. Estas representaciones ofrecen una forma intuitiva y visual de comprender cómo se distribuyen los datos.

IMPORTANTE: Las representaciones gráficas admiten muchísimas variaciones y, por ello, las funciones de R incluyen numerosos argumentos para personalizarlas. El objetivo de esta práctica no es que memorices todas las opciones, sino que te familiarices con el lenguaje gráfico de R y entiendas cómo construir y adaptar gráficos básicos. Lo que realmente importa es que comprendas qué tratamiento requieren los datos para poder representarlos y, sobre todo, cómo interpretar adecuadamente las figuras obtenidas. Así, más adelante podrás ajustar y ampliar estas herramientas según las necesidades de tus propios datos o investigaciones.

También es importante que sepas cómo puedes obtener información sobre cualquier función de R y los argumentos que usa. Para ello, escribe el nombre de la función en el script y pulsa la tecla “F1”, o bien escribe el nombre de la función (en el script o en la consola) precedido por el signo de interrogación invertida; por ejemplo ?round proporciona ayuda sobre la función que ya hemos utilizado anteriormente para redondear resultados.

Obtén la pantalla de ayuda para la función pie(), que permite representar diagramas de sectores.

Resumimos a continuación las funciones básicas para representar diagramas de frecuencias. Revisa el listado y comenzaremos a practicar con ellas.

Para un objeto de tipo data frame llamado datos que presenta las variables aludidas en los ejemplos:

Función Propósito Particularidades Ejemplo
Vars. cualitativas
plot() Gráfico genérico que puede producir diferentes tipos de diagramas Con variables cualitativas genera directamente un diagrama de barras plot(datos$sexo)
barplot() Diagrama de barras a partir de frecuencias usar sobre table(variable) barplot(table(datos$sexo))
pie() Gráfico de sectores usar sobre table(variable) pie(table(datos$sexo))
Vars. cuantitativas
hist() Histograma de una variable numérica hist(datos$peso)
density()
(solo cálculo)
Calcula la densidad para representarla con plot() o para incorporarla con lines() a un diagrama preexistente no es un objeto gráfico plot(density(datos$peso))

hist(datos$peso,freq=FALSE)
lines(density)
boxplot() Diagrama de caja boxplot(datos$trigliceridos)

Como ya se ha indicado, todas las funciones gráficas permiten añadir argumentos para personalizar la representación, como el título (main = "..."), el color (col = "..."), o los límites de los ejes (xlim, ylim). A continuación se muestra un resumen de los argumentos gráficos más utilizados.

En general, las funciones gráficas soportan alguno o todos los argumentos que se indican a continuación:

Argumento Propósito Ejemplo
Títulos
main Título principal del gráfico main = "Histograma de HDL"
sub Subtítulo del gráfico sub = "Datos del estudio"
xlab Etiqueta del eje X xlab = "HDL (mg/dL)"
ylab Etiqueta del eje Y ylab = "Frecuencia"
cex.main Tamaño del título principal cex.main = 1.2
cex.lab Tamaño de las etiquetas de los ejes cex.lab = 1.1
cex.axis Tamaño de los valores en los ejes cex.axis = 0.9
Apariencia
col Color de las barras o elementos gráficos\(^{*}\) col = "lightblue"
border Color del borde de las barras border = "white"
Ejes
xlim Rango del eje X (mínimo y máximo) xlim = c(20, 120)
ylim Rango del eje Y ylim = c(0, 50)
freq Frecuencia absoluta (TRUE) o densidad (FALSE) freq = FALSE
breaks Número o posiciones de los cortes del histograma breaks = 20
las Orientación del texto de los ejes las = 1
Líneas
lwd Grosor de líneas lwd = 2
lty Tipo de línea lty = 2

\(^{*}\) La función colors() proporciona un listado de nombres válidos de color.
Escribiendo demo("colors") se pueden ver las paletas en la ventana gráfica.

Los colores se pueden establecer mediante códigos numéricos o -en muchos casos- usando su nombre. Por ejemplo col="red" establece el color rojo. Podemos usar nombres como "blue", "green","grey", etc. Como se indica en el resumen anterior, puedes ver la lista de nombres de color escribiendo colors() (esta función no necesita argumentos, pero hay que escribir el paréntesis).

3.1 Diagramas de barras

Son los diagramas más apropiados para variables cualitativas, o discretas con pocos valores distintos.

Hay dos funciones que permiten generar diagramas de barras:

  1. plot(). Es una función genérica, muy versátil, que permite obtener muchos tipos de diagramas. Cuando se indica una variable de tipo factor, representa directamente un diagrama de barras.
  2. barplot(). Esta función no opera sobre la variable directamente, sino que lo hace a través de la tabla de frecuencias que se obtiene con table(), de manera que la sintaxis es
barplot(table(variable))

Como ha surgido en el ejemplo, podemos aprovechar para indicar que table() es una función que devuelve la distribución de frecuencias de un objeto de clase factor.

En lo que sigue, para simplificar la explicación, nos referiremos principalmente a la clase factor como contrapunto a las variables de tipo numérico. Sin embargo, muchas de las consideraciones —incluidas las funciones como table()— también son válidas para otros tipos de datos categóricos, como ordered, logical o incluso character (siempre que se utilice con un número razonable de categorías).

Representa la distribución de frecuencias del grupo sanguíneo (variable gs).

# observemos que estas dos líneas generan el mismo diagrama:

plot(datos$gs)

barplot(table(datos$gs))

# podemos cambiar el color de las barras y personalizar los títulos
barplot(
  table(datos$gs),
  col = "lightblue",
  main = "Distribución del grupo sanguíneo",
  xlab = "Grupo",
  ylab = "Frecuencia"
)

3.2 Representación de histogramas

Cuando la variable es cuantitativa continua —o discreta con un rango muy amplio de valores—, su distribución se representa de forma clásica mediante un histograma. Para ello se utiliza la función hist().

Representa, mediante un histograma, la distribución de la variable peso.

hist(datos$peso)

Añade un título principal y títulos personalizados a los ejes. Haz que el histograma tenga el color "LightSteelBlue".

# Observa que, para ganar claridad, utilizamos varias líneas para un solo comando
# No olvides la coma que indica la separación entre los distintos argumentos

hist(
  datos$peso,
  main = "Distribución del peso (n=150)",
  xlab = "Peso (kg)",
  ylab = "Frecuencia",
  col = "LightSteelBlue"
)

La representación mediante histograma puede hacerse usando frecuencias absolutas o relativas (densidades). Por defecto, hist() muestra la frecuencia absoluta, pero podemos obtener la densidad simplemente añadiendo el argumento freq = FALSE.

Por otra parte, también podemos modificar el número de clases mediante el argumento breaks. Veamos un ejemplo

Representa el histograma anterior con la densidad y haciendo 20 intervalos de clase.

hist(
  datos$peso,
  main = "Distribución del peso (n=150)",
  xlab = "Peso (kg)",
  ylab = "Densidad",
  col = "LightSteelBlue",
  breaks = 20,
  freq = FALSE
)

Un recurso gráfico muy útil para representar la distribución de variables continuas es la curva de densidad estimada (KDE). Puede mostrarse de forma independiente o superponerse al histograma. Para esta segunda opción, es imprescindible que el histograma esté expresado en escala de densidad y no en frecuencias absolutas; solo así ambas representaciones serán comparables y coherentes entre sí.

Representa el histograma anterior con la curva KDE.

hist(
  datos$peso,
  main = "Distribución del peso (n=150)",
  xlab = "Peso (kg)",
  ylab = "Densidad",
  col = "LightSteelBlue",
  breaks = 20,
  freq = FALSE
)

densidad <- density(datos$peso) # <-- obtenemos los valores de la curva
# Dibujamos la curva sobre el diagrama anterior generado con hist()
# utilizando la función lines().
lines(densidad, col = "brown", lwd = 2) # <-- lwd = 2 hace que la línea tenga más grosor

Observa en el código anterior que lines() dibuja una curva añadiéndola a un gráfico que ya existe, en este caso el creado por hist(). Por sí sola, lines() no genera un gráfico, solo incorpora un motivo grafico -la curva KDE en este caso- a una representación previa.

3.3 Representación de diagramas de caja

Este tipo de diagrama es apropiado para representar variables de tipo cuantitativo. Es de especial interés para comparar grupos de forma robusta y para detectar valores extremos (outliers). La función para representar un diagrama de este tipo es boxplot().

Representa un diagrama de caja para la variable colesterol

boxplot(
  datos$colesterol,
  col = "lightblue",
  main = "Concentración de colesterol (n=150)",
  xlab = "",
  ylab = "Colesterol (mg/dL)"
)

Observa que en este diagrama la caja representa el recorrido del 50 % central de la distribución. Las líneas superior e inferior, conocidas como bigotes, abarcan aproximadamente el 25 % de cada uno de los extremos y muestran el rango que puede considerarse dentro del patrón esperable de los datos. Las observaciones que se alejan notablemente del resto (outliers) se representan como puntos individuales.
Dado que los cuartiles son medidas de posición robustas ante valores extremos, este tipo de diagrama resulta especialmente útil para detectar estos posibles valores atípicos.

4 Diagramas comparativos

Veamos ahora la forma de comparar dos distribuciones. Bien porque aparece una variable de agrupación, o bien porque se trate de dos variables distintas.

4.1 Diagramas de barras comparativos

Compara la distribución de los grupos sanguíneos en función del sexo.

Los pasos son:

  1. Obtener las frecuencias cruzadas de las dos variables, table(variable_1,variable_2), y asignarlas a un objeto.
  2. Se puede definir un vector con colores personalizados, o dejar los que usa el sistema por defecto.
  3. Utilizar barplot() con el objeto definido a traves de table(). barplot() contempla un argumento beside que puede tomar valores TRUE/FALSE; en el primer caso representa las barras solapadas y en el segundo apiladas.
  4. Finalmente, a barplot() se le pueden añadir argumentos de personalización como son los títulos.
# 1.  Obtenemos la tabla de frecuencias (el orden en que se pongan las variables condiciona el gráfico a obtener)
tab <- table(datos$gs, datos$sexo)
tab # <-- es interesante que veas el resultado

# 2.  Definimos una paleta de colores de nuestro gusto, en este caso cuatro tonos de azul,
# uno por cada grupo sanguíneo:
cols <- c("#B0C4DE", "#5F9EA0", "#4682B4", "#27408B")

# 3. y 4. Generamos el diagrama
barplot(
  tab, # <-- Indica las frecuencias
  beside = TRUE, # <-- barras solapadas, si es FALSE se representan apiladas
  col = cols, # <-- paleta de colores
  main = "Grupo sanguíneo por sexo (frecuencias)",
  xlab = "Sexo",
  ylab = "Frecuencia",
  legend.text = TRUE, # <-- añade una leyenda
  args.legend = list(
    # <-- establece opciones de la leyenda
    x = "topleft", # <-- posición, podemos poner "topright", "bottomleft",...
    bty = "n", # <-- no dibuja un marco para la leyenda
    title = "Grupo"
  ) # <-- título de la leyenda
)


Modifica el diagrama anterior para obtener uno de barras apiladas

haz una copia del código y solo tienes que cambiar el valor del argumento beside

4.2 Comparación de la distribución de variables cuantitativas

Cuando queremos comparar la distribución de variables cuantitativas, el histograma no suele ser el recurso más adecuado: al superponer el histograma de un grupo sobre el del otro, las barras tienden a ocultarse entre sí y resulta difícil interpretar la comparación. En cambio, las curvas de densidad estimada (KDE) ofrecen una alternativa mucho más útil, ya que permiten visualizar de forma limpia y simultánea la forma de ambas distribuciones.

Compara la distribución de las lipoproteinas de alta y baja densidad, HDL y LDL, respectivamente.

Los pasos son:

  1. Obtener las densidades de ambas variables
  2. Dibujar con plot() una de estas densidades (aprovechamos para personalizar el gráfico aquí)
  3. Añadir la otra curva con lines() (usando otro color)
  4. Podemos añadir finalmente una leyenda que indique qué curva representa a cada variable

Si quisiéramos añadir más curvas, solo hay que repetir el paso 3 y ampliar la leyenda.

# 1. Calculamos las densidades
dens_hdl <- density(datos$HDL)
dens_ldl <- density(datos$LDL)

# 2. generamos un diagrama con una de las curvas e incluimos aquí los
# argumentos gráficos de títulos principal y de los ejes
plot(
  dens_hdl,
  col = "blue",
  lwd = 2, # <-- atributos de color y grosor de línea
  main = "Concentración de lipoproteinas de alta y baja densidad (n=150)",
  xlab = "HDL y LDL (mg/dL)",
  ylab = "Densidad"
)

# 3. Añadimos la segunda curva al diagrama anterior
lines(
  dens_ldl,
  col = "brown",
  lwd = 2 # <-- atributos de color y grosor de línea
)

# 4. añadimos una leyenda para identificar las curvas
legend(
  "topright", # <-- posición de la leyenda
  legend = c("HDL", "LDL"), # <-- vector con las etiquetas
  col = c("blue", "brown"), # <-- vector con los colores
  lwd = 2, # <-- línea del mismo grosor
  bty = "n" # <-- sin marco
)

Observa que la curva LDL se corta. Convendría adaptar la métrica del eje para que se observen bien las dos. Vamos a ello:

Adapta el recorrido del eje de abscisas para que sea posible representar los valores de LDL y HDL por completo

La función plot() admite un argumento que permite establecer el recorrido de cada uno de los ejes: xlim para el eje de abscisas y ylim para el de ordenadas. Nosotros solo tenemos que modificar el primero.

A xlim se le proporcionan dos valores: el mínimo (inicio del eje) y el máximo (final del eje). En este caso, el inicio debería corresponder al valor mínimo observado en HDL y LDL, mientras que el final debería fijarse en el mayor de ambos. Si se desea, puede añadirse un pequeño margen adicional para mejorar la visualización.

# obtención de los extremos del eje x
holgura <- 5 # <-- es un valor arbitrario, elegido a la vista del summary de las variables
x_min <- min(datos$HDL, datos$LDL) - holgura
x_max <- max(datos$HDL, datos$LDL) + holgura

# no necesitamos calcular las densidades, ya las tenemos de antes

# copiamos y pegamos el código anterior, añadiendo el argumento xlim a plot()
plot(
  dens_hdl,
  col = "blue",
  lwd = 2, # <-- atributos de color y grosor de línea
  main = "Concentración de lipoproteinas de alta y baja densidad (n=150)",
  xlab = "HDL y LDL (mg/dL)",
  ylab = "Densidad",
  xlim = c(x_min, x_max)
)

# Ahora añadimos, como antes, la segunda curva al diagrama anterior
lines(
  dens_ldl,
  col = "brown",
  lwd = 2 # <-- atributos de color y grosor de línea
)

# Finalmente añadimos la leyenda para identificar las curvas
legend(
  "topright", # <-- posición de la leyenda
  legend = c("HDL", "LDL"), # <-- vector con las etiquetas
  col = c("blue", "brown"), # <-- vector con los colores
  lwd = 2, # <-- línea del mismo grosor
  bty = "n" # <-- sin marco
)

Ahora tenemos un diagrama mucho más adecuado.

A la vista de las distribuciones de las lipoproteinas de alta y baja densidad (HDL y LDL), responde a las siguientes cuestiones

¡toma notas de esto en tus apuntes!

  1. Discute la simetría y la variabilidad de ambas distribuciones.
  2. Discute la posición relativa que deben tener la media aritmética y la mediana en cada una de las distribuciones (no se trata de calcularlas, lo que se pide es que a la vista de las distribuciones, intuyas en qué parte de cada distribución deben localizarse ambas medidas; si alguna debe ser mayor que la otra)
  3. Comparativamente, ¿cómo crees que deben ser las desviaciones típicas de ambas distribuciones? ¿Crees que son comparables?
  4. Si la calculáramos ¿cuánto debería valer el área bajo cada curva? ¿por qué?

4.3 Diagramas de caja comparativos

Vamos a conocer una nueva clase de objeto de R; se trata de formula.

En R, una fórmula es una forma compacta y expresiva de describir relaciones entre variables. Se utiliza para indicar que una variable se explica en función de otra —la primera actúa como variable respuesta y la segunda como variable explicativa— y constituye la base de la mayoría de las funciones estadísticas y gráficas del lenguaje.

La fórmula más simple tiene la expresión:

variable_respuesta   ~   variable_explicativa


El símbolo “~” es el equivalente al “=” en una fórmula matemática. En R, el sígno “=” tiene otros usos, así que las fórmulas se definen con “~”. Para ganar intuición, lee este símbolo como ‘…es función de…’. Por ejemplo:

datos$colesterol ~ datos$sexo


lo leeríamos como “la variable colesterol es función de la variable sexo”. Vamos a usar esta nueva clase de objeto para definir los diagramas de caja comparativos.

Obtén un diagrama de cajas con la distribución del nivel de colesterol en función del sexo del paciente.

boxplot(datos$colesterol ~ datos$sexo)

Obtén un diagrama de cajas con la distribución del nivel de colesterol en función del grupo sanguíneo del paciente.
Asigna al diagrama el color “LightSteelBlue” A la vista del diagrama obtenido, responde a las siguientes cuestiones:

¡toma notas de esto en tus apuntes!

  1. Discute si se pueden considerar homogéneos los niveles de colesterol en los cuatro grupos sanguíneos.
  2. ¿Detectas valores extremos en alguna distribución?
  3. ¿Crees que es correcta la siguiente afirmación?:

El 50 % de los pacientes del grupo sanguíneo AB con los valores de colesterol más altos presenta niveles de colesterol superiores al nivel que alcanza el 75 % de los pacientes del grupo B con los valores más bajos.

  1. ¿Crees que son simétricas las distribuciones?

5 Cálculo de medidas descriptivas

A continuación vemos cómo resumir las variables mediante sus principales medidas de síntesis.

Como sabemos, la mejor forma de describir una variable categórica es presentar las frecuencias absolutas y relativas de cada una de sus categorías. Cuando además existe una relación de orden o la variable tiene una identidad numérica, es posible calcular medidas de posición y dispersión que permiten resumir mejor su distribución.

A continuación se muestra un resumen de las funciones que permiten obtener las medidas descriptivas. Revísalas y, después, veremos algunos ejemplos prácticos.

Para un objeto de tipo data frame llamado datos:

Función Propósito Tipo Ejemplo
Frecuencias
table() frecuencias absolutas tabla table(datos$sexo)
prop.table() frecuencias relativas (proporciones). Debe tener como argumento anidado a la función table() tabla prop.table(table(datos$sexo))
Posición
mean() media aritmética numérico mean(datos$edad)
median() mediana (percentil 50) numérico median(datos$edad)
min() valor mínimo numérico min(datos$edad)
max() valor máximo numérico max(datos$edad)
quantile() cuantiles (incluye cuartiles) numérico/vector quantile(datos$edad)
summary() resumen mixto: min, Q1, mediana, media, Q3, max mixto summary(datos$trigliceridos)
Dispersión
range() mínimo y máximo (proporciona los dos valores, no la diferencia max-min) vector numérico range(datos$edad)
sd() desviación estándar numérico sd(datos$colesterol)
var() varianza numérico var(datos$colesterol)
IQR() rango intercuartílico (Q3 - Q1) numérico IQR(datos$edad)

Obtén la distribución de frecuencias del sexo del paciente

  • las absolutas
  • y las relativas (redondeadas a tres decimales)
table(datos$sexo)
prop.table(table(datos$sexo))

Determina las siguientes medidas descriptivas del nivel de colesterol:

  • Media
  • Desviación típica
  • Percentiles 5 y 95
  • Mediana
  • Rango intercuartílico
  • Se define el rango o amplitud como la diferencia entre los valores máximo y mínimo de la variable. Obtén esta medida.
  • En R (base), no existe una función que calcule el coeficiente de variación (CV). Este coeficiente es el cociente entre la desviación típica y la media, siempre que la media sea superior a 1. Obtén el CV del nivel de colesterol.
# media y desviación típica
mean(datos$colesterol)
sd(datos$colesterol)

# percentiles
quantile(datos$colesterol, 0.05)
quantile(datos$colesterol, 0.95)

# mediana y rango intercuartílico
quantile(datos$colesterol, 0.5) # también serviría median(datos$colesterol)
IQR(datos$colesterol)

# rango o amplitud
max(datos$colesterol) - min(datos$colesterol)

# coeficiente de variación
sd(datos$colesterol) / mean(datos$colesterol)
None Caso clínico: Índices aterogénicos
El índice aterogénico I (Castelli I) es un indicador de riesgo cardiovascular que se obtiene relacionando el nivel de colesterol total con el de lipoproteinas de alta densidad, las HDL, según la expresión:

\[ IA_1=\frac{\text{colesterol}}{\text{HDL}}. \]

Por otra parte, el índice aterogénico II (Castelli II) es un indicador más específico para el evaluar el riesgo aterosclerótico asociado a los niveles de lipoproteinas de baja densidad, las LDL. Este nuevo índice se define como el cociente

\[ IA_2=\frac{LDL}{HDL}. \]

Realiza las actividades propuestas a continuación y discute los planteamientos de los enunciados.

¡toma notas de esto en tus apuntes!

  1. Crea una variable con el índice aterogénico I (llámala IA1) e incorpórala a la base de datos datos
  2. Determina la media y la desviación típica del \(IA_1\).
  3. Obtén un histograma y la curva de densidad KDE para este índice.
  4. ¿Crees que es simétrica la distribución?
  5. Una medida de asimetría simple es la que compara la media con la mediana y estandariza la diferencia con la desviación típica:

    \[ As=\frac{\bar{x}-\text{mediana}}{s} \]

    Obtén esta medida para el índice aterogénico I. Si \(As=0\), la distribución es simétrica (la media y la mediana coinciden) ¿Cómo es en este caso? ¿Cómo interpretas el signo de este coeficiente?
  6. Si consideramos como grupo de riesgo al 20% de los pacientes con el \(IA_1\) más alto, ¿a partir de qué valor de este índice se considera que un sujeto pertenece al grupo de riesgo?
  7. Compara las distribuciones por sexos del \(IA_1\) mediante diagramas de caja ¿es correcto deducir que el 50% de los hombres tienen valores más altos del índice aterogénico I que el 75% de las mujeres?
  8. Obtén el índice aterogénico II (llámalo IA2) e incorpóralo a la base de datos.
  9. Obtén la media y desviación típica de los dos índices, IA1 e IA2. ¿Cuál de los dos crees que presenta mayor variabilidad?
  10. Calcula el coeficiente de variación de los dos índices. Ahora, ¿cuál de los dos crees que presenta mayor variabilidad?
  11. Justifica si las respuestas que has dado en las dos cuestiones anteriores coinciden y, en caso negativo, di cuál es la razón. ¿Qué conclusión sacas?
  12. Nos interesa comparar la forma de la distribución de los dos índices \(IA_1\) y \(IA_2\), pero como tienen métricas distintas es necesario estandarizarlos. La estandarización que vamos a hacer consiste en restar a cada índice su media y dividir el resultado por su desviación típica (a esto se le llama tipificación). Vamos a llamar ZIA1 y ZIA2 a estos índices estandarizados, que se obtienen según las expresiones:
    \[ Z_{IA_1}=\frac{IA_1-\overline{IA_1}}{s_{IA_1}}, \qquad Z_{IA_2}=\frac{IA_2-\overline{IA_2}}{s_{IA_2}} \]
    Estandariza los dos índices y compara sus densidades mediante curvas KDE (las dos en el mismo diagrama, asigna un color diferente a cada una para diferenciarlas)

Hemos terminado con esta práctica. Probablemente, quieras guardar el script que has generado.


— o0o —