Introducción a R para Ciencias Naturales
Jose Miguel Ponciano y Juan Pablo Gomez
2023-08-23
1 Introducción
R es un lenguaje de programación que inicialmente fue creado como un ambiente conveniente para el análisis estadístico y para realizar cálculos en grandes volúmenes. Es una plataforma de libre acceso y disponible para plataformas Windows, Mac o Linux. Tiene una amplia flexibilidad para la programación, que incluso programadores experimentados aprenden cosas nuevas todos los días. Adicionalmente, al ser una plataforma de libre acceso y de contribución libre, sus posibilidades son ilimitadas y sus capacidades mejoradas diariamente. A principios de los años 2000, era sencillo rastrear los cambios y las mejoras a este software. Actualmente, la comunidad ha crecido de tal manera y su uso es de carácter global, que es imposible en este momento mantenerse informado sobre todo lo que ocurre dentro de este ambiente. A pesar que la plataforma se actualiza casi que a diario con las contribuciones de los usuarios, estas contribuciones están almacenadas en la pagina web del proyecto: http://www.r-project.org}{http://www.r-project.org. En este sitio web, encontrarán información sobre la descarga, los paquetes y otra información útil para el manejo de R.
Como se darán cuenta, R, es un lenguaje de programación similar a Python, C++ o cualquier otro lenguaje similar. No es una aplicación como SPSS, por ejemplo, en la cual a través de simples “clicks”, el programa cumple con unas instrucciones. En el caso de R, las instrucciones se dan a través de la consola de comandos. Estos comandos están basados en un lenguaje de programación llamado “Fortran” que fue evolucionando hasta convertirse en este lenguaje. Así como el aprendizaje de cualquier idioma, aprender a programar en R tiene una curva de aprendizaje bastante elevada. Una persona aprendiendo a hablar o escribir en inglés, al comienzo solamente puede traducir palabras sueltas. Con el tiempo, el léxico se incrementa y la persona puede escribir frases gramaticalmente correctas. Cuando la persona domina el idioma, se espera que pueda mantener conversaciones fluidas en cualquier tema, escribir ensayos enteros y tener una fluidez relativamente alta. El aprendizaje de R se da en la misma manera. Al principio, solamente conocerán algunos comandos y con el transcurso del tiempo ese conocimiento permitirá utilizar los comandos sueltos en scripts enteros que permiten recrear análisis complejos. Nuestro objetivo en este documento es ayudar a los estudiantes a que superen el inicio de esa curva de aprendizaje relativamente rápido y sigan su aprendizaje hasta convertirse en programadores expertos.
En tiempos más recientes, la comunidad ha creado nuevos software que permiten hacer la interfaz con R bastante más amigable. Por ejemplo, hoy en día, la mayoría de los usuarios de R, utilizan R Studio, el cual mantiene todas las capacidades de R, mantiene los beneficios de la programación libre pero facilita algunas tareas que pueden volverse tediosas en el manejo de R. Para este curso, utilizaremos R Studio el cual, en nuestra opinión apoya en esa disminución de la curva de aprendizaje. Para descargar R Studio pueden ir a la página web http://www.r-studio.com}{http://www.r-studio.com. En la página podrán encontrar versiones de R Studio que requieren una membresía, pero nosotros utilizaremos el paquete básico en el cual es de libre acceso.
2 Estructura de R
Para iniciar con el aprendizaje de la programación en R, comenzaremos con las capacidades básicas. Para el siguiente ejercicio, deben ingresar a R Studio, asegurarse que este cargado. La consola de R Studio esta dividida por lo general en cuatro paneles. Uno con la consola de comandos, uno con la información de los objetos cargados, uno con un documento en blanco (el cual usaremos en el futuro cercano para guardar nuestro código) y el ultimo en el que pueden acceder a información de ayuda, sobre los paquetes instalados, los gráficos generados durante la sesión, etc…
Iniciaremos entonces con las capacidades básicas de R. Esencialmente, R puede ser utilizado como una calculadora. Por ejemplo si van a la consola y escriben
21+2
## [1] 23
Si escriben 21*2 entonces R retorna el resultado de esta operación. Si escriben:
21*-2
## [1] -42
La anterior operación ilustra la jerarquía en las operaciones en R.
Al escribir 21*-2
, en realidad R calculó
21*(-2)
. Esto también ilustra que a veces, comandos que
parecen incorrectos o incluso hasta “estúpidos” pueden resultar útiles
al momento de programar. Para aprender R hay que equivocarse y es
necesario intentar para equivocarse.
2.1 Objetos y tipos de objetos en R
R es un lenguaje de programación orientado en los objetos. Cada vez que se hace cualquier calculo, podemos guardar su resultado con un nombre. El resultado entonces se convierte en un objeto que se puede recuperar o se puede alterar por medio de otras operaciones. Esto es conveniente cada vez que hagamos operaciones complejas y no queremos reescribir todas las instrucciones cada vez. Para asignar un nombre al cálculo, utilizamos el signo “<” seguido por “-”, el cual se asemeja a una flecha apuntando hacia la izquierda. Intuitivamente, esto significa que asigne cualquier operación que tengamos a la derecha el nombre que tengamos en la izquierda. Retomando el ejemplo anterior, hubiéramos podido escribir:
A<-21
B<-21+2
B
## [1] 23
En este caso estamos almacenando cada una de las operaciones. Para
continuar con el ejemplo anterior, ¿como almacenarían al operación
21*-2
? ¿Que pasa si reemplazan el símbolo
<-
con un =
?
Como verán, hay muchas formas de escribir la misma operación en R y por lo tanto el lenguaje utilizado por cada persona es una firma autentica de cada uno, asi como cada escritor tiene una forma de escribir diferente.
Ahora que sabemos que son los objetos, podemos empezar a entender los tipos de objetos posibles en R. Una respuesta sencilla para esta sección, es que el tipo de objetos es infinito. Cada programador puede definir el tipo de objeto, pero R en general se basa en los siguientes tipos: Vectores, Arrays, Data Frames y Listas. Con estos cuatro tipos de objetos, se pueden construir una infinidad de estructuras que permiten realizar cálculos, guardar, organizar, etc.
2.1.1 Vectores
Los vectores almacenan objetos del mismo tipo concatenados. Existen 3 tipos de vectores
2.1.2 Vectores Numéricos
y<-18
y1<-c(-3.6,1.58)
y2<-1:9
Todas las anteriores son formas de escribir vectores de uno o más elementos. Existen otras formas de crear vectores más complejos que veremos cuando aprendamos sobre las funciones.
2.1.3 Vectores de Caracteres
En algunas ocasiones, los caracteres son importantes para incluir en nuestros análisis. Por ejemplo, en un experimento en donde tenemos niveles como “Control”, “Tratamiento1” y “Tratamiento2” es útil tener el la base de datos los caracteres directamente y no tener que acordarse que 0 corresponde a “Control” y así sucesivamente.
z<-"Control"
z1<-c("Control","Tratamiento")
2.1.4 Vectores lógicos
Los vectores lógicos consisten de datos binarios, generalmente del tipo “verdadero” y “falso”. La creación de los vectores lógicos es sencilla pues R automáticamente entiende T y F como verdadero y falso.
vector.logico<-c(TRUE,FALSE,TRUE)
vector.logico1<-c(T,F,T)
vector.logico
## [1] TRUE FALSE TRUE
vector.logico1
## [1] TRUE FALSE TRUE
Como pueden ver, R entiende que T
y F
son
abreviaciones de TRUE
y FALSE
, y por lo tanto
se pueden usar intercambiablemente.
2.1.5 Manipulación de Vectores
Los vectores de cualquier tipo, corresponden entonces a una colección de caracteres que pueden ser accedidos de diferentes formas. Como lo hemos visto anteriormente, al escribir el nombre del objeto al cual se le ha asignado un vector, el programa automáticamente retorna el resultado. Esto va a ser cierto, no solo para vectores pero para objetos de cualquier tipo. Mas adelante aprenderemos a acceder y utilizar objetos de diferentes tipos. Por el momento, es necesario entonces entender la estructura interna del programa.
Un vector corresponde a la colección unidimensional de unos objetos.
En R, la forma de acceder a una posición particular de un vector es
necesario adicionar []
al nombre del objeto que contiene el
vector. Por ejemplo, supongan que tenemos el siguiente vector con 10
posiciones:
vector10<-1:10
Si quieren acceder a la primera posición entonces se debe escribir
vector10[1]
## [1] 1
Si por el contrario, quieren acceder a la posición 10 entonces se debe escribir
vector10[10]
## [1] 10
Es posible también acceder a varias posiciones del vector utilizando
vectores en si entre los símbolos []
. Por ejemplo, en caso
de querer acceder a las posiciones 1 y 2 o 9 y 10, la forma más sencilla
de hacerlo sería de la siguiente manera
vector10[1:2]
## [1] 1 2
vector10[9:10]
## [1] 9 10
De acuerdo con lo aprendido hasta el momento sobre la creación de vectores numéricos, ¿pueden acceder solo a las posiciones 1 y 5 del vector10?
En general existen cuatro formas de acceder a las pocisiones de los vectores. La primera, la cual ya aprendimos anteriormente es llamando las pocisiones deseadas. La segunda, por medio de la exclusión de algunas posiciones. De tal manera que si yo quiero acceder a todas las posiciones del vector10, excepto la 1 y 2, entonces
vector10[-(1:2)]
## [1] 3 4 5 6 7 8 9 10
Noten que en este caso incluí un paréntesis separando el vector 1,2 del signo “-” para indicar que las dos posiciones del vector son negativas. En este caso, R entenderá que debe excluir las posiciones 1 y 2. De igual forma a la selección de pocisiones no contiguas, es posible excluir posiciones no contiguas utilizando vectores.
La tercera forma en la cual se puede acceder a las pocisiones de
vectores es utilizando vectores lógicos como los aprendimos
anteriormente. Los vectores lógicos se traducen numéricamente a
FALSE = 0
y TRUE = 1
por lo que podemos
pedirle a R que acceda a las posiciones que queramos por medio de
vectores lógicos dentro del operador []
Por ejemplo:
vector10[c(T,T,T,T,F,T,F,T,F,T)]
## [1] 1 2 3 4 6 8 10
Alternativamente, podemos pedirle a R que solamente extraiga las
posiciones de un vector que cumplan con una condición de un argumento
lógico. Para esto, debemos entender primero las operaciones lógicas. En
este caso tenemos los operadores <
, >
y
==
. Noten que este último es diferente de =
,
pues si recuerdan de instrucciones anteriores, =
es
equivalente a <-
y por lo tanto su uso se destina a
designación de objetos y no a operadores lógicos.
Volviendo al tema de los operadores lógicos, podemos preguntarle a R por las posiciones de un vector que cumplan una condición particular. Por ejemplo, queremos saber cuales posiciones del vector son mayores que 5.
vector10>5
## [1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
O, alternativamente queremos saber que posición contiene el número 5.
vector10==5
## [1] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
Noten que en estos dos ejemplos, el resultado de la operación es un vector lógico indicando con T las posiciones del vector10 que cumplen con la condición particular y con F las que no. Esta información nos permite acceder a las posiciones del vector10 que cumplen con esas condiciones que estamos imponiendo. Por ejemplo, para ver solamente las entradas del vector10 que son mayores que 5 entonces la sintaxis se realiza de la siguiente forma:
vector10[vector10>5]
## [1] 6 7 8 9 10
Finalmente, cuando cada posición de un vector tiene un nombre diferente, podemos acceder a los elementos a traves de los nombres de las posiciones.
Supongan que tenemos el siguiente vector
primates<-1:5
Le podemos asignar un nombre a cada posición del vector
primates
de esta forma
names(primates)<-c("Homo sapiens","Saguinus oedipus","Lagotrix lagotricha"
,"Cebus apella","Cebus capuchinus")
primates
## Homo sapiens Saguinus oedipus Lagotrix lagotricha Cebus apella
## 1 2 3 4
## Cebus capuchinus
## 5
De este vector solamente quiero obtener las posiciones que corresponden a Homo sapiens y a Saguinus oedipus.
primates[c("Homo sapiens","Saguinus oedipus")]
## Homo sapiens Saguinus oedipus
## 1 2
Existen algunos vectores predefinidos en R. Entre estos se encuentran los vectores con las letras del abecedario.
LETTERS
## [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
## [20] "T" "U" "V" "W" "X" "Y" "Z"
letters
## [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
## [20] "t" "u" "v" "w" "x" "y" "z"
Estos vectores predefinidos pueden ser útiles a la hora de utilizarlos en operaciones más complejas.
2.1.6 Breve introducción a las funciones y su uso en la creación de vectores
Hasta el momento hemos trabajado con la definición manual de
vectores. Sin embargo, como cualquier otro Software, R posee algunas
funciones que también vienen preestablecidas y que pueden ser de
utilidad en el momento de definir vectores. Para esto, definamos primero
qué es una función. Una función en R es un objeto especial que se
utiliza para realizar operaciones. Las funciones están definidas por
argumentos, que le indican a R la operación a realizar según la función.
Verán que en este caso, el objeto es seguido por un par de paréntesis y
dentro de los paréntesis están los argumentos de la función. Una función
comúnmente utilizada para la creación de vectores esta contenida con el
comando seq
. Aquí podemos introducir también la forma de
acceder automáticamente a la ayuda de R desde la consola de comandos.
Para obtener información sobre la función de generación de secuencias
pueden escribir
?seq
Como pueden la ayuda de R es muy útil pues tiene toda la información
necesaria para utilizar la función especificada. Inicia con una breve
descripción de la función y luego describe la forma de uso, los
argumentos, algunos detalles, el resultado (Value) retornado por el uso
de la función. Finalmente, incluye algunas referencias en las cuales
esta basada la función, algunas sugerencias sobre funciones similares y
en la mayoría de los casos termina con Ejemplos de como usarla. En el
caso de la función seq
, existen muchas formas de generar
secuencias numéricas con un número de argumentos que le dan una amplia
funcionalidad. Si queremos generar una secuencia de los números 1 al 10
utilizando esta función entonces escribimos
seq(from=1,to=10,by=1)
## [1] 1 2 3 4 5 6 7 8 9 10
Alternativamente, si queremos generar un vector que contenga números entre el 1 y el 10 pero que el vector tenga una longitud de 20 posiciones entonces podemos escrbir
seq(from=1,to=10,length.out=20)
## [1] 1.000000 1.473684 1.947368 2.421053 2.894737 3.368421 3.842105
## [8] 4.315789 4.789474 5.263158 5.736842 6.210526 6.684211 7.157895
## [15] 7.631579 8.105263 8.578947 9.052632 9.526316 10.000000
R automáticamente divide la secuencia entre los números 1 y 10 en partes iguales, para cumplir con el objetivo asignado que es retornar un vector con 20 posiciones.
2.1.7 Como saber si un vector es de un tipo o de otro.
Todos los objetos en R guardan información sobre su naturaleza. En
ese sentido, es posible preguntarle a R sobre el tipo de un objeto en
particular. Hasta el momento solo conocemos los vectres, pero hemos
visto que los vectores pueden ser de varios tipos. Tenemos vectores
numéricos, vectores de caracteres y vectores lógicos. Existen otro tipo
de vectores como los factores, que utilizaremos más adelante cuando
estemos definiendo los tipos de datos para los modelos. Veamos entonces
como podemos acceder a la información sobre el tipo de objeto que
tenemos. Arriba definimos vectores numéricos como vector10
,
vectores de caracteres como z1
o vectores lógicos como
vector.logico
. Podemos acceder al tipo de objeto utilizando
la función class()
class(vector10)
## [1] "integer"
class(z1)
## [1] "character"
class(vector.logico)
## [1] "logical"
2.1.8 Ejercicios
- Crear un vector que contenga la secuencia de todos los números impares entre el 1 y el 11.
- Crear un vector que contenga la secuencia de todos los números pares entre el 2 y el 1000
- Declarar un vector en el que las posiciones tengan los nombres Pitangus sulphuratus, Ortalis garrula, Chrysuronia goudoti y Picumnus cinamommeus
- Declarar un vector que contenga la secuencia de los números desde el 1 hasta el 20 y seguidamente acceder solamente a las posiciones que tienen los números impares.
- Con el mismo vector del punto 4, acceder solamente a las posiciones que continen números mayores que 10.
2.1.9 Arrays
Los Arrays son conjuntos de vectores organizados en n-dimensiones. Por ejemplo, los vectores que trabajamos en la sección anterior son simplemente Arrays que tienen una sola dimensión. Una matriz es un Array de dos dimensiones; la primera con las filas y la segunda con las columnas. Es importante tener en cuenta, los objetos dentro de todas las posiciones de un Array o de una matriz deben ser del mismo tipo. Es decir, todos los objetos deben ser númericos, de caracteres o lógicos, pero no es posible combinar tipos de objetos dentro de un mismo Array.
Para definir un array, R tiene la función array()
. Sin
embargo, debido a que las matrices son el tipo de array más utilizado,
el lenguaje, también ha dispuesto una función diferente para definir
matrices.
Supongamos que en un experimento, estamos contando la cantidad de aves que encontramos en tres puntos preestablecidos en el campus de la Universidad del Norte. Este conteo lo realizamos el lunes de la primera semana del semestre y lo repetimos el viernes con el objetivo de entender como cambia el número de aves en un punto de acuerdo con el día de la semana. Dado que realizamos 3 conteos en 2 días diferentes entonces tenemos un vector con tres datos.
y<-c(18,32,15,11,12,19)
En este caso, no es muy útil tener la información organizada en un
vector pues queremos comparar observaciones del mismo punto en dos días
diferentes. En este caso es mucho más util organizar la información en
una matriz, bien sea de 3 filas y 2 columnas o viceversa. Utilizando la
función array()
conteos<-array(data=y,dim=c(3,2))
conteos
## [,1] [,2]
## [1,] 18 11
## [2,] 32 12
## [3,] 15 19
Si observan, el argumento dim
nos permite especificar el
número de dimensiones que queremos en el array. En este caso, el vector
de dos posiciones le esta indicando a R que es un vector de dos
dimensiones con 3 posiciones en la dimensión 1 y 2 dimensiones en la
posición 2. Esto lo podemos verificar utilizando la función
dim()
dim(conteos)
## [1] 3 2
¿Que pasa si cambiamos la definición del argumento
dim
en la función array de c(3,2)
a
c(2,3)
?
Si miran detenidamente, R por defecto organiza los datos de tal forma que llena primero las columnas y luego llena las filas. En este sentido, se debe prestar particular atención a la forma en la que se ingresan los datos para no cometer errores. Para este ejemplo de los conteos de las aves, ingresamos los tres conteos del primer día en las tres primeras posiciones del vector y los tres conteos del segundo día en las tres ultimas posiciones.
Asumamos entonces que solamente queremos ver la información
consignada en el primer día de muestreo. En el caso de los vectores, si
queriamos consultar la información solamente en la primera posición,
entonces escribiamos [1]
. En el caso de los arrays de dos
dimensiones (i.e. matrices) debemos darle información a R sobre las
posiciones que queremos en cada una de las dimensiones separando las
posiciones en las dimensiones por comas [,]
. Supongamos que
queremos obtener solo la información de la primera fila y la primera
columna.
conteos[1,1]
## [1] 18
En R y en general en casi todos los lenguajes de programación, las dimensiones de una matriz estan definidas de tal manera que el primer número se refiere a las filas y el segundo a las columnas. Esto lo pueden notar arriba cuando definieron las dimensiones del array de formas diferentes. ¿Que pasa entonces si yo quiere acceder a los datos solamente del primer día?
conteos[,1]
## [1] 18 32 15
Al dejar la posición de las filas en blanco, R entiende que debe retornar todas los datos ingresados en todas las fila, pero solo la primera columna. ¿Como podrían ver los datos solamente del primer punto de conteo, pero de los dos días?
2.1.9.1 Asignando nombres a las filas y las columnas
Dado que en este caso estamos trabajando con datos de los conteos de las aves, y muy probablemente este sea le escenario al que se enfrentan muchos de ustedes en el momento de analizar sus datos, es conveniente asignar nombres a las filas y a las columnas. De esta manera, los datos estarán mejor descritos y evitamos confusiones. Vamos a crear entonces dos vectores, uno con el nombre de las filas, corresponientes a los tres puntos de conteo y uno con el nombre de las columnas correspondientes a lunes y viernes.
filas<-c("P1","P2","P3")
columnas<-c("Lunes","Viernes")
Luego, utilizamos las funciones colnames()
y
rownames()
para asignarle los nombres a cada una, pero
primero veamos cual es el resultado de aplicar estas funciones.
colnames(conteos)
## NULL
rownames(conteos)
## NULL
En este caso, las funciones estan extrayendo los objetos que
corresponden a los nombres de filas y de columnas. Para poder reemplazar
estos nombres con los que queremos, debemos entonces decirle a R que al
objeto colnames(conteos)
le asigne un vector particular. Lo
mismo con los nombres de las filas.
colnames(conteos)<-columnas
rownames(conteos)<-filas
conteos
## Lunes Viernes
## P1 18 11
## P2 32 12
## P3 15 19
2.1.9.2 Otra forma de definir matrices
Asi como lo mencionamos anteriormente, las matrices son simplemente
un caso especial de los arrays. Sin embargo, por la utilidad, R ha
definido una función particular para declarar matrices. Por ejemplo,
podemos crear una matriz idéntica a la creada utilizando la función
array()
pero utilizando la función matrix()
.
Para ver los argumentos de esta función pueden escribir
?matrix
directamente en la consola.
conteos1<-matrix(y,nrow=3,ncol=2)
Si vieron en la ayuda, esta función tiene otros argumentos y uno muy
útil como por ejemplo el byrow=FALSE
. Este argumento le
dice a R que llene primero todas la matriz primero por columnas o
primero por filas. Comparen por ejemplo el resultado de
conteos1
con lo siguiente:
conteos2<-matrix(data=y,nrow=3,ncol=2,byrow=TRUE)
conteos2
## [,1] [,2]
## [1,] 18 32
## [2,] 15 11
## [3,] 12 19
¿Cual es la diferencia?
Adicionalmente de la misma manera le asignamos nombres a las filas y
las columnas de la matriz conteos
, podemos asignarle
nombres a las filas y columnas de las matrices conteos1
y
conteos2
.
Algo que puede resultar muy útil al momento de generar matrices o arrays, es que R por defecto llena todas las posiciones solicitadas con la información disponible. Así, en una matriz de 10x10 en donde todas las posiciones son 0, en vez de generar un vector con cien 0´s, se puede abreviar de la siguiente manera:
zeros<-matrix(data=0,nrow=5,ncol=3)
zeros
## [,1] [,2] [,3]
## [1,] 0 0 0
## [2,] 0 0 0
## [3,] 0 0 0
## [4,] 0 0 0
## [5,] 0 0 0
Finalmente, es posible reemplazar posiciones dentro de una matriz o un vector que hayamos creado con anterioridad. Por ejemplo, supongamos la matriz de zeros corresponde a una matriz en blanco que queremos llenar. Esto corresponde a los conteos de el número de estudiantes que asistieron a las clases de 5 cursos del departamento de química y biología al inicio, en la mitad y al final del semestre. Como estmos hasta ahora iniciando semestre, solo tenemos los datos de el inicio.
estudiantes.inicio<-rep(x=25,times=5)
Como en todos los cursos en al inicio del semestre asistieron 25
estudiantes, fue posible tomar ventaja de la función rep()
para generar un vector del número 25 repetido 5 veces. Ahora, para
ingresar los datos en nuestra matriz de zeros, debemos reemplazar la
primera columna con nuestro vector estudiantes.inicio
zeros[,1]<-estudiantes.inicio
zeros
## [,1] [,2] [,3]
## [1,] 25 0 0
## [2,] 25 0 0
## [3,] 25 0 0
## [4,] 25 0 0
## [5,] 25 0 0
Sin embargo, es una mala practica mantener nuestro nombre zeros, pues el objeto, por un lado ya no es un objeto lleno de zeros y por otro lado, ya tenemos datos sobre nuestro experimento que estamos realizando. Recuerden que en este caso, las filas corresponden a los diferentes cursos y las columnas a los diferentes momentos en los que se midió la asistencia a cada curso. A continuación entonce por favor:
- Renombrar el objeto zeros con un nombre que este relacionado con el experimento realizado.
- Completar la matriz con los datos que ustedes se imaginan pueden obtener a lo largo del semestre para cada una de las 3 mediciones de asistencia.