R es un lenguaje orientado a objetos: cada comando crea un objeto que
debe ser “nombrado” para que permanezca en la memoria utilizando una
<-
y su nombre debe ser “invocado” para que aparezca en
pantalla. A pesar de ser un lenguaje de programación, R es
comparativamente simple e intuitivo.
objeto | descripción |
---|---|
data.frame | Objeto que contiene datos. Habitualmente se crea al importar datos externos, aunque pueden ser creados dentro del mismo R. Consiste en una serie de variables (vectors) de igual longitud y que pueden ser del mismo tipo o no. |
vectors | Colección de datos del mismo tipo (números, letras, etc.) que puede ser creado dentro de R o importado como una columna de un data.frame. Existen muchos tipos de vectores, entre ellos: numeric consiste de números reales; integer consiste de números enteros; character contiene letras, nombres, etc. (notar que cada elemento se encuentra entre comillas, por ej. “A1”); factor sirve para representar variables categóricas, porta información sobre los niveles del factor (levels) y sobre si estos niveles siguen un orden o no. |
matrix | Matriz formada por la unión de vectores de un mismo tipo y largo, por un solo vector que es partido en columnas y filas o (más habitualmente) producto de ciertas funciones, por ejemplo cor, que construye matrices de correlación. |
list | Objeto que compuesto de objetos de distinto tipo y largo. |
tibble | Objeto propio de los paquetes de tidyverse, similar en sus funciones a un data.frame pero con menores capacidades (por ejemplo, no admite nombres de filas), destinadas a simplificar su uso y evitar errores comunes.Más información en https://tibble.tidyverse.org/articles/tibble.html |
# El símbolo # desactiva el espacio a su derecha.
# Es útil para poner aclaraciones en nuestras rutinas.
# El comando más básico: Asignar crea objetos.
# Puede usarse = en su reemplazo o -> para asignar el nombre al final.
# Para unificar el estilo de todas las rutinas sólo usaré <-
n <- 15
n # escribir el nombre de un objeto es "invocarlo"
# al usar de nuevo el mismo nombre el objeto anterior se pierde ("pisarlo")
n <- 46 + 12
n
# los caracteres categóricos necesitan ser ingresados con comillas
di <- "A"
di
# Obtener ayudas, el símbolo ?
# si tenemos dudas sobre el funcionamiento de una función
? lm
# Si desconocemos el nombre de una función para realizar determinada
# tarea, puede realizarse una búsqueda con ?? (sólo en los paquetes intalados)
?? "linear models"
# crear vectores. La función concatenar c
vector <- c(1, 2, 3, 4)
vector <- c("a", "b", "c", "d")
Las funciones de esta sección son las más sencillas de R. Como regla general las funciones siempre deben escribirse acompañadas de paréntesis, que contienen los elementos sobre los cuales la función actuará. Un ejemplo de función es c, que simplemente concatena objetos. funciones más complejas apelan a diferentes argumentos a los cuales hay que asignarles un valor y se encuentran separados por comas.
# vectores de repetición.
# esta función tiene dos argumentos, separados por una coma
# rep(lo_que_queremos_repetir, cuantas veces)
xx <- rep("A", 50)
xx
# secuencias
seq1 <- c(1:50) # el símbolo : indica desde:hasta
seq1
seq2 <- seq(from = 0, to = 0.99, by = 0.01) # argumentos explícitos
seq2
# combinando vectores de a pares: cbind y rbind
az <- cbind(seq1, xx)
za <- rbind(c(1:25), rnorm(25))
# comparar
az
za
# construyendo una matriz: matrix()
m <- c(1:20)
matriz1 <- matrix(m, 4, 5) # (vector a usar, filas, columnas)
matriz1
Los datos pueden prepararse con cualquier software para planillas de
datos como Excel o LibreOffice Calc. Se recomienda:
- Que los nombres de las columnas no tengan espacios ni comiencen con
números.
- Evitar los símbolos extraños como %, &, ^, ~, ñ, etc.
- Utilizar nombres cortos.
- Los datos faltantes no deben dejarse en blanco, sino señalarse con
NA.
- Utilizar un formato tidy es decir donde
*cada columna es una variable y cada fila es una observación.
Una vez lista la planilla se recomienda guardarla en formato .txt o .csv (si bien R admite otros formatos). Pueden guardarse en cualquier carpeta del equipo. Si utilizamos R Studio y guardamos los sets de datos en la misma carpeta que la rutina, el programa determinará esa carpeta como el directorio de trabajo de forma automática, por lo que no es necesario escribir la ruta completa al archivo. También puede evitarse escribir la ruta si al abrir R seleccionamos la carpeta que donde los archivos están guardados con la opción Session - Set Working Directory, o usando la función setwd (para examinar cual es la carpeta en uso, utilizar getwd). Esta opción es muy útil si vamos a abrir varios archivos de datos guardados en el mismo directorio. Finalmente, puede abrirse una ventana de búsqueda para seleccionar el archivo con la opción file.choose().
Desde R 4.0.0 las variables categóricas ya no son reconocidas como factores al importar un set de datos en R. Así que deben ser convertidas una por una con as.factor() cuando sea necesario o con la opción stringsAsFactors = TRUE dentro de read.table o de read.csv.
# la función read.table
# opción 1 con ruta completa (notar orientación de las barras /)
# en este caso asumimos que se encuentra en el directorio RD
datos <- read.table("C:/RD/datos/peces1.txt", header=TRUE)
# opción 2 seleccionando el directorio en uso previamente desde "Archivo"
# o con setwd
setwd("C:/RD/") ## "C:/RD/" es un ejemplo...
datos <- read.table("peces1.txt", header=TRUE)
# opción 3 Abre una ventana de búsqueda
# desventaja: requiere que el humano trabaje!
datos <- read.table(file.choose(), header=TRUE)
# opción 4 la función read.csv
datos <- read.csv("C:/RD/peces1.csv", header=TRUE)
# los datos ya están guardados, pero si queremos verlos...
datos # no siempre es buena idea, sobre todo si son muchos
# una forma práctica de ver solo las primeras filas es con head
head(datos)
# o echar un vistazo a la estructura de los datos
# str informa sobre la estructura de cualquier objeto
str(datos)
# Información básica del set de datos
nrow(datos) # número de filas
ncol(datos) # número de columnas
names(datos) # nombre de las columnas
Estas operaciones se realizan para extraer de un objeto la parte que
nos interesa, por ejemplo una columna con una variable de un marco de
datos.
Sugiero no usar attach(), esta función aumenta las
probabilidades de confundirse e introducir errores.
# Para seleccionar una columna del marco de datos utilizamos $
datos$grupo
# los corchetes indican el contenido de un conjunto de datos,
# matriz o vector. La coma separa filas de columnas
#columnas
datos[, 1]
datos[, "grupo"]
# grupos de columnas
datos[, 1:3]
datos[, c("grupo", "largo.a")]
# filas y datos individuales
datos[2, ]
datos[2, 3]
datos[-2, 2]
# indexando por una condición
datos[datos$largo.a > 15, ]
datos[datos$largo.a > 15 & datos$grupo == "A", ]
# Indexación de vectores
vec <- datos$largo.a
vec[1]
vec[vec > 15]
Para esta sección es clave conocer los símbolos
lógicos: - == (igual)
- != (distinto)
- > (mayor)
- < (menor)
- >= (mayor o igual)
- <= (menor o igual)
- & y
- | o
- %in% en c(lista de posibles valores…)
# Subdivisión de un conjunto de datos
dat1 <- subset(datos, datos$grupo == "A")
dat1
dat2 <- subset(datos, datos$grupo == "A" & datos$trat == 2)
dat2
# Crear una lista de bases de datos
ldat <- split(datos, f = datos$grupo)
ldat
ldat[["B"]] # indexacion d listas
La indexación y la subdivisión permite seleccionar una parte de un objeto (por ejemplo una columna). Esto es útil para aplicar transformaciones y también para resumir información de partes de un objeto.
# reemplazar (al usar el mismo nombre) una variable numérica por un factor
datos$trat
datos$trat <- as.factor(datos$trat)
datos$trat # notar la lista de niveles del factor
# crear una nueva columna en una base de datos ya existentes
# (utilizo un nombre nuevo)
datos$pob <- c(rep("pob1", 15), rep("pob2", 15))
datos$log.largo.a <- log(datos$largo.a)
head(datos)
# crear una nueva columna uniendo clasificadores
datos$clave <- paste(datos$pob, datos$trat, sep = ".")
head(datos)
# Usando factor para arreglos más complicados
datos$pais <- factor(datos$pob, levels = c("pob2", "pob1"),
labels = c("Bolivia", "Chile"))
head(datos)
En general si cualquiera de ellas se aplica sobre datos que contienen NA (dato ausente) el resultado será NA también, por lo cual hay indicarle a la función que los elimine. Una alternativa es eliminar previamente todas las filas con datos faltantes usando la función na.omit.
# media
M <- mean(datos$largo.a, na.rm = TRUE)
M
# desvío estándar
S <- sd(datos$largo.a, na.rm = TRUE)
S
# varianza
V <- var(datos$largo.a, na.rm = TRUE)
V
# sobre un conjunto de datos, se obtiene una matriz de varianza-covarianza
VC <- var(datos[,c(3,4)], na.rm = TRUE)
VC
# de forma parecida, puede obtenerse una matriz de correlación
CO <- cor(datos[, c(3, 4)], use="complete.obs") #complete.obs elimina NA
CO
# en cambio, para realizar un test de correlación
cor.test(datos$largo.a, datos$largo.b, method = "pearson")
# resumen de datos
summary(datos)
# una función útil para aplicar una función a un subconjunto de los datos
# es aggregate, con una estructura ligeramente más complicada
MG<-aggregate(datos$largo.a, by=list(datos$grupo), FUN=mean, na.rm=TRUE)
MG
Es cada vez más frecuente encontrar operaciones básicas re R realizadas a través del paquete dplyr. Si bien mi objetivo es enseñar a utilizar r-base incorporo esta sección y la siguiente para mostrar compartivamente estos diferentes dialectos de un mismo lenguaje.
library(dplyr)
# Subdivisión o *filtrado*
dat1 <- filter(datos, grupo == "A")
dat2 <- filter(datos, grupo == "A", largo.b > 100)
# Selección por colunmas
dat3 <- select(datos, grupo, largo.a)
# select tiene funciones de ayuda. Por ejemplo, seleccionar todas las
# columnas que comiencen con "larg". Ver ?select para más ejemplos.
dat4 <- select(datos, starts_with("larg"))
# Modificación de columnas
# puede crearse un nuevo set de datos como aquí o modificarse el original
dat5 <- mutate(datos,
prop = largo.b / largo.a, # columna nueva
log.largo = log(largo.a)) # columna nueva
En 2014 Stefan Bache introdujo el operador pipa %>%
en
el paquete magrittr
que “canaliza” un objeto hacia una función (pipe puede también
traducirse como un tubo o cañería). Su uso principal es expresar una
secuencia de múltiples operaciones, evitando la creación de objetos
intermedios.
A partir de R.4.1.0, el lengujae básico incorpora una pipa nativa
(native pipe) |>
con funciones similares pero
una sintaxis ligeramente diferente a la de la pipa del paquete
magrittr.
Veamos un ejemplo sencillo donde:
library(magrittr)
library(dplyr)
# Usando R base, con creación de objetos intermedios
datos <- read.table("peces1.txt", header=TRUE)
largo.a_A <- subset(datos$largo.a, datos$grupo == "A")
M <- mean(largo.a_A)
M
# Usando R base, evitando los objetos intermedios
# Notar cómo se debe leer de "adentro" hacia "afuera".
M <- mean(subset(datos$largo.a, datos$grupo == "A"))
M
# Usando la pipa de magrittr %>%
# Notar como sigue más de cerca la lógica de
# un "lenguaje natural"
datos$largo.a %>%
subset(datos$grupo == "A") %>%
mean() -> M # en magrittr los paréntesis que acompañan mean son opcionales
M
# Usando 'pipas nativas' |>
datos$largo.a |>
subset(datos$grupo == "A") |>
mean() -> M
M
# Usando dplyr y pipa de magrittr
# (mismo resultado usando |>)
filter(datos, grupo == "A") %>%
select(largo.a) %>%
summarize(mean(largo.a)) -> M
M
Los paquetes de tidyverse forman una colección de paquetes especialmente dedicados a la ciencia de datos, introducida por Hadley Wickham. Con la excepción de ggplot2, en este curso no profundiaremos en estos paquetes, que tienen una popularidad creciente. En mi experiencia son muy eficientes para la ciencia de datos, pero el objetivo de este curso es solamente aplicar R al análisis estadístico en ciencias biológicas. Creo que es más útil enseñar tidyverse en cursos de R de nivel intermedio y no introductorio, o con otras orientaciones. R base ofrece pocas herramientas que deben combinarse para resolver un problema. Por el contrario, las funciones de tidyverse se cuentan por cientos, son sumamente eficaces (y en muchos casos resuelven problemas que requieren bastante programación en R), pero limitan la creatividad, uno de los puntos que quiero enfatizar. Finalmente, tidyverse está sujeto a rápidos cambios, mientras que R Base es altamente conservado, permitiendo la ejecución de rutinas incluso varios años después de publicadas. Por ese motivo, lo encuentro más util para asegurar una ivnestigación reproducible.
Un excelente lugar para introducirce a tidyverse es R para ciencia de datos. También, para comparar tres tipos de sintaxis en R (con signo $, el modo fórmula y tidy) se puede consultar esta cheatshet.