Práctica 1

Autor/a

Objetivos de la práctica

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

  • Conocer lo más importante del entorno RStudio.
  • Ejecutar operaciones básicas en R.
  • Crear objetos y vectores.
  • Crear un data.frame con datos de interés en Enfermería.
  • Crear nuevas variables mediante cálculos.
  • Unir archivos de datos.
  • Modificar las filas y columnas de un data.frame.
  • Guardar y recuperar datos en un archivo externo (.csv o .txt).

Las primeras prácticas son de aprendizaje autónomo. Debes seguir el guión e ir haciendo las tareas propuestas. Si tienes dudas, avisa al/la profesor/a.


Toma de contacto con RStudio

Identifica las siguientes áreas del entorno:

  1. Script (arriba izquierda): donde escribiremos el código. Si al iniciar RStudio por primera vez no aparece este marco, no te preocupes, es porque no tenemos ningún script abierto. Más adelante se abrirá, ahora la consola estará ocupando todo el marco izquierdo.
  2. Consola (Console, abajo izquierda): donde se ejecuta el código.
  3. Entorno (Environment, arriba derecha): donde se almacenan los objetos.
  4. Files / Plots / Packages / Help (abajo derecha).
Figura 1: Pantalla de RStudio

Continúa leyendo este guion. Debes ir haciendo todas las tareas propuestas (marcadas con \(\checkmark\)). No tienes que entregar nada, pero quizá quieras tomar tus propias notas.


1 Calculando con la consola de R

El objetivo de este ejercicio es familiarizarse con las operaciones aritméticas básicas en el entorno R y RStudio.

Limpieza de la consola. Para comenzar, podemos eliminar los mensajes iniciales de la consola. Para ello, basta con pulsar el icono de la brocha situado en la esquina superior derecha de la ventana (todas las ventanas de RStudio disponen de su propia brocha). También podemos utilizar la combinación de teclas Ctrl + L.

Una vez limpiada la consola, aparecerá únicamente el símbolo >, denominado prompt. Este símbolo indica que R está preparado y a la espera de que escribamos una instrucción.

1.1 Operaciones aritméticas

En la consola, escribe las siguientes operaciones y pulsa ENTER después de cada una de ellas. Fíjate en los símbolos que actúan como operadores aritméticos. Puedes usar las flechas arriba y abajo para desplazarte por el historial de comandos de la consola.

4 + 2
4 - 2
4 * 2
4 / 2
4^2

1.2 Uso del paréntesis en las operaciones

Escribe y ejecuta cada una de las siguientes operaciones en la consola. Observa la diferencia entre los dos resultados, explica qué ocurre:

2 * 2 + 2
2 * (2 + 2)

Calcula las siguientes expresiones:

\[ \frac{3^2+4^2}{5} \tag{1}\] \[ 3^2+\frac{4^2}{5} \tag{2}\]

¿Dan el mismo resultado? Explica la diferencia entre los dos comandos que has escrito para realizar ambos cálculos.

(3^2+4^2)/5 
[1] 5
3^2+4^2/5
[1] 12.2

2 Creación de un script

En R, un script es un guión de código. Se trata de documentos (con formato de texto) en donde se puede escribir y ejecutar código.

Estas son algunas de las ventajas del uso de scripts respecto al manejo directo de la consola:

  • Reproducibilidad: el análisis puede repetirse exactamente, sin depender del historial de la consola.
  • Documentación: los scripts permiten añadir comentarios que explican cada paso del análisis.
  • Organización del trabajo: el código queda estructurado en bloques lógicos (importación, limpieza, análisis…).
  • Depuración más sencilla: es posible modificar una línea y volver a ejecutar el bloque completo.
  • Automatización: permite automatizar tareas, repetir análisis y ejecutar procesos complejos sin intervención manual.
  • Portabilidad: el script puede compartirse con otros usuarios, garantizando que obtengan exactamente los mismos resultados.
  • Eficiencia a medio plazo: evita reescribir código y permite reutilizar análisis previos.
  • Integración con editores como R-Markdown o Quarto: facilita generar informes y resultados reproducibles a partir del mismo código. Tanto R-Markdown como Quarto se pueden escribir usando RStudio. De hecho, este guión está elaborado con Quarto.

Para crear un nuevo Script debes usar el menú File/New File/ R Script.

Crea un nuevo script y escribe en él las dos líneas siguientes. Ejecuta seguidamente el código pulsando Ctrl+Enter. Observa la diferencia con el manejo de la consola.

2 * 2 + 2 
2 * (2+2)

\(\checkmark\) Prueba a seleccionar con el cursor las dos líneas y pulsar Ctrl+Enter.

\(\checkmark\) Repite los cálculos de las expresiones 1 y 2 desde el script. Observa la mejora en cuestión de edición respecto al uso de la consola (por ejemplo, ahora puedes usar copiar y pegar de forma más ágil).

Es muy recomendable documentar bien el código, ya que facilita su comprensión y reutilización en el futuro.

Para añadir un comentario basta con escribir el carácter # antes del texto correspondiente.

Todo lo que aparece tras el carácter # hasta el final de la línea se considera comentario; al pasar a la línea siguiente, el comentario deja de estar activo. Verás que en las soluciones de las tareas propuestas en este guion, aparecen comentarios explicativos.

Si al final de un comentario, se escriben cuatro guiones seguidos: “—-”, este asume la categoría de título de sección y se incorpora al índice outline (este aparece pulsando el botón de la esquina superior derecha de la ventana script; en versiones antiguas de RStudio puede faltar, convendría acturalizarlo).

Documenta tu script: añade un título al código que llevas escrito, por ejemplo:

# Cálculos básicos ----

y en las siguientes secciones pon como el título de la sección, por ejemplo

## Creación de un script  ---- 

Comprueba el efecto de haber puesto los cuatro guiones tras el comentario en la ventana outline. En lo que sigue, es recomendable que personalices a tu gusto el script que estamos elaborando en esta práctica, aunque en los ejercicios propuestos no se va a sugerir nada en este sentido para ganar agilidad.

A partir de ahora dejaremos de trabajar directamente en la consola, salvo para realizar algún cálculo puntual o de carácter transitorio. En adelante, centraremos el trabajo en la elaboración de scripts.

En general, cada línea de un script debe contener un único comando. Sin embargo, más adelante veremos que algunos comandos se pueden escribir ocupando varias líneas. También es posible escribir varios comandos en una misma línea si se separan con punto y coma: ;.

Observa que en el menú File aparecen las opciones Save (Ctrl+S) y Save as... que permiten guardar el script que estamos generando (el archivo tendrá extensión ‘.R’).


3 Funciones básicas de cálculo

Una función en R es un conjunto de instrucciones que realiza una tarea determinada y devuelve un resultado. Las funciones permiten automatizar cálculos, evitar repetir código y trabajar de forma más eficiente.

Por ejemplo, la función round() sirve para redondear el número de decimales del valor que se le indique. Como toda función en R, esta incluye tres elementos básicos:

  • Nombre de la función, que identifica la operación que realiza; en este caso el nombre es round (cuidado, es sensible a mayúsculas o minúsculas)
  • Paréntesis, que deben escribirse siempre después del nombre, incluso cuando no haya argumentos. El paréntesis se debe abrir siempre a continuación del nombre de la función, sin dejar espacios en blanco.
  • Argumentos, que son los valores o parámetros que la función utiliza para trabajar.
    • Pueden escribirse nombrados (por ejemplo: round(x = 3.1416, digits = 2)),
    • o sin nombrar, respetando el orden esperado: round(3.1416, 2) hace lo mismo que la anterior.
    • En muchas funciones, hay argumentos que tienen un valor por defecto. En este caso, se puede omitir el argumento y la función asumirá su valor por defecto. En el caso de digits, su valor por defecto es digits=0, por lo tanto, si escribimos round(3.1416), la función devolverá el primer argumento redondeado sin decimales: 3, igual que si hubiéramos escrito round(3.1416, 0).

Lo comprobamos, copia y ejecuta este código:

# redondeo a 2 decimales
round(x = 3.1416, digits = 2)

# Lo mismo, pero sin nombrar los argumentos
round(3.1416, 2)

# Omisión del argumento.
# por defecto, digits=0, así que la lína
# siguiente redondea al valor entero más próximo
round(3.141)
NotaFunciones numéricas básicas

Estas funciones permiten hacer operaciones básicas de cálculo. x representa un valor numérico

  • sqrt(x)             raíz cuadrada
  • abs(x)                valor absoluto
  • round(x,n)      redondeo al entero más próximo con n decimales
  • floor(x)           redondeo hacia abajo
  • ceiling(x)      redondeo hacia arriba
  • log(x)                logaritmo natural
  • log10(x)           logaritmo base 10
  • exp(x)               exponencial
Haz los siguientes cálculos, presentando los dos últimos con tres decimales:

\[ \sqrt{50},\space\space \log(25),\space\space \log(\sqrt{37}),\space\space e^{-0.5} \]

sqrt(50)
log(25)
round(log(sqrt(37)), 3)
round(exp(-0.5), 3)

4 Creación de objetos en R

Un objeto en R es una entidad que almacena datos o código y que tiene un tipo y una estructura definidos. Es decir, un objeto es cualquier cosa que puede guardarse en memoria y manipularse en R.

Un objeto se crea con el operador <- (los dos símbolos deben ir juntos, sin espacios intercalados). Por ejemplo

x <- 7

este código crea el objeto x asignándole el valor 7. Ahora podemos utilizar el objeto en lugar del valor, por ejemplo, copia este código, pégalo en tu script y ejecútalo (observa el uso de los comentarios tras # para documentar las operaciones):

# La siguiente línea recupera el valor de x y lo muestra por pantalla:
x   
# La siguiente recupera el valor dado a x, lo eleva al cuadrado y
# asigna el resultado al objeto y, que se crea con esta operación: 
y <- x^2    

# Esta línea muestra por pantalla el valor de y:            
y           

# Recupera el valor de x, le suma 2 y el resultado lo vuelve a guardar en x     
x<-x+2      

# ¿Cuánto vale ahora x?
x           

R es sensible a mayúsculas y minúsculas, es decir, x y X serían objetos diferentes (el segundo no está definido ahora mismo).

Podemos saber de qué clase es un objeto utilizando la función class(). Averigua de qué clase son los objetos x e y definidos anteriormente.

None Caso clínico: Cálculo del índice de masa corporal de un paciente

Supongamos que la altura de un paciente es 183 cm y su peso 95 kg.

Se trata de determinar su índice de masa corporal (IMC), definido como

\[ IMC=\frac{peso \space(kg)}{\left( altura \space (m)\right)^2} \tag{3}\]

Vamos a hacer el cálculo creando los objetos oportunos:

  1. Define un objeto altura y otro objeto peso a los que debes asignar los valores indicados.
  2. Vamos a determinar el índice de masa corporal (IMC) de ese sujeto. Haz el cálculo de la expresión (3) e indica cuál es el IMC (observa si hay que adaptar las unidades de medida).
  3. Utilizando el código que ya has escrito, determina cuál es el valor del IMC para un nuevo paciente que pesa 90 kg y que mide 180 cm.
  4. Modifica convenientemente el código para que el IMC aparezca con un solo decimal.
altura<-183
peso<-95
imc<-peso/(altura/100)^2
round(imc,1)

Los IMC pedidos son 28.4 y 27.8. Para obtener el segundo, basta con cambiar los valores asignados al peso y a la altura y volver a ejecutar el código.

4.1 Comparaciones lógicas

En R, además de trabajar con números, es fundamental manejar valores lógicos. Estos son valores que solo pueden adoptar dos estados:

  • TRUE (verdadero)
  • FALSE (falso)

Las variables lógicas suelen ser el resultado de evaluar una comparación. Es decir, R “comprueba” una condición y devuelve TRUE o FALSE en función de si se cumple o no. Esta capacidad resulta esencial para realizar filtros, tomar decisiones en el análisis y automatizar cálculos más complejos.

Suponemos aquí que x e y representan dos valores o dos vectores numéricos

operador Propósito Tipo Ejemplo
== igual lógico x == y
!= distinto lógico x !- y
< menor lógico x < y
<= menor o igual lógico x <= y
> mayor lógico x > y
>= mayor o igual lógico x >= y
Cuidado con no poner un espacio intercalado en los operadores que se componen de dos signos. Deben ir juntos.

Define dos objetos x e y asignándoles los valores que desees. Utiliza todos los operadores relacionales y observa el resultado.

# Asumimos dos valores arbitrarios
x <- 7
y <- 14

# Realizamos comparaciones
x == y  # ¿es x igual a y?
x != y  # ¿es x distinto de y?
x > y   # ¿es x mayor que y?
x >= y  # ¿es x mayor o igual que y?
# etc
TipObservación

Observemos que hay una correspondencia entre los valores lógicos y los valores numéricos 0 y 1:

  • el valor lógico TRUE se comporta como el valor numérico 1.
  • el valor lógico FALSE se comporta como el valor numérico 0.

Esto es útil a efectos de poder hacer ciertos cálculos, como veremos un poco más adelante.

Aunque solo hemos visto el uso de comparaciones lógicas con objetos sueltos, las variables lógicas se vuelven especialmente útiles cuando se aplican a conjuntos de datos: permiten seleccionar observaciones, identificar casos que cumplen una condición y controlar el flujo de operaciones. Por tanto, comprender cómo funcionan estas comparaciones es un paso previo imprescindible para trabajar con estructuras de datos más complejas e interesantes, como son los vectores y los data frames.


4.2 Vectores en R

En R, los vectores representan una de las estructuras de datos básicas y, a la vez, una de las más importantes. Casi todo el análisis estadístico que realizaremos en adelante parte de datos almacenados en forma de vectores.

Un vector no es más que una colección ordenada de valores del mismo tipo, por ejemplo:

  • la frecuencia cardiaca en reposo de un grupo de pacientes,
  • los niveles de glucosa diaria de un sujeto durante un mes,
  • las puntuaciones obtenidas en un cuestionario de ansiedad,
  • los tiempos de espera en urgencias, etc.

El trabajo con vectores es esencial en R porque permite manejar conjuntos completos de datos de forma coherente, evitando errores asociados a valores sueltos y facilitando operaciones estadísticas, comparaciones y filtrados de manera simultánea. En la práctica, trabajar con vectores hace el análisis más eficiente, limpio y reproducible.

Además, R está diseñado para el cálculo vectorizado, lo que permite realizar operaciones complejas sin recurrir a bucles iterativos ni a estructuras de programación más avanzadas. Esto simplifica el código y acelera el procesamiento de datos.

Vamos a introducir el manejo de vectores a través de un ejemplo.

4.2.1 Creación de vectores

None Caso clínico: Seguimiento de la presión arterial de un paciente

El personal de enfermería ha registrado la presión arterial, tanto sistólica (PAS) como diastólica (PAD) de un paciente en reposo, siempre a las 9 de la mañana. Los valores son:

Día 1 2 3 4 5 6 7 8
PAD 84 98 102 92 100 96 105 90
PAS 162 158 165 149 160 155 142 150

Las medidas están expresadas en mmHg.


Queremos hacer algunos cálculos con estas medidas con el objetivo de evaluar el nivel de riesgo cardiovascular del paciente.

Lo primero que hay que hacer es crear un vector que almacene cada una de las variables de presión arterial.

Introduce estas dos líneas en tu script y pulsa Ctrl+Enter después de cada una (o bien tras seleccionar las dos como un bloque):

PAD <- c( 84,  98, 102,  92, 100,  96, 105,  90)
PAS <- c(162, 158, 165, 149, 160, 155, 142, 150)

La función c() es la encargada de concatenar los valores individuales para formar un vector. Ahora, cuando se maneje PAD, se estará aludiendo a los 8 valores que componen este vector. Lo mismo para PAS.

Con el código anterior ya hemos definido los dos vectores. Observa como aparecen en el panel Environment (panel numerado con 3 en la Figura 2):

Figura 2: Listado de objetos activos en el entorno de R. Se indica el nombre del objeto, su clase (en este caso numérica), su extensión (de 1 a 8) y sus valores.

Veamos ahora el manejo de los vectores.

Comprueba qué se obtiene si se multiplica PAS \(\times\) 2 ¿se duplica un solo valor o todos los del vector?

De este ejemplo se deduce que en, en general, vamos a poder operar con un vector como si fuera un número. El efecto de hacer, por ejemplo, la multiplicación de un vector por 2 es que cada uno de sus valores se multiplican por 2.

NotaAlgunas funciones para vectores

Suponemos aquí que x representa un vector de valores numéricos

  • length(x)      longitud del vector
  • min(x)            valor mínimo  
  • max(x)             valor máximo
  • sum(x)             suma de todos los valores

Las funciones estadísticas operan, fundamentalmente, sobre vectores, las iremos viendo más adelante.

Prueba a hacer operaciones aritméticas básicas y aplicar alguna de las funciones que ya conocemos con los vectores que acabamos de definir. Por ejemplo:

  • Obtén los valores mínimo y máximo de la presión arterial sistólica.
  • Determina la longitud de ambos vectores.
  • Obtén la raíz cuadrada de los valores de presión arterial sistólica.
min(PAS)
max(PAS)
length(PAS)
length(PAD)
sqrt(PAS)

Comprueba cómo podemos añadir nuevas observaciones a un vector -es decir, juntar vectores- utilizando la función c(). Por ejemplo:

# La siguiente línea añade el valor 160 al final del vector PAS:
c(PAS,160) 
# pero no hemos asignado el resultado a ningún objeto, este vector 
# con 9 datos no se ha almacenado en ningún sitio 

# A continuación, se añaden dos valores al final de PAS
# se almacena el resultado en PAS2 y se muestra su contenido
PAS2<-c(PAS,c(160,165)) 
PAS2                    

# ¿Qué hacen las siguientes líneas?
previaPAS<-c(160,165,153)
PAS3<-c(previaPAS,PAS)
PAS3
length(PAS3)

A partir de ahora, piensa que un número es un vector de tamaño igual a 1.

None Caso clínico: Presión de pulso

La presión de pulso (PP) es un parámetro hemodinámico del máximo interés que se define como la diferencia entre las presiones sistólica y diastólica: \[PP=PAS-PAD\] y representa la amplitud de la onda de presión arterial.

  1. Obtén la presión de pulso de cada uno de los días (define un objeto para este vector de valores)
  2. Obtén los valores máximo y mínimo de la presión de pulso
  3. Obtén la presión de pulso media de los ocho días. La media es la suma de valores dividida por el número de sumandos, utiliza las funciones sum() y length() que hemos mencionado antes.
  4. Los valores de la presión de pulso permiten hacer una evaluación del riesgo cardiovascular (RCV). Considerando los criterios de la tabla siguiente, valora en qué situación puede estar el paciente estudiado
Indicador criterio Interpretación clínica
PP media > 60 mmHg rigidez arterial probable (mayor carga en el ventrículo izdo.)
PP mínima < 40 mmHg episodios de perfusión dudosa (indicador de hipovolemia)
PP máxima > 70 mmHg hipercontractilidad o HTA sistólica muy marcada (indicador de RCV)
  1. Se defiene el índice PP/PAS como el cociente entre la presión de pulso y la presión diastólica \[ I_{PP/PAS}=\frac{PP}{PAS}.\] El rango normal de este índice es 0.25-0.35. Valores más altos indican pulsos amplios, rigidez arterial, aumento de presión sistólica relativa y valores más bajos una PP estrecha, propia de la hipovolemia. Determina este índice (llámale IPP) para el paciente estudiado y obtén sus valores máximo, mínimo y medio con precisión de dos decimales.
  2. Escribe y ejecuta este código: IPP>0.35 interpreta la respuesta que da R.
  3. Interpreta la respuesta que da sum(IPP>0.35)
# 1.
PP<-PAS-PAD # crea el vector PP
PP          # lo muestra por la consola
[1] 78 60 63 57 60 59 37 60
# 2.
max(PP)
[1] 78
min(PP)
[1] 37
# 3.
sum(PP)/length(PP)           # calcula la media (pero no la asigna a ningún objeto)
[1] 59.25
pp_media<-sum(PP)/length(PP) # define un objeto para el valor de la PP media

# 5. 
IPP<-PP/PAS

# 6.
IPP>0.35  # indica si cada valor de IPP es mayor (TRUE) o no (FALSE) a 0.35
[1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
# 7.
sum(IPP>0.35) # indica cuántas observaciones son IPP > 0.35. 
[1] 7
# los valores TRUE se comportan como un valor 1, mientras que los valores 
# FALSE se comportan como un valor cero. Al hacer sum() de valores TRUE o FALSE
# estamos determinando el número de veces que ocurre TRUE en el vector

Valores obtenidos

  • PP mínima = 37 < 40 leve estrechamiento en el pulso. Sugiere episodios puntuales de perfusión baja.
  • PP máxima = 78 > 70 rigidez arterial marcada, es un patrón típico de hipertensión sistólica en pacientes con arteriosclersosis. Además, este indicador sirve de evidencia de riesgo cardiovascular alto.
  • PP media = 59.25 \(\approx\) 60, indica rigidez arterial relevante, hay cierta carga en el ventrículo izquierdo, la aorta amortigua poco la presión sistólica
  • IPP con valores altos, 7 días por encima de 0.38. Esto indica un pulso amplio provocado por una rigidez arterial importante.

El paciente presenta presión de pulso amplia de forma sostenida, con picos elevados y mínimos solo moderadamente bajos. Esto es propio de pacientes de edad avanzada, hipertensos crónicos o sujetos con riesgo cardiovascular elevado.

4.2.2 Selección de los elementos de un vector

Veamos ahora cómo obtener valores de un vector bajo dos criterios:

  • Dada la posición -o posiciones- en el vector, obtener los valores correspondientes.
  • Dados ciertos valores, obtener sus posiciones en el vector.

Ilustramos esto utilizando los objetos PAS y PAD definidos en Sección 4.2.1.

Posición \(\rightarrow\) valor: Estudia qué hace cada una de estas líneas de código

PAS
PAS[3]
PAS[1:3]
PAS[4:length(PAS)]
PAS[c(1,5)]       
PAS[-3]           
PAS[-c(2,3)]      
PAS                  # Muestra los valores del objeto PAS
PAS[3]               # Devuelve el tercer valor de PAS

# Las siguientes líneas devuelven objetos que también son vectores: 
PAS[1:3]             # = los tres primeros valores de PAS
PAS[4:length(PAS)]   # = desde el valor 4 al último (independientemente de cuántos haya)
PAS[c(1,5)]          # = es lo mismo que poner c(PAS[1], PAS[5])
PAS[-3]              # = todos menos el tercero
PAS[-c(2,3)]         # = todos menos el segundo y el tercero

Posición \(\leftarrow\) valor: Ahora estudia qué hace cada una de estas líneas de código

PAS # lo volvemos a consultar para comparar con lo que sigue
which(PAS==165)
which(PAS>=160)
which.min(PAS)
which.max(PAS)
# Cada línea devuelve un vector con las posiciones donde se da la condición indicada
which(PAS==165)   # recuerda que se usa doble signo '=='' para comparar la igualdad
which(PAS>=160)   # usa >= para mayor o igual y <= para menor o igual
which.min(PAS)    # posición del mínimo
which.max(PAS)    # posición del máximo

4.2.3 Ordenación de los elementos de un vector

Básicamente, hay dos operaciones de ordenación:

  • Ordenar de forma efectiva los elementos de un vector. Esto se hace con la función sort()
  • Obtener la posición que ocuparía cada elemento del vector si este se ordena. Se hace con la función order()

En ambos casos, la ordenación es en sentido creciente. Para ordenar con un criterio decreciente, de mayor a menor, las dos funciones admiten el argumento decreasing=TRUE (en ambos casos, su valor por defecto es decreasing=FALSE).

Funciones de ordenación. Analiza qué hace cada una de estas líneas de código:

PAS # pedimos ver los valroes de PAS para comparar con lo que sigue

sort(PAS)                   
sort(PAS, decreasing=TRUE)  

order(PAS) 
order(PAS, decreasing=TRUE)  

orden<-order(PAS)
PAS[orden] 
PAD[orden] 
sort(PAS)                   # PAS de menor a mayor (por defecto, decreasing=TRUE)
sort(PAS, decreasing=TRUE)  # PAS de mayor a menor

order(PAS) # = número de posición de cada observación en la ordenación
order(PAS, decreasing=TRUE) # igual, pero en sentido decreciente 

orden<-order(PAS)
PAS[orden]  # = PAS ordenada de menor a mayor, igual que sort(PAS)
PAD[orden]  # = PAD ordenada según el orden de PAS (¡interesante!)

Vamos a practicar con la ordenación de valores:

  • Obtén un vector con los tres primeros valores de la presión de pulso (PP)
  • Determina en qué día se han producido los valores máximo y mínimo de la presión de pulso
  • Obtén la secuencia de ordenación de los valores de la presión de pulso (usando order(). Interpreta estos valores).
  • Obtén un vector con la presión de pulso ordenada de forma descendente
  • Determina cuántos días ha estado el índice PP/PAS por encima de 0.4
PP[1:3]
[1] 78 60 63
which.max(PP)
[1] 1
which.min(PP) 
[1] 7
order(PP)
[1] 7 4 6 2 5 8 3 1
sort(PP, decreasing=TRUE) 
[1] 78 63 60 60 60 59 57 37
sum(IPP>0.4)
[1] 1

5 Bases de datos en R: los data frames

Hasta ahora, hemos trabajado con las variables PAS, PAD, PP e IPP como vectores independientes. Aunque esto es útil para empezar, resulta poco práctico y propenso a errores cuando manejamos varios vectores que representan información del mismo conjunto de individuos, o de días como en nuestro ejemplo: es fácil perder el orden, mezclar longitudes o modificar uno de los vectores sin actualizar convenientemente los demás.

Para trabajar de forma segura y eficiente, R ofrece un tipo de objeto que se traduce en una estructura de datos más elaborada: son los data frames (marcos de datos), una estructura tabular donde cada columna es una variable y cada fila corresponde a una observación (sujeto, día, situación,…). Al reunir nuestras variables -PAS, PAD, PP, IPP- en un único data.frame, garantizamos que las variables permanecen sincronizadas, podemos inspeccionarlas de manera conjunta y disponemos de una base sólida para el análisis estadístico y la manipulación eficiente de los datos.

5.1 Creación de un data frame

La función que permite juntar vectores en un data frame es data.frame(). Es imprescindible que los vectores tengan el mismo número de elementos. De lo contrario se genera un error.

Vamos a crear un data frame con los valores de presión arterial introducidos en la sección Sección 4.2.1. Lo llamaremos datos_PA (fíjate en el uso del guion bajo para hacer el nombre más intuitivo). Por ahora, incluiremos únicamente las observaciones originales de presión sistólica y diastólica. Más adelante veremos cómo añadir nuevas columnas con la presión de pulso y el índice PP/PAS.

# Creamos el data.frame a partir de las variables definidas anteriormente
datos_PA <- data.frame(PAS, PAD)

# Veamos la tabla resultante
datos_PA

Como vemos, se obtiene una tabla rectangular con dos columnas y ocho filas. Cuando queramos aludir a alguna de las columnas de un data.frame, la sintaxis es: nombre_data.frame$nombre_columna. Por ejemplo:

# Alusión a la columna PAS del data.frame datosa partir de las variables existentes
datos_PA$PAS

# Veamos la tabla resultante
datos_PA

Aunque existen más formas de referirse a los elementos de un data frame. En la Sección 4.2.2 vimos que la sintaxis vector[índice] permite recuperar el elemento del vector situado en la posición indicada. Por ejemplo, PAS[3] devuelve la tercera observación del vector PAS.

En un data frame, en cambio, debemos especificar dos posiciones: la fila y la columna. Así, la expresión data.frame[fila, columna] permite acceder al valor que ocupa esa combinación de fila y columna.

Además, esta notación ofrece otras posibilidades útiles. Si omitimos el número de fila, obtenemos el vector completo correspondiente a una columna (data.frame[, 1] devuelve el vector correspondiente a la columna 1; observa que la coma es imprescindible para distinguir filas y columnas). Del mismo modo, si indicamos la fila pero dejamos en blanco la columna (data.frame[3, ]), recuperamos la fila completa.

Vamos a hacer consultas al data.frame que acabamos de crear (tendrás que recordar los conceptos que vimos en la la Sección 4.2.2). Obtén los valores que se piden a continuación:

  • las observaciones de presión arterial del día 5.
  • las observaciones de presión arterial de los días 5 y 6.
  • los valores de presión arterial diastólica de los días 4 al 6.

Comprueba qué ocurre si escribimos los corchetes, pero no ponemos ningún índice

datos_PA[5, ]       # PAS y PAD del día 5
  PAS PAD
5 160 100
datos_PA[5:6, ]     # PAS y PAD de los días 5 y 6
  PAS PAD
5 160 100
6 155  96
# valores de PAD de los días 4 al 6. 
# Hay dos forma de hacerlo:
datos_PA[4:6, 2]    # aquí manejamos todo el data.frame
[1]  92 100  96
datos_PA$PAD[4:6]   # aquí manejamos el vector datos_PA$PAD 
[1]  92 100  96
# si no ponemos índices, es lo mismo que no poner corchetes, se alude a todo el objeto:
datos_PA
  PAS PAD
1 162  84
2 158  98
3 165 102
4 149  92
5 160 100
6 155  96
7 142 105
8 150  90
datos_PA[ , ]  # observa que dejar el hueco de fila o columna en blanco equivale a decir "todos"
  PAS PAD
1 162  84
2 158  98
3 165 102
4 149  92
5 160 100
6 155  96
7 142 105
8 150  90

5.2 Añadir nuevas columnas a un data frame

Hay varias formas de añadir columnas a un data frame ya existente. Todas las que vamos a ver dan el mismo resultado. Dependiendo de la situación, puede convenir una u otra, pero el resultado es el mismo

A menudo encontraremos varias maneras de obtener un mismo resultado en R. Esto forma parte de la propia filosofía del lenguaje: lo habitual es que existan múltiples caminos para llegar a la misma solución.

Veamos tres maneras de incorporar la columna PP en el data frame datos_PA. Para interpretar correctamente los comandos que siguen debes leer lo que aparece a la derecha de <- y

  • Asignarla directamente datos_PA$PP <- PP
  • Crear un data frame ampliado y asignarlo al original: datos_PA <- data.frame(datos_PA, PP)
  • Utilizar la función de anexado de columnas cbind(): datos_PA <- cbind(datos_PA, PP)

Para entender qué hace un comando que utiliza el operador de asignación <-, conviene leer primero lo que aparece a su derecha y precederlo mentalmente con “se crea…”. Después, interpreta el operador como “y se asigna a”, y finalmente menciona el objeto que aparece a la izquierda. Por ejemplo:

datos_PA <- data.frame(datos_PA, PP)

se interpreta como sigue:

se crea el objeto data.frame(datos_PA, PP) y se asigna al objeto datos_PA


Cuando usamos el mismo nombre de objeto a ambos lados de <- (como ocurre con datos_PA), el proceso debe entenderse así: primero se evalúa la expresión situada a la derecha, que en este caso utiliza datos_PA como data frame de referencia para añadirle una nueva columna. Una vez generado ese resultado, R lo asigna al nombre indicado a la izquierda. Si el objeto no existía, lo crea; y si ya existía, lo reemplaza por completo con el nuevo contenido.

\(^{*}\) Observación técnica. Al operador <- se le puede dar la vuelta: ->. En este caso, lo que haya a la izquierda se asigna a lo que haya a la derecha.

Amplía datos_PA como se propone a continuación:

  1. Utiliza cualquiera de las tres formas descritas anteriormente para añadir el vector IPP con el índice PP/PAS que ya definimos en la Sección 4.2.1.
  2. El índice PP/PAS presenta demasiados decimales y resulta un tanto engorroso de leer. Haz el cambio oportuno para que en el data frame aparezca solo con tres.
  3. Si escribimos en R la sentencia 1:8, obtenemos un vector con valores consecutivos del 1 al 8. En general, la expresión a:b genera una secuencia que avanza de uno en uno desde a hasta b (y si a es mayor que b, la secuencia se crea en orden descendente). Utilizando esta notación, añade a datos_PA una primera columna que represente el día de observación.
# ATENCIÓN: En cada apartado, solo hay que usar una de las soluciones propuestas
#
# 1.
# Todas estas sentencias hacen lo mismo:
# a) usando el vector IPP ya calculado
datos_PA$IPP<-IPP
datos_PA<-cbind(datos_PA, IPP)
datos_PA<-data.frame(datos_PA,IPP)
# b) utilizando el cálculo original
datos_PA$IPP<-PP/PAS 

# 2. 
# Cambiamos los decimales de datos_PA$IPP
datos_PA$IPP<-round(datos_PA$IPP,3)

# 3. 
# Varias formas de hacerlo:
# a) Si usamos la asignación 
# datos_PA$dia<-1:8
# se crea esta columna, pero al final de todas, no al principio.
# Para que sea la primera columna podemos hacerlo 
# como sigue (observa el orden donde aparece dia como argumento):

# b) calculando el día de forma implícita
datos_PA<-data.frame(dia=1:8, datos_PA) 
datos_PA<-cbind(dia=1:8, datos_PA)
# c) calculando previamente el día como vector
dia<-1:8
# ahora podemos hacer
datos_PA<-data.frame(dia, datos_PA) 
# o bien
datos_PA<-cbind(dia, datos_PA) 

5.3 Añadir nuevas filas a un data frame

Si la función cbind() permitia unir nuevas columnas a un data.frame (la ‘c’ alude a column y bind significa “unir” en inglés), existe una función análoga para unir filas: rbind() (la “r” alude a row, “fila”).

Pero hay que asegurar que la fila que se vaya a unir (puede ser más de una en la misma operación) tiene la misma estructura que el data.frame que la va a recibir.

Se dispone de dos lecturas de presión arterial correspondientes a los días 9 y 10 del mismo paciente. Deseamos unir esta información al data.frame original. Las lecturas son:

Día 9 10
PAD 94 88
PAS 160 150

mmHg

En el apartado anterior exploramos varias maneras de añadir columnas a un data frame. Durante estas pruebas es posible que hayamos generado columnas duplicadas o redundantes, lo que ocasionaría problemas en esta sección. Para evitarlo, vamos a sanear el objeto datos_PA antes de incorporar nuevas filas, garantizando así que la operación se realice de forma correcta. Ejecuta este código:

dia<-1:8
PAD <- c( 84,  98, 102,  92, 100,  96, 105,  90)
PAS <- c(162, 158, 165, 149, 160, 155, 142, 150)
PP  <-PAS-PAD
IPP <-PP/PAS
datos_PA <- data.frame(
  dia = dia,
  PAS = PAS,
  PAD = PAD,
  PP  = PP,
  IPP = round(IPP,3)
)
NoneSolución

Añadimos ahora nuevas observaciones al datos_PA:

En primer lugar, tenemos que crear un registro de las nuevas observaciones. Esto lo podemos hacer de varias maneras, una de ellas es creando un data frame:

nuevoPAD <- c( 94, 88)
nuevoPAS <- c(160,150)
nuevoPP  <-nuevoPAS-nuevoPAD
nuevoIPP <-nuevoPP/nuevoPAS

nuevo_PA <- data.frame(
  dia = c(9,10),
  PAS = nuevoPAS,
  PAD = nuevoPAD,
  PP  = nuevoPAS-nuevoPAD,
  IPP = round(nuevoIPP,3)
)

Observa que la estructura de nuevo_PA debe coincidir exactamente con la de datos_PA; es decir, los nombres situados a la izquierda del = han de ser los mismos que aparecen en datos_PA.

Además, fíjate en que algunos cálculos podrían haberse realizado directamente dentro de la definición de nuevo_PA, como ocurre con dia. Lo importante es elegir la forma que nos resulte más clara y cómoda.

Cuidado con no confundir cuándo se usa <- y cuándo =:

  • Usa <- para crear o actualizar objetos en el entorno (en este contexto, el signo = también funcionaría, pero se desaconseja utilizarlo).
  • Usa = para pasar argumentos con nombre a funciones (no estás creando objetos, sino estableciendo valores para parámetros).

A continuación, incorporamos esta nueva información a datos_PA:

NoneSolución

Añadimos ahora nuevas observaciones a datos_PA:

En primer lugar, tenemos que crear un registro de las nuevas observaciones. Esto lo podemos hacer de varias maneras, una de ellas es creando un data.frame:

datos_PA<-rbind(datos_PA, nuevo_PA)
datos_PA # observa el data.frame con los 10 dias

Si queremos eliminar la información que hemos generado de forma auxiliar, podemos usar la función rm() (abreviatura de remove,“eliminar” en inglés).

# Eliminación de las variables auxiliares
rm(nuevoPAD)
rm(nuevoPAS)
rm(nuevoPP) 
rm(nuevoIPP)
rm(nuevo_PA)

5.4 Eliminación de filas o columnas de un data frame

Para practicar sin modificar nuestro data frame original, vamos a trabajar con una copia.

Crea una copia de datos_PA que se llame copia_PA

copia_PA<-datos_PA      # creamos la copia
copia_PA                # la mostramos por pantalla

Hay varias maneras de eliminar filas o columnas de un data frame. Vemos solo una de ellas para cada caso

5.4.1 Eliminación de columnas

# Eliminación de la columna dia
copia_PA$dia<-NULL      # eliminación de la columna "dia"
copia_PA                # mostramos por pantalla

Elimina la columna IPP de copia_PA

copia_PA$IPP <- NULL      # creamos la copia
copia_PA                  # la mostramos por pantalla

5.4.2 Eliminación de filas

Para eliminar filas de un data frame podemos utilizar su sistema de indexación. Basta con indicar el número de la fila (o filas) que queremos suprimir, precedido del signo “–”. En el siguiente ejemplo, fíjate en que dejamos vacío el índice de columnas —lo que indica que queremos seleccionar todas ellas— y en que es necesario asignar el resultado para que el cambio se mantenga en el objeto original.

# Eliminación de la primera fila
copia_PA <- copia_PA[-1, ] # observa que hay que asignar el resultado de la derecha

# Eliminación de las filas 4 y 5 
copia_PA <- copia_PA[-c(4,5), ] 

5.5 Archivado y lectura de data frames en R

Cuando trabajamos con data frames en R necesitaremos guardar los datos para utilizarlos más adelante, ya sea en el mismo proyecto o en otro análisis. Para ello, R ofrece distintos formatos con los que podemos archivar tablas, cada uno con sus propias ventajas en cuanto a compatibilidad, tamaño y rapidez. En esta sección presentamos los formatos más habituales y veremos cómo guardar y leer información utilizando funciones básicas del lenguaje.

5.5.1 Formatos de archivos de datos en R

Los principales tipos de formatos con los que podemos trabajar en R son los siguientes:

  • Los archivos de texto plano son simplemente tablas guardadas como texto, donde las columnas se separan mediante espacios, comas, punto y coma o tabulaciones. La gran ventaja es que cualquier programa puede abrirlos, por lo que son una opción muy práctica para compartir datos. En R se leen con read.table() o read.csv(), y se escriben con write.table() o write.csv(). En el apartado siguiente veremos estas funciones.

  • Entre los formatos nativos de R encontramos los archivos RDS (.rds), que guardan un único objeto conservando todos sus atributos, mediante saveRDS() y readRDS(), y los archivos RData (.RData), que permiten almacenar varios objetos a la vez usando save() y load(). De momento, no los vamos a ver aquí.

  • R también puede trabajar con formatos externos muy utilizados en entornos reales. Por ejemplo, es posible leer o guardar datos en archivos de Excel (.xlsx) mediante paquetes como readxl, o conectarse a bases de datos como SQLite o MySQL cuando el volumen de información es mayor. Además, existen otros formatos especializados para grandes conjuntos de datos. Por ahora, tampoco abordaremos estos formatos.

En este capítulo nos centraremos en los formatos de texto plano, ya que son los más sencillos, accesibles y fáciles de compartir.

5.5.2 Archivado y lectura en formato CSV (.csv)

La función más sencilla para guardar un data frame es usar la función write.csv(). CSV es el acrónimo en inglés de texto separado por comas (Comma Separated Values). Esto significa que usa por defecto una coma como separador de columnas y un punto como separador decimal, que es lo que esperan la mayoría de programas en su configuración internacional (por ejemplo Excel).

El código necesario para almacenar nuestro data frame como un archivo CSV es el siguiente:

write.csv(
  datos_PA,              # nombre del data.frame
  file = "datos_PA.csv", # nombre del archivo a crear. Si hay que indicar una carpeta, usa "/" como separador
  row.names = FALSE)     # evita que exportemos el número de fila como primera columna

Si posteriormente queremos recuperar esta tabla, el código necesario para leerla es

datos_importados<-read.csv("datos_PA.csv")

Algunas observaciones importantes son las siguientes: el nombre del data frame que genera la lectura de la tabla, no tiene por qué coincidir con el que utilizamos en su momento para crear ese data frame (antes fue datos_PA y ahora hemos usado datos_importados); no hay que indicar nada relativo al formato, esta función espera que se use el punto decimal y que las columnas estén separadas por comas.

5.5.3 Archivado y lectura del formato texto en general (.txt)

La función write.csv() es simplemente una versión especializada de write.table(), pensada para el formato CSV típico en inglés. Esto significa que, como ya hemos visto, usa por defecto una coma como separador de columnas y un punto como separador decimal. En cambio, write.table() es mucho más flexible, porque permite elegir el separador de columnas (sep), el separador decimal (dec) y otros detalles. Esto es especialmente útil en países donde se suele usar el formato “europeo”, por ejemplo punto y coma para separar columnas y coma como decimal. Veamos cómo usar write.table():

write.table(
  datos_PA,              # nombre del data.frame  
  file = "datos_PA.txt", # nombre del archivo
  sep = ";",             # carácter separador, hemos elegido el punto y coma
  dec = ",",             # establece la coma como separador decimal 
  col.names = TRUE,      # indica que la primera fila es el nombre de las variables
  row.names = FALSE      # evita que exportemos el número de fila como primera columna
)

Ahora, para leer los datos, debemos hacer una operación similar a

datos_importados <- read.table(
  "datos_PA.txt",          # nombre del archivo
  sep = ";",               # separador de columnas
  dec = ",",               # separador decimal
  header = TRUE,           # la primera fila es el nombre de la variable
  stringsAsFactors = FALSE # evita que los datos de texto se conviertan en factores 
)
5.5.3.1 Fin de la práctica

Guarda todo lo que hemos hecho para que lo puedas recuperar más adelante, tanto el script como el data frame y damos por finalizada esta primera práctica.

::: {#refs} :::