Gracias por visitar esta página. Vamos a aprender las bases de programación en R y cómo relacionarlo con estadística y Machine Learning. R  es un dialecto del lenguaje S. S es un lenguaje creado por John Chambers en 1976 en los laboratorios Bell Labs. Fue implementado como una serie de librerías Fortran. En 1988 el sistema fue reescrito con C y lo hizo más portable y veloz. El libro más emblemático de este lenguaje es el libro blanco, o Statistical models in S. R usa la versión 4 de S. La idea básica del lenguaje es poder usarlo de manera interactiva y centrarse en su uso sin necesidad de saber programar en el. R fue creado por New Zeland, Ross Ihaka y Robert Gentleman. En 1995 se hizo software libre con la licencia GPL.

R está dividido en dos partes: el sistema base y todos sus complementos. La funcionalidad de R se divide en paquetes y estos tendrán que ser cargados según demanda desde nuestro código. Únicamente el paquete base es cargado de manera automática. El repositorio CRAN tiene alrededor de 4000 paquetes para usar. También existen otros repositorios cómo Bioconductor y páginas de terceros. Pero CRAN tiene una calidad alta y comprobada. 

Pero manos a la obra!

Entrada y salida estándar

Cada entrada que realizarmos en el promt de R se llaman expresiones. Primero vamos a ver el operador de asignación. Veamos ejemplos de cómo asignar valores a una variable. 

> x <- 1 # Esto es un comentario
> print(x)
[1] 1
> x # Evaluar la expresión
[1] 1
> texto <- "hola mundo"
> texto
[1] "hola mundo"

