#### Conociendo el R #### #### Objetos en R #### # Vamos a empezar por crear un vector numérico bien sencillo numerito <- 1:6 numerito # Vamos a ver qué clase de vector es este class(numerito) # y el dato de su longitud length(numerito) # Vamos a crear un segundo vector, esta vez usando decimales valores <- c(2.3, 4.2, 6, 8.1, 10.3, 12.4) valores # Y vamos a ver que acá la clase cambia, ya que no estamos trebajando ocn enteros class(valores) # Vemos que tiene la misma longitud que el anterior length(valores) # Ahora vamos a generar un vector cuyos elementos son textos textos <- c('acelga', 'banana', 'cebolla', 'durazno', 'espinaca', 'frutilla') textos # Vamos a ver qué clsae de vector es este class(textos) length(textos) # Y vamos a generar un vector de 2 factores, "fruta" y "verdura", correspondientes # a las posiciones de los ítems del vector anterior fact class(fact) # Vamos a ver acá que este vector tiene 6 elementos en 2 categorías length(fact) levels(fact) # Con todos estos vectores vamos a armar ahora una matriz matriz1 <- cbind(textos, fact, numerito, valores) matriz1 # En este caso, al no poder asignar más de una clase a los elementos de la matriz, # se colapsan las clases de datos a la más general, 'character', y toma todo como texto class(matriz1) length(matriz1) dim(matriz1) # Conocer las dimensiones y los índices de la matriz nos va a servir, por ejemplo, # para hacer llamadas o asignaciones a elementos específicos matriz1[1] # Devuelve el valor del primer elemento de la matriz (eran 24), podemos probar otros valores matriz1[12] matriz1[24] # Pero podemos pedir los datos en alguna de las filas matriz1[1,] # O columnas matriz1[,1] # La función 'cbind' utilizada arma matrices asignando columnas en orden, y se puede # utilizar para agregar columnas nuevas cbind(matriz1, valores) # Para armar matrices agregando filas usamos 'rbind' rbind(matriz1, valores) # Esta operación así planteada produce una advertencia porque la cantidad de elementos # en 'valores' no coincide con la dimensión correspondiente de nuestra matriz. Podemos # en este caso ignorarlo y quedarnos con nuestra matriz con el dato recortado, o podemos # a efectos del ejemplo trasponer la matriz original y pegarle el vector 'valores' como # una nueva fila a esta matriz transpuesta, esta vez sin advertencias. rbind(t(matriz1), valores) # Como estos ejemplos no fueron asignados a un nuevo objeto, no quedan guardados en el # área de trabajo. Podemos guardar la matriz transpuesta en un nuevo objeto matriz1trans <- t(matriz1) matriz1trans # En general para trabajar con datos vamos a usar objetos de clase data frame. Vamos a # armar, con los mismos vectores que hicimos la matriz anterior, un objeto de esta clase datos <- data.frame(textos, fact, numerito, valores) datos class(datos) # Pero veamos qué pasó con la columna de 'textos' datos$textos # Ahora se asignó la clase 'factor' a esta columna class(datos$textos) # En realidad no queríamos esto, sino usarla como nombres de los casos observados # de las variables en 'fact', 'numerito' y 'valores', así que la vamos a asignar # como etiqueta de las observaciones en las filas usando el argumento 'row.names' datos <- data.frame(textos, fact, numerito, valores, row.names = 1) datos # Y vemos que ahora no tenemos una columna 'texto' en nuestro data frame datos$textos # sino que son los nombres de las filas rownames(datos) # y estas etiquetas siguen siendo un vector de clase 'character' class(rownames(datos)) # y podemos usar a su vez los índices para modificar algún elemento, por ejemplo # si en vez de 'acelga' teníamos 'achicoria' rownames(datos)[1] <- 'achicoria' datos # Entonces acá observamos otra utilidad de la indexación, que es ingresar o corregir datos # También podemos utilizar la función 'which' para encontrar valores, por ejemplo which(rownames(datos) == 'achicoria') which(datos$valores > 8) # Y esto lo podemos a su vez incorporar en la instrucciones para consultar los datos datos$valores[which(datos$valores > 8)] # O también para realizar operaciones, por ejemplo realizar una sustitución rownames(datos)[which(rownames(datos) == 'achicoria')] <- 'apio' datos # Los otros vectores mantuvieron sus clases originales class(datos$fact) class(datos$numerito) class(datos$valores) # La longitud del data frame es diferente que la de las matrices. En aquel caso cada celda # es un elemento, mientras que el data frame es una lista con vectores como variables en # columnas, por lo que los elementos son en realidad las variables length(datos) # Sin embargo, las dimensiones siguen siendo iguales dim(datos) # A su vez, como tanto las filas como las columnas tienen nombres asignados, podemos # consultarlos y asignarlos mediante 'dimnames', que devuelve una lista con 2 elementos dimnames(datos) # Las listas son otra de las estructuras de datos en R. Se utilizan generalmente como salida # en funciones que devuelven resultados más complejos, como las de análisis estadísticos. # Vamos a generar una lista de ejemplo con los objetos que teníamos como elementos lista1 <- list(numerito, valores, textos, fact, matriz1, datos) lista1 # Al llamarla podemos ver que presenta 6 elementos, cosa que podemos comprobar con 'length' length(lista1) # Las listas no presentan dimensiones dim(lista1) # A su vez, dentro de la lista podemos llamar y asignar valores de los objetos que la # componen. Podemos, entonces, corregir el nombre de la primer verdura en la versión de # 'datos' dentro de esta lista, para que vuelva a ser 'acelga' y quede coherente con # los valores de 'textos' y 'matriz1' rownames(lista1[[6]])[1] <- 'acelga' lista1 # Otro tipo de valores son los vectores lógicos, resultados de consultas cuyos # resultados son verdaderos o falsos, como la operación 2 > 3 class(2>3) # Si aplico esta operación a un vector voy a obtener el resultado para cada elemento numerito > 3 # Y a su vez lo puedo asignar a un nuevo objeto veclogico <- numerito > 3 veclogico # que va a ser un vector lógico class(veclogico) #### Operadores #### # Ya hemos visto algunas operaciones sencillas. Los operadores en R son los clásicos # operadores matemáticos, que se pueden usar como en la calculadora para hacer # operaciones sencillas y devuelven vectores con 1 elemento 2 + 1 4 - 2 350 / 28 4 * 3 45 ^ 2 45 ^ .5 # Y la sintaxis es igual que en las matemáticas clásicas (3 + 2 ^ 2) * 4 / 2 - 14 # y podemos aplicar las operaciones a vectores numerito * 4 # También hablamos algo de los operadores lógicos, como 'es igual a' 3 == 2 # 'es diferente a' 3 != 2 # 'es menor', o 'menor o igual a' 3 <= 2 # su opuesto 3 >= 2 # y lo podemos utilizar en operaciones con vectores numerito > 3 # Si ponemos el '!' (NOT) antes de la operación, damos vuelta el resultado !(numerito > 3) # Podemos además combinar más de una condición usando '&' (AND) y '|' (OR) 3 == 2 | 3 > 2 3 == 2 & 3 > 2 3 != 2 & 3 > 2 #### Funciones #### # En R hay infinidad de funciones, tanto en los paquetes básicos como en los # instalados. Empecemos este ejemplo con una sencilla, como la raíz cuadrada sqrt(45) # Esta función solo sirve para calcular raíz cuadrada. Si quiero calcular una # raíz enésima puedo generar una función que la calcule como x ^ (1/y). Para eso # la voy a asignar a un objeto, en este caso 'raiz' raiz <- function(x, y){ # voy a definir los argumentos x e y, sin valores por defecto # voy a poner dentro del corchete lo que hace la función x ^ (1/y) # y voy a cerrar el corchete } # y ya tenemos nuestro objeto 'raiz', de clase 'function' class(raiz) # Y ahora tenemos una función para calcular raíces enésimas raiz(45, 2) raiz(27, 3) raiz(16, 4) # Las funciones así definidas no tienen más documentación que lo que hayamos escrito al # definirlas, pero podemos consultar la documentación de las funciones en R a través del # uso de la ayuda. Con la función 'help' vamos a la documentación local de las funciones help(sqrt) # que también podemos acceder precediendo de '?' al nombre de la función ?sqrt # si no recordamos exactamente la función o tenemos una idea de alguna palabra clave # que nos sirva como guía, podemos usar help.search, con la palabra entre comillas help.search("root") # o precedida de '??' ??root # Otras función útil para conocer las funciones con las que estamos trabajando es 'args', # que devuelve los argumentos que necesitamos darle para utilizarla args(sqrt) args(args) args(sd) args(confint) # Excepto en funciones que son muy de raíz del R, a veces es útil escribir solo el nombre # de la función, ya que devuelve lo que está dentro de la misma raiz sd #### Estructuras de control: for, if, else, while #### # Vamos a pasar a ver algunas estructuras de control de flujo, que nos sirven para realizar # procedimientos iterativos y/o condicionales, muy útiles en la programación de funciones # 'for' sirve para hacer repeticiones en elementos de un vector especificado, como cuando # se debe repetir un procedimiento en una serie de datos. Veamos un ejemplo: vamos a hacer # que por cada elemento del vector 'valores', se despliegue un cartel con el número de # elemento y el valor correspondiente. for(i in 1:length(valores)){ # para cada elemento print(paste("elemento", i, "valor", valores[i])) # damos la instrucción de qué debe hacerse } #y cerramos # 'if' sirve para hacer operaciones e instrucciones condicionales. Podemos utilizarlo solo for(i in 1:length(numerito)){ # para cada elemento if(numerito[i] < 3){ # ingresamos la condición a cumplir print(paste("elemento", i, "menor a 3")) # damos las instrucciones } # cerramos el condicional } # y cerramos el 'for' # Si queremos agregar una acción a realizarse en caso de que no se cumpla la condición inicial # podemos agregar 'else' y especificar una nueva instrucción for(i in 1:length(numerito)){ # de nuevo, para cada elemento del vector if(numerito[i] < 3){ # la condición print(paste("elemento", i, "menor a 3")) # la instrucción en caso afirmativo } else { # abrimos 'else' para cuando no se cumple la condición print(paste("elemento", i, "igual o mayor a 3")) # la instrucción en caso negativo } # cerramos los condicionales } # y cerramos el 'for # Y todo esto lo podemos incorporar en una función para que nos devuelva valores diferentes # según se cumple una condición cartelito <- function(Vector, valor){ for(i in 1:length(Vector)){ if(Vector[i] < valor){ print(paste(i, 'es menor a', valor)) } else { print(paste(i, 'es mayor a', valor)) } } } class(cartelito) cartelito(Vector = 1:50, valor = 42) # Y aún lo podemos arreglar para que sea más preciso, incorporando un segundo # condicional dentro del primer 'else' cartelito <- function(Vector, valor){ for(i in 1:length(Vector)){ if(Vector[i] < valor){ print(paste(i, 'es menor a', valor)) } else { if(Vector[i] == valor){ print(paste(i, 'es IGUAL a', valor)) } else { print(paste(i, 'es mayor a', valor)) } } } } cartelito(Vector = 1:50, valor = 42) # Finalmente, el loop 'while' se usa para continuar ejecutando una instrucción # mientras las condiciones no cambien i # tenemos a i de una instrucción anterior que quedó cargado en el área de trabajo # y le vamos a pedir a R que evalúe si es menor a 20, y que mientras lo siga siendo # nos despliegue un cartelito while(i < 20) { print(paste(i, "es menor a 20")) } # Acá i no cambia, entonces se va a seguir ejecutando la instrucción indefinidamente # (lo detenemos presionando la tecla ESC o con el botón de STOP en RStudio). # Vamos a introducir un cambio, haciendo que además de imprimir el cartel, se cambie el valor # de i en cada vuelta del loop. while(i < 20) { print(paste(i, 'es menor a 20')) i <- i + 1 } # Ahora sí. #### Gráficos #### # No voy a entrar en demasiado detalle sobre los gráficos, más allá de algunas funciones # básicas. Para empezar, vamos a plotear los datos que tenemos en nuestro data frame plot(datos$valores ~ datos$numerito, main = "Datos de la feria", xlab = "Numerito", ylab = "Valores", xlim = c(0,7), ylim = c(0,14)) # Vamos a agregar etiquetas a los puntos con las frutas y verduras de nuestra lista text(rownames(datos), x = datos$numerito, y = datos$valores + 1) # Y vamos a agregar un punto nuevo, destacado points(x = 4, y = 2, pch = 20, cex = 5) # 'pch' especifica tipo de símbolo y 'cex' tamaño # y se lo vamos a adjudicar al zapallo, destacado en rojo text("zapallo", x = 4, y = 4, col = "red", cex = 2) # Solo para probar, vamos a insertar una imagen a partir de los colores graduados de los # valores que ploteamos, en la base del gráfico que tenemos image(as.matrix(datos[2:3]), add = T, x = 1:6, y = 0:1) # el argumento 'add' es para indicar # que estamos agregando a un gráfico preexistente. ### ALGUNOS COMANDOS EXTRA ### #existe una línea de comando para limpiar la consola cat("\014") # también una línea de comando para limpiar el ambiente de trabajo (usar con cuidado!) rm(list=ls()) # y por último, una lína de comando para limpiar los gráficos dev.off() ### FIN ### x11() # Vamos a abrir otra ventana de gráficos # Y vamos a hacer un gráfico de cajas de los valores en 'valores' en función de las categorías # en 'fact' boxplot(datos$valores ~ datos$fact, main = "Datos de la feria", xlab = "Categoría", ylab = "Valores", ylim = c(0,14)) # La función 'image' que usamos antes la podemos usar para plotear cosas como matrices # con celdas coloreadas. Vamos a abrir otra ventana de gráficos x11() # Ahora estamos en esta nueva ventana, y la anterior queda inactiva. No las cierren. # y vamos a plotear una matriz, generada como objeto dentro del plot, con valores de 1 a 100, # y con un "ruido" con distribución normal centrada en cada valor y desvío igual a 5, con los # valores desplegados de 1 a 10 en cada eje image(matrix(1:100 + rnorm(100, 0, 5), 10, 10), x = 1:10, y = 1:10) # Ahora vamos a volver a la ventana del boxplot dev.set(4) # y le vamos a poner la misma imagen que al primer gráfico, acomodando los valores de x para # que queden desplegados a intervalos iguales entre 1 y 2, en secuencia cada 0.2 image(as.matrix(datos[2:3]), add = T, x = seq(1, 2, 0.2), y = 0:1) # Algunos análisis tienen sus propios métodos de gráficos incorporados. Veamos qué ocurre # por ejemplo al plotear una regresión lineal a partir de nuestros datos dev.set(2) plot(lm(datos$valores ~ datos$numerito)) # Y también hay paquetes con expansiones de las opciones gráficas, como 'ggplot', 'lattice', # 'grid', y paquetes para trabajar con datos que necesitan paquetes de gráficos específicos, # como 'maps', 'raster', 'shape'. # Bueno, por acá queda un muestrario de objetos, procedimientos y funciones básicas que # podemos ir usando y combinando para trabajar en R. #### Instalación de paquetes #### # Finalmente vamos a pasar al tema de los paquetes, instalando los que van a ser utilizados # en el curso: seewave, tuneR, soundgen, bioacoustics y monitoR # install.packages('seewave', 'tuneR', 'soundgen', 'bioacoustics', 'monitoR')