Como vemos los tipos de las variables son dinámicas y en la declaración se asignan de manera automática por el intérprete. La forma en la cuál se realizan comentarios es con el símbolo de gato (#). La primer expresión al imprimir estas variables, significa que es el primer elemento, ya que podría almacenar más, al ser todos vectores. Introducir la variable al promt sola o haciendo print, se llama evaluar la expresión. 

Ahora vamos a crear una secuencia con un vector:

> x <- 1:30
> x [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [26] 26 27 28 29 30

Podemos ver ahora que el vector es impreso en la pantalla y el marcador [26] únicamente aparece si existe un final de línea y aún no se ha terminado de imprimir todo el vector. Este nos ayuda a tener un panorama general de cuántos elementos tenemos en el vector. 

Objetos

R tiene cinco tipos básicos de objetos atómicos, que son: caracteres, números (reales), enteros, complejos y lógicos. El objeto más básico es un vector, donde cada vector únicamente pude contener objetos de su mismo tipo, pero una lista que es representado como un vector puede contener objetos de diversas clase, y es por eso que se usan con regularidad. Un vector vacío puede ser creado con la función vector() y tiene dos argumentos, la clase y el objeto. Pero veamos un poco más de cerca los números: los números en R son tratados como reales de doble presición, pero si se requiere un entero se debe agregar un sufijo L al mismo, por ejemplo el número 9L. También existen números especiales cómo Inf que representa al infinito. El valor NaN es un valor indefinido que se obtiene con casos cómo 0/0. 

 Cada objeto puede tener atributos. Atributos comunes son: nombres, dimensiones, clases, tamaño, etc. Ver qué atributos tiene un objeto se puede hacer con la función attributes(). 

Tipos de datos

Para crear vectores R cuenta con la función c(). Un uso común es especificar directamente los valores que se quiere, como vamos a mostrar a continuación:

> x <- c(TRUE,FALSE)
> x
[1]  TRUE FALSE
> x <-c(0.1,0.4)
> x
[1] 0.1 0.4
> x <- c("a","b","c")
> x
[1] "a" "b" "c"
> x <- c(1+0i,2+1i)
> x
[1] 1+0i 2+1i
> x <- vector("numeric", length=5) > x [1] 0 0 0 0 0

En el ejemplo anterior se muestran los diferentes tipos de datos con que cuenta R y cómo se asignan en forma de vector. También es posible asignar un vector directamente con la función vector() cómo se muestra en la última asignación. 

Ahora bien, si se mezclan tipos de datos en un único vector no se generará un error, sino que se obliga al cambio del tipo de variable para todos los elementos del vector. En el código de abajo se muestran unos pocos ejemplos.  

> y
[1] "0.4" "x"
> y <- c(TRUE,10)
> y
[1]  1 10
> y <- c("x",FALSE)
> y
[1] "x"     "FALSE"
> 

Ahora bien, se puede obligar a un vector ser interpretado cómo. A continuación mostramos cómo un vector puede ser interpretado como diferentes objetos básicos de R. 

> x
[1] 0 0 0 0 0
> class(x)
[1] "numeric"
> as.logical(x)
[1] FALSE FALSE FALSE FALSE FALSE
> as.character(x)
[1] "0" "0" "0" "0" "0" 

 No todas las operaciones de conversión arrojan resultados. Por ejemplo de caracteres a números no es posible obtener valores y se expresará en el vector cómo NA, al igual de caracteres a lógicos. 

Pero las listas si pueden mezclar tipos de objetos. Veamos un ejemplo.

> x <- list("a",1,FALSE,1+1i)
> x
[[1]]
[1] "a"

[[2]]
[1] 1

[[3]]
[1] FALSE

[[4]]
[1] 1+1i

Matrices 

Los matrices son una forma de vectores, pero además tienen el atributo dimensión. En este caso vamos a crear una matriz 2x4 de la siguiente forma.  

> m <- matrix(nrow=2, ncol=4)
> m
     [,1] [,2] [,3] [,4]
[1,]   NA   NA   NA   NA
[2,]   NA   NA   NA   NA
> dim(m)
[1] 2 4

La dimensión se obtiene con la función dim(). En la pantalla se puede ver la forma que adquiere la matriz y se imprime en pantalla. Ahora vamos a asignar valores a una matriz de la siguiente forma. 

> m <- matrix(31:38, nrow=2,ncol=4)
> m
     [,1] [,2] [,3] [,4]
[1,]   31   33   35   37
[2,]   32   34   36   38
>

La cantidad de datos especificada debe corresponder a la cantidad de datos que pueden ser almacenados en la matriz, en otras palabras debe ser un submúltiplo del número de filas de la matriz. 

 También es posible crear matrices directamente agregando el atributo dimensión a un vector: 

> a = c(1,4,3,10,2,0)
> a
[1]  1  4  3 10  2  0
> dim(a) <- c(2,3)
> a
     [,1] [,2] [,3]
[1,]    1    3    2
[2,]    4   10    0

También podemos hacer juntando hileras o columnas. Veamos el ejemplo de cómo se hace:

> a <- c(1,2,3)
> b <- c(9,8,7)
> m <- cbind(a,b)
> m
     a b
[1,] 1 9
[2,] 2 8
[3,] 3 7
> m <- rbind(a,b)
> m
  [,1] [,2] [,3]
a    1    2    3
b    9    8    7 

Como pudimos apreciar, con dos vectores creados con anterioridad es posible juntarlos en una matriz ya sea tomándolos como columnas (cbind) o cómo hileras (rbind). 

Factores

Son usados para representar datos categóricos y pueden ser ordenados o no ordenados. Los factores son vectores con una etiqueta por cada entero. Esto es muy útil ya que los factores tiene la capacidad de describirse a sí mismos. Vamos a crear un ejemplo un factor para mostrar cómo funcionan en la práctica:

 > x <- factor(c("Hombre", "Mujer", "Hombre", "Hombre", "Mujer"))
> x
[1] Hombre Mujer  Hombre Hombre Mujer
Levels: Hombre Mujer
> table(x)
x
Hombre  Mujer
     3      2
> unclass(x)
[1] 1 2 1 1 2
attr(,"levels")
[1] "Hombre" "Mujer"

Los vectores es convertido en factor. Este factor tiene etiquetas basadas en las entradas que obtuvo. En este caso son dos: hombres y mujeres. Si usamos la función tabla() entonces mostraremos el número de repeticiones por etiqueta. La función unclass permite separar los elementos del factor en dos vectores, uno con los valores y el segundo con las etiquetas. 

Data Frames

Los data frames son usados para guardar datos tabulares, y son formas especiales de listas donde cada elemento de esta lista debe de tener el mismo tamaño. A diferencia de las matrices, las data frames pueden almacenar valores de diferentes tipos de clases en cada columna. También es importante saber que tienen un atributo especial, llamado, row.names que almacenan las etiquetas de cada hilera. Son creadas a partir de datos externos de manera común con las funciones read.table() y read.csv() y pueden ser convertidos en matrices con data.matrix(). 

Si bien es común cargar estos tipos de datos desde datos externos, vamos a mostrar cómo generarlos desde la terminal:

> c = c("Aves", "Peces", "Felinos")
> c
[1] "Aves"    "Peces"   "Felinos"
> b
[1] 9 8 7
> x<-data.frame(especies=c, animales=b)
> x
  especies animales
1     Aves        9
2    Peces        8
3  Felinos        7 

Como podemos ver cada columna tiene un nombre, y se le asigna directamente desde la creación del data frame. Mas adelante veremos cómo cargar datos desde un archivo. 

Nombres

Todos los datos en R pueden tener nombres. No únicamente los data frames. Para ejemplificar esto, vamos a ver el siguiente código, donde primero se genera un vector. Con la función names() podemos comprobar que este vector no tiene nombres. Pero generamos otro vector con los nombres para cada dato, podemos agregar este vector como nombres del primer vector x. 

> x <- 30:32
> x
[1] 30 31 32
> names(x)
NULL
> asistentes <- c("Lunes","Martes","Miércoles")
> names(x) <- asistentes
> x
    Lunes    Martes Miércoles
       30        31        32
> names(x)
[1] "Lunes"     "Martes"    "Miércoles"

Ahora al imprimir el valor de x aparecen los nuevos valores de nombres. También la función names() ahora muestra los nombres con los cuales se asocian los valores del vector. Pero también las listas pueden tener nombres. 

> l <- list(uno=1,dos=2,tres=3)
> l
$uno
[1] 1

$dos
[1] 2

$tres
[1] 3

También se puede asignar nombres a las columnas y las hileras de las matrices. Esto se hace con la función dimnames(), y un ejemplo se muestra a continuación. 

> m <- matrix(c(1,4,2,10), nrow=2, ncol=2)
> m
     [,1] [,2]
[1,]    1    2
[2,]    4   10
> dimnames(m) <- list(c("x","y"), c("a","b"))
> ,m
Error: inesperado ',' in ","
> m
  a  b
x 1  2
y 4 10

 Leer datos

 Para leer datos en R se tienen diversas funciones. Primero tenemos read.table() y read.csv() y son basante comúnes. Leen datos de archivos de texto. También readLine() lo hace sobre este tipo de archivos. La función source() carga archivos R, que también pueden contener datos. Otras funciones para cargar datos son unserialize() y load(). También existen funciones para escribir datos: write.table(), writeLines(), dump(), dput(), save(), serialize().

Interesantes son también las funciones dput() y dget(). En esta se guarda en un archivo de texto todos los metadatos de los objetos que queremos almacenar. 

> x <- data.frame(a=100,b="hola")
> x
    a    b
1 100 hola
> dput(x)
structure(list(a = 100, b = structure(1L, .Label = "hola", class = "factor")), .Names = c("a",
"b"), row.names = c(NA, -1L), class = "data.frame")
> dput(x, file="x.R")
> new.x <- dget("x.R")
> new.x
    a    b
1 100 hola

Pero estas funciones únicamente nos permiten almacenar un objeto en un archivo. Para guardar varios objetos vamos a requerir las funciones dump() y source(). Estas pueden guardar los objetos al enumerar su nombre en un vector de cadenas. 

> x<-"Hola mundo"
> y<-data.frame(a=c(1,2),b=c("a","b"))
> x
[1] "Hola mundo"
> y
  a b
1 1 a
2 2 b
> dump(c("x","y"), file="data.R")
> rm(x,y)
> source("data.R")
> y
  a b
1 1 a
2 2 b
> x
[1] "Hola mundo"

Subconjuntos 

Para el análisis de datos es importante obtener subconjuntos de nuestros datos. Para ellos, R tiene diversos operadores. [ siempre regresa un objeto de la misma clase que el original. Puede ser elegido para seleccionar más de un elemento. [[ es usado para extraer elementos de una lista o data frame, pero únicamente puede extraer un elemento y el tipo de clase que regresa no es necesariamente el mismo que el origen. Por último $ es usado para extraer elementos de una lista o de un data frame. 

Veamos las formas en que se pueden realizar estos sub conjuntos. 

> x <- c("a","b","c","d","a")
> x[1]
[1] "a"
> x[1:3]
[1] "a" "b" "c"
> x[x > "b"]
[1] "c" "d"
> y <- x > "b"
> y
[1] FALSE FALSE  TRUE  TRUE FALSE
> x[y]
[1] "c" "d"

La primer forma es tomar un elemento con base en su índice. Pero también se puede extender esto usando un rango de valores lo cual devolverá varios valores. A su vez es posible extraer datos del vector con una condición. Con una condición también se puede extraer un vector de bools que especifique en que lugar se cumple y en cuál no la condición.  

Subconjuntos de Listas

Tomar subconjuntos de listas es diferente que hacerlo de vectores.  En este caso vamos a ver que existen diversas formas de acceder a los datos en una lista. La primera es con un sub índice normal [], el cuál nos dará el contenido de la n-esima posición de la lista. Pero esto también puede ser accedido directamente usando el símbolo $ y el nombre de la lista. Abajo también se muestra el uso de [[]].

> x <- list(a=1:5,b=1.2)
> x
$a
[1] 1 2 3 4 5

$b
[1] 1.2

> x[1]
$a
[1] 1 2 3 4 5

> x$b
[1] 1.2
> x[["b"]]
[1] 1.2
> x["b"]
$b
[1] 1.2

Para extraer elementos precisos de nuestra lista podemos hacerlo con un vector que indique las posiciones de los elementos. 

> x <- list(a=1:3,b=1.2,c="hola mundo")
> x[c(1,3)]
$a
[1] 1 2 3

$c
[1] "hola mundo" 

Una última nota interesante para este punto es que usando la consola es posible referirse a un elemento de una lista sin tener que especificar su nombre completo. R buscará el elemento más parecido de la lista y lo desplegará. Por ejemplo:

> x <- list(aloalo=1)
> x$a
[1] 1

Matrices

Las matrices son obvias para hacer subconjuntos. Podemos acceder a sus elementos con un doble [x,y] de la forma en que se muestra:

> x <- matrix(1:6, 2,3)
> x
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> x[1,2]
[1] 3
> x[1,]
[1] 1 3 5
> x[,2]
[1] 3 4
>

 En el primer caso, podemos acceder a un elemento de la matríz especificando el lugar que se encuentra con [x,y]. Si queremos acceder a una hilera lo hacemos con [x,] y si es el caso de una columna lo hacemos con x[,y]. Pero los elementos que obtenemos no son matrices, si no vectores. Para cambiar esto, podemos usar el argumento drop y ponerlo en falso:

> x[,2, drop=FALSE]
     [,1]
[1,]    3
[2,]    4

De esta manera, lo que obtenemos es nuevamente una matriz y no un vector. 

Eliminar valores faltantes

Comúnmente cuando se leen valores de datos, existen datos faltantes expresados cómo NA. Se puede eliminar estos datos de un vector de la siguiente manera: 

> x <- c(1,NA,2,NA)
> x
[1]  1 NA  2 NA
> eliminar <- is.na(x)
> x[!eliminar]
[1] 1 2 

complete.cases() por el otro lado nos permite tomar únicamente los elementos completos de todos los vectores que se le proporcione. 

Operaciones vectoriales

Una vez que hemos mostrado cómo usar en lo general los tipos de datos y en concreto los vectores en R veamos algunas operaciones que es posible realizar con estos vectores. 

La suma de vectores se puede realizar de manera bastante simple. 

> A <- 1:3
> B <- 2:4
> A+B
[1] 3 5 7
> C <- 3:8
> A+C
[1]  4  6  8  7  9 11

La multiplicación también puede ser realizada entre vectores al igual que la multiplicación:

> A*B
[1]  2  6 12
> A/B
[1] 0.5000000 0.6666667 0.7500000

Lo mismo puede ser usado para matrices:

> A <- matrix(1:4, 2,2)
> B<-matrix(5:8,2,2)
> A
     [,1] [,2]
[1,]    1    3
[2,]    2    4
> B
     [,1] [,2]
[1,]    5    7
[2,]    6    8
> A+B
     [,1] [,2]
[1,]    6   10
[2,]    8   12

Hay que especificar que las operaciones son elemento por elemento. No son en estricto sentido operaciones matriciales o vectoriales matemáticamente. Para hacer una verdadera multiplicación de matrices se usará %*%. Mostramos la diferencia:

> A*B
     [,1] [,2]
[1,]    5   21
[2,]   12   32
> A%*%B
     [,1] [,2]
[1,]   23   31
[2,]   34   46

 

Gracias por leer este breve tutorial práctico. Si bien únicamente se trataron temas básicos sobre el manejo de datos, esto es fundamental para su uso posterior. Espero sigan leyendo las siguientes entregas de este tutorial. Si hay correcciones, sugerencias o comentarios serán bienvenidas para mejorar este artículo. 


Estructuras de Control. 

Al igual que en otros lenguajes de programación, las estructuras de control en R permiten controlar el flujo de la ejecución de un programa. Las estructuras más comunes son if, else; for; while; repeat; break; next; y return. La mayoría de estructuras de control no son usadas en las sesiones interactivas, pero son usadas al escribir funciones o expresiones más complejas. 

if

Comencemos con la sentencia if. Se usa para hacer una pregunta lógica y tomar decisiones. En el siguiente ejemplo asignaremos un valor a la variable x y preguntaremos si este valor es mayor a 5. Si es cierto, entonces asignaremos 1 a y, de lo contrario se le asignará 0. Posteriomente imprimiremos este valor en la pantalla. 

> x <- 10
> if(x>5){
+ y <- 1
+ } else{
+ y <- 0
+ }
> print(y)
[1] 1

Esto es equivalente a la siguiente construcción, donde es posible manejar a un if con una salida que se redirigirá con el operador <-. En el ejemplo siguiente se hace un equivalente al ejemplo anterior, con esta nuevo formato. 

> x <- 10 
> m <- if(x > 5){
+ 1
+ } else {
+ 0
+ }
> print(m)
[1] 1

for

La próxima estructura a revisar es for. Esta es bastante común para crear ciclos y toma una variable de iteración y asigna susecivamente valores de una secuencia de valores. Son muy usados para iterar sobre elementos de una lista, vector u otros objetos. 

> for(i in 1:5){
+ print(i)
+ }
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

Cómo se ha descrito, esto es importante si se toma en cuenta una lista o un objeto sobre el cuál interar:

[1]  3  6  4 14  3  2  4
> b <- 0
> for ( i in a){
+ b<-b+i
+ }
> b
[1] 22

También es posible iterar sobre el índice de los elementos de la lista:

> a
[1]  1  4  2 12  1  0  2
> for(i in 1:7){
+ print(a[i])
+ }
[1] 1
[1] 4
[1] 2
[1] 12
[1] 1
[1] 0
[1] 2

También es posible evitar usar las llaves, pero la instrucción debe de ponerse en la misma línea que el for

> for(i in 1:5) print(i)
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

Para determinar el tamaó del arreglo sobre el que vamos a iterar usamos la función seq_along de la siguiente forma:

> for (i in seq_along(a)){
+ print(a[i])
+ }
[1] 1
[1] 4
[1] 2
[1] 12
[1] 1
[1] 0
[1] 2

 Por otro lado, si queremos iterar sobre una matriz necesitamos un for anidado. 

> x <- matrix(c("a","b","c","d","e","f"),2,3)
> x
     [,1] [,2] [,3]
[1,] "a"  "c"  "e"
[2,] "b"  "d"  "f"
> for(i in seq_len(nrow(x))){
+   for(j in seq_len(ncol(x))){
+     print(x[i,j])
+   }
+ }
[1] "a"
[1] "c"
[1] "e"
[1] "b"
[1] "d"
[1] "f"

 while

Estos ciclos prueban una condición al iniciar y no terminan hasta que se incumpla la condición especificada. La acción al interior del cuerpo del while se repetirá todas las veces hasta que se se termine el ciclo. 

> c <- 0
> while(c < 5){
+ print(c)
+ c <- c + 1
+ }
[1] 0
[1] 1
[1] 2
[1] 3
[1] 4

La instrucción repeat ejecuta un ciclo infinito. Pero para terminar un ciclo es posible también usar la instrucción break, que sirve para interrumpirlo. Esta instrucción no únicamente sirve para finalizar repeat, si no cualquier otro ciclo. Damos un ejemplo de cómo funcionan:

> x <- 10
> repeat{
+ if (x < 5){
+ break
+ } else{
+ x <- x - 1
+ print(x)
+ }
+ }
[1] 9
[1] 8
[1] 7
[1] 6
[1] 5
[1] 4

Una instrucción semejante es next, que nos permitirá interrumpir el ciclo actual, y pasar a la siguiente iteración

> for(i in 1:10){
+ if(i<5){
+ print("bajo")
+ next
+ }
+ print(i)
+ }
[1] "bajo"
[1] "bajo"
[1] "bajo"
[1] "bajo"
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10

 Funciones en R

 Las funciones permite unir una serie de instrucciones y ahorrar críticamente. Usualmente estas funciones son escritas en un archivo específico, en vez de agregarlo directamente a la línea de comandos. 

Vamos a crear primer un archivo R que se llame exp.r que contenga la función. Posteriormente vamos a guardar ese archivo y cargarlo desde R con la función source() al espacio de trabajo interactivo. 

suma <- function(x,y){
  x+y
}

Para llamar a la función será necesario:

> source("exp.r")
> suma(12,3)
[1] 15

Ahora bien, intentaremos hacer lago un poco más difícil. Esta vez realizaremos una función de media, que no es la más óptima, por mucho. Sin embargo, mostraremos una versión que ejemplifica una función más compleja. Agregamos a nuestro archivo exp.r

mymean <- function(x){
  val <- 0
  for (i in x){
    val <- val + i
  }
  val / length(x)
}

Y lo importamos a la sesión:

> x <- c(1,10,2,20,5)
> source("exp.r") > diez(x,4)

 

 

 

Share This