Capitulo 3 Conceptos base en R

El objetivo de este tutorial es familiarizarnos con los conceptos básicos de R. ¿Qué es un objeto en R? ¿Con qué clases/tipos de objetos se trabaja en R?

A lo largo de este tutorial aprenderemos a usar R como una calculadora, a definir vectores y operar con ellos; a crear matrices, listas y data frames; a seleccionar elementos, añadir filas y columnas, etc. Como lo que se pretende es que se entienda la filosofía y la práctica del trabajo con R, todos los conceptos que se introducen se ilustran con ejemplos muy sencillos. No obstante, la selección de funciones que se realiza en este tutorial tiene una aplicación directa en el tratamiento real de datos.

3.1 Primeros pasos

Vamos a hacer este ejercicio para ir incorporando ciertos conceptos de R. Vamos a crear algunos vectores, que es digamos, la unidad base de R. No os apuréis, es algo sencillo y en seguida definiremos mejor lo que es un vector:

1+3
## [1] 4
99 / 3^2
## [1] 11
(1+3)*99 / 3^2
## [1] 44
a  <-  1+3  
a      # "a" es un vector de un solo componente, que es 4
## [1] 4
b  =  99 / 3^2 
b      # "b" es un vector de un solo componente, que es 11
## [1] 11
a*b    # hemos hecho una multiplicación entre dos vectores
## [1] 44
ejem  <-  c(a,b,a*b) # hemos creado un vector
ejem
## [1]  4 11 44
mean(ejem)
## [1] 19.66667
med <- mean(ejem)
med
## [1] 19.66667
round(med, digits = 2)
## [1] 19.67
round(mean(ejem), digits = 2)
## [1] 19.67

R utiliza funciones para realizar operaciones. Una función es, por ejemplo, mean(). Para utilizar una función deben especificarse unos argumentos, que es lo que escribimos dentro de los paréntesis. En el caso de la función round() hemos especificado dos argumentos: el vector que queremos redondear (ejem) y el número de decimales del redondeo (digits).

El símbolo “<-” es el operador para asignar. También se puede utilizar “=” , aunque es preferible utilizar el “<-”.

El símbolo “#” se utiliza para introducir un comentario. Todo lo que quede a la derecha de “#” no se ejecutará.

Cuando se realiza una asignación se obtiene un objeto. Podemos ver el resultado o contenido de un objeto de varias formas. Por ejemplo, para ver qué es el objeto a podemos escribir en la consola:

  • a
  • print(a)
  • (a <- 3+4)

También lo podemos ver en el panel de entorno del escritorio de RStudio (arriba a la derecha en Rstudio por defecto).

3.2 Calculadora en R

Como hemos visto, el uso más básico de R sería utilizarlo como una calculadora científica. Veamos las bases:

2 + 2,5

# > 2 + 2,5
# Error: inesperado ',' in "2 + 2,"

Nos ha dado un error porque R ttiliza “.” como separador decimal y no “,”. Tendremos que recordar esto cuando carguemos bases de datos de Excel, que muchas veces vienen con el separador equivocado para R :).

por lo tanto, debemos escribir:

2 + 2.5
## [1] 4.5

Estas son algunas de las operaciones básicas:

  • Suma: ‘+’
  • Resta: ‘-’
  • Multiplicación: ’*’
  • División: ‘/’
  • Potencia: ‘^’
  • Cociente entero: ‘%/%’
1+2
## [1] 3
4-1
## [1] 3
2*3
## [1] 6
2/3
## [1] 0.6666667
2^2
## [1] 4
7%/%3
## [1] 2
2*3+5/2
## [1] 8.5

El uso de los paréntesis para indicar la precedencia de las operaciones es el natural.

2*(3+5/2)
## [1] 11
2*((3+5)/2)
## [1] 8
2^3+4
## [1] 12
2^(3+4)
## [1] 128

¡Cuidado! No podemos omitir el signo ’*’ en las multiplicaciones.

2(3+5)
# Error: tentativa de aplicar una no-función

¿Qué ocurre si olvidamos un paréntesis?

2*(3+5
   
# > 2*(3+5
# + 

Este tiene solución. R cree que falta algo y por ello te muestra el signo ‘+’. Inserta “)” y funcionará.

En cambio en el caso opuesto R no sabe qué hacer :).

2*3+5)

# > 2*3+5)
# Error: inesperado ')' in "2*3+5)"

Otras operaciones matemáticas comunes son:

  • Logaritmo natural: ‘log()’
  • Logaritmo base 10: ‘log10()’
  • Raíz cuadrada: ‘sqrt()’
  • Exponente natural: ‘exp()’
  • Etc.

Otras funciones básicas son:

  • ‘round(x)’ redondea ‘x’. Si queremos redondear a cierto número de decimales, por ejemplo 2 decimales, usaremos ‘round(x, digits=2)’.
  • ‘floor(x)’ redondea ‘x’ a un número entero por defecto, dando el mayor número entero menor o igual que ‘x’.
  • ‘ceiling(x)’ redondea x a un número entero por exceso, dando el menor número entero mayor o igual que ‘x’.
  • ‘trunc(x)’ da la parte entera de ‘x’, eliminando la parte decimal: es lo que se llama truncar ‘x’ a un entero.

3.3 Qué es un vector

Un vector es el objeto más sencillo de R. La mayoría de operaciones y funciones en R trabajan de forma vectorial. Veamos que puede querer decir esto.

Para crear un vector se utiliza la función c().

a = c(9,-7,3,1,0,-2)
a     # a es un vector con 6 componentes
## [1]  9 -7  3  1  0 -2
b <- 1:6
b     # ":" crea un vector que resulta de la secuencia entre el primer número y el segundo. 
## [1] 1 2 3 4 5 6

¿Qué resultado espero de estas operaciones?

a*2
## [1]  18 -14   6   2   0  -4
a - mean(a)
## [1]  8.3333333 -7.6666667  2.3333333  0.3333333 -0.6666667 -2.6666667
a - b
## [1]  8 -9  0 -3 -5 -8

¿Lo entendemos? Veamos otro ejemplo:

a = c(1,2,2,1)
b <- c(3,2,1)

¿Qué longitud tienen los vectores “a” y “b”? Aquí la respuesta está clara, pero en aplicaciones reales utilizaríamos la función length().

length(a)   # esta función os será muy útil, conviene recordarla.
## [1] 4
length(b)
## [1] 3

Los vectores no tienen la misma longitud, entonces. ¿Cuál será el resultado de a + b?

a + b
## Warning in a + b: longitud de objeto mayor no es múltiplo de la longitud de uno menor
## [1] 4 4 3 4

R nos da un mensaje de aviso (warning), no es lo mismo que un error. Nos avisa que hay algo que no cuadra pero realiza la operación que nosotros queremos. ¿Qué ha hecho? Yo tendría cuidado cuando obtenemos un aviso o “warning”.

Una cuestión muy importante que siempre tenemos que tener en cuenta cuando trabajamos con vectores es que en un vector sólo podemos concatenar elementos del mismo tipo. Eston son algunos de los tipos/clases de elementos (o datos) más comunes en R:

  • Carácter
  • Numéricos
  • Enteros
  • Lógicos
a <- c(1,2,3,4)    # creamos el vector "a"
class(a)           # devuelve el tipo de objeto
## [1] "numeric"
b <- c("X","Y")
class(b)
## [1] "character"
c <- c(TRUE, FALSE, T, F)    # en general, puede escribirse TRUE/FALSE o T/F
class(c)
## [1] "logical"

Y como hemos dicho, un vector no puede contener más de un tipo/clase de elemento. Si combinamos elementos pasa o siguiente:

x <- c(1,2,"a")
y <- c(FALSE, 1)
z <- c("a",T)
class(x)
## [1] "character"
class(y)
## [1] "numeric"
class(z)
## [1] "character"

R ha forzado a que todos los elementos del vector sean del mismo tipo.

En ocasiones somos nosotros los que estamos interesados en forzar que todos los elementos del vector sean del mismo tipo (esto es la explicit coercion). Para ello utilizamos las funciones as.numeric() , as.character(), as.logical() … Si el resultado no tiene sentido R producirá un mensaje de error o warning. Un ejemplo:

as.numeric(x)
## Warning: NAs introducidos por coerción
## [1]  1  2 NA
as.character(x)
## [1] "1" "2" "a"

R no ha sabido dar un valor numerico a “a”, por lo que nos ha dado un “NA”, o “Not Available” (No Disponible).

Por último, veamos cómo seleccionamos elementos de un vector. Para seleccionar/acceder a un elemento de un vector se suelen emplear []. Veamos como se hace:

a <- c(7,3,5,7,-2,-9,0)
a[1]   # seleccionamos el primer elemento de "a"
## [1] 7
a[-1]  # seleccionamos todos los elementos de "a" excepto el primer elemento
## [1]  3  5  7 -2 -9  0
a[3]   # seleccionamos el tercer elemento de "a"
## [1] 5
a[c(1,6)]  # seleccionamos el primer y sexto elemento de "a"
## [1]  7 -9
b <- c(2,4,6)
a[b]   # seleccionamos elementos de "a" en base a "b"
## [1]  3  7 -9
a[-b]  # seleccionamos todos los elementos de "a" excepto los indexados por "b"
## [1]  7  5 -2  0

3.4 Tipos de objetos

3.4.1 Matrices y Arrays

La función matrix() permite organizar los datos en una matriz con tantas filas y columnas como se indiquen.

x <- matrix (data= c(1,2,3,4), nrow=2, ncol=2) # o x <- matrix (c(1,2,3,4), nrow=2, ncol=2)
x
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4

Observamos que se ha creado una matriz de 2x2 (2 filas y 2 columnas) y, además, muy importante, ¿cómo se ha completado la matriz? ¡Por columnas! Si queremos que se rellene por filas hay que incluir el argumento byrow en los argumentos de la función.

y <- matrix (c(1,2,3,4), nrow=2, ncol=2, byrow=T)
y
##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4

aunque también podíamos haber omitido el argumento relativo al número de filas o de columnas, porque conocida una dimensión R completaría la matriz dados los datos con los que se trabaja.

y <- matrix (c(1,2,3,4), 2, byrow=T)  # no especificamos nrow porque por defecto es el primer argumento después de los datos
y
##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4

También podemos asignar nombres a filas y columnas.

y <- matrix (c(1,2,3,4), nrow=2, ncol=2, byrow=T, dimnames=list(c("X1","X2"),c("Y1", "Y2")))
y
##    Y1 Y2
## X1  1  2
## X2  3  4

Para añadir o modificar el nombre de filas y columnas de una matriz se hace uso de las funciones colnames() y rownames(). También se puede utilizar la función dinmanes(), como en el ejemplo anterior y asignar nombres a través de una lista (ver el punto 5.2 de este tutorial).

y <- matrix (1:8, nrow=4)
y
##      [,1] [,2]
## [1,]    1    5
## [2,]    2    6
## [3,]    3    7
## [4,]    4    8
colnames(y) <- c("Variable 1", "Variable 2")
rownames(y) <- c("obs1","obs2","obs3","obs4")
y
##      Variable 1 Variable 2
## obs1          1          5
## obs2          2          6
## obs3          3          7
## obs4          4          8

Y podemos comprobar la dimensión de una matriz con esta otra función:

dim(y) ## primero número de filas y luego número de columnas
## [1] 4 2

3.4.1.1 Añadir filas/columnas

Dos funciones muy útiles, se utilizan muchísimo, cuando se trabaja con matrices (o vectores o dataframes) son rbind y cbind. La función rbind permite añadir filas, la función cbind permite añadir columnas. Vamos a ver cómo se utilizan.

Creamos dos objetos, uno será una matriz y el otro un vector.

x <- matrix(c(1,2,3,4),2,2)
y <- c(5,6)
x
##      [,1] [,2]
## [1,]    1    3
## [2,]    2    4
y
## [1] 5 6

Si ahora queremos añadir, por filas, los datos contenidos en el objeto ‘y’ al objeto ‘x’ entonces haremos:

rbind(x,y)
##   [,1] [,2]
##      1    3
##      2    4
## y    5    6

y si queremos añadir los datos de ‘y’ a los de ‘x’ por columnas:

cbind(x,y)
##          y
## [1,] 1 3 5
## [2,] 2 4 6

Cuidado!! El número de filas (o columnas) del objeto que añadimos (objeto y) debería ser múltiplo del número de filas (o columnas) del objeto al que se añaden los datos (objeto ‘x’). R nos dará un aviso (Warning), pero no un error. R realiza la operación de esta manera:

x <- c(4,5)
y <- c(10,11,12)
rbind(x,y)
## Warning in rbind(x, y): number of columns of result is not a multiple of vector length (arg 1)
##   [,1] [,2] [,3]
## x    4    5    4
## y   10   11   12

En algunas ocasiones que ahora pareceran incompresibles, querremos crear vectores o matrices vacías:

x <- c()           # crea un vector vacío
x
## NULL
y <- matrix(nrow=3, ncol=4)    # crea una matriz vacía
y
##      [,1] [,2] [,3] [,4]
## [1,]   NA   NA   NA   NA
## [2,]   NA   NA   NA   NA
## [3,]   NA   NA   NA   NA

3.4.1.2 Seleccionar elementos de una matriz

Para seleccionar elementos de una matriz utilizamos el símbolo de los corchetes: [,]. El primer valor se referirá a la fila y el segundo a la columna.

¿Qué valores obtendremos de esta matriz?

M <- matrix(1:16,4,4,byrow = T)
M
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12
## [4,]   13   14   15   16

Con esto:

## [1] 7
##      [,1] [,2]
## [1,]    2    4
## [2,]    6    8
##      [,1] [,2] [,3]
## [1,]    2    3    4
## [2,]    6    7    8
## [3,]   10   11   12
## [1] 1 2 3 4
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
##      [,1] [,2]
## [1,]    2    3
## [2,]    6    7
## [3,]   10   11
## [4,]   14   15
##      [,1] [,2] [,3] [,4]
## [1,]    5    6    7    8
## [2,]   13   14   15   16

Comprobadlo vosotros mismos en la consola.

Ahora, veamos como extraer los valores de la tercera columna que sean mayores que 10. Será muy útil la función which().

idx = which(M[,3] > 10)
idx
## [1] 3 4
M[idx,3]
## [1] 11 15

Hay otras muchas maneras de hacer esto que iremos viendo más adelante en el curso :).

3.4.2 Listas

A diferencia de los vectores o matrices, las listas pueden contener elementos/componentes de distinto tipo. Observemos esta lista que tiene 3 componentes (pueden ser matrices, vectores, dataframes, otras listas, etc.).

l1 <- list(0:8, "Curso", F)
l1
## [[1]]
## [1] 0 1 2 3 4 5 6 7 8
## 
## [[2]]
## [1] "Curso"
## 
## [[3]]
## [1] FALSE

Para acceder a elementos de una lista usaremos [[]]

l1[[1]]
## [1] 0 1 2 3 4 5 6 7 8
l1[[2]]
## [1] "Curso"

Veamos otro ejemplo

l2 <- list( Cliente = c("Juan", "Mary", "Carla"), Edad =c(68,34,13), Capital = c(48000, 12000, 123000))
l2
## $Cliente
## [1] "Juan"  "Mary"  "Carla"
## 
## $Edad
## [1] 68 34 13
## 
## $Capital
## [1]  48000  12000 123000

Fijémonos en la diferencia de presentación de las listas “l1” y “l2”. Como en la lista “l2” hemos nombrado los componentes, estos aparecen al ejecutar el objeto precedidos del símbolo $. Ahora también podemos acceder a un componente de la lista por su nombre.

l2$Cliente
## [1] "Juan"  "Mary"  "Carla"
l2[[1]]
## [1] "Juan"  "Mary"  "Carla"

Y también podemos acceder a valores dentro de estas componentes.

l2[[1]][2]
## [1] "Mary"
idx = which(l2$Capital>50000)
l2$Cliente[idx]
## [1] "Carla"

También podemos realizar operaciones con listas.

l2$Capital * 10
## [1]  480000  120000 1230000
median(l2$Capital)
## [1] 48000

3.4.3 Data Frame

Los data.frame se usan para almacenar datos en forma de tablas (filas y columnas), como estamos habituados en Excel y/o similares. Los data.frame se componen de diferentes vectores, que pueden almacenar objetos/datos de distinto tipo: numérico, carácter, etc. y son almacenados por columnas. Recordad que en las matrices todos los elementos tienen que ser enteros o numéricos.

Los data.frame pueden entenderse como un tipo especial de lista donde cada elemento de la lista tiene que tener la misma longitud. Cada elemento de la lista sería una columna y la longitud de cada elemento de la lista serían las filas.

Generalmente los data.frame los creamos al cargar/leer una base de datos (veremos más adelante), pero ahora vamos crear una data.frame para ver su estructura.

data <- data.frame(Cliente =  c("Juan", "Mary", "Carla"), Edad =c(68,34,13), Capital = c(48000, 12000, 123000))
data
##   Cliente Edad Capital
## 1    Juan   68   48000
## 2    Mary   34   12000
## 3   Carla   13  123000

Junto con los data.frame, os voy a introducir la función str(), que hace referencia a estructura, y names() que ya os podéis imaginar para qué sirve :).

names(data)
## [1] "Cliente" "Edad"    "Capital"
str(data)
## 'data.frame':    3 obs. of  3 variables:
##  $ Cliente: chr  "Juan" "Mary" "Carla"
##  $ Edad   : num  68 34 13
##  $ Capital: num  48000 12000 123000

Comentemos lo que vemos

Para acceder a los elementos de un data.frame utilizamos los símbolos $, como en las listas, o [,], como en las matrices. Si queremos seleccionar la variable ‘Cliente’ del objeto data (que es un data frame):

data$Cliente
## [1] "Juan"  "Mary"  "Carla"
data[,1]
## [1] "Juan"  "Mary"  "Carla"

y para seleccionar los dos primeros clientes de la data.frame:

data$Cliente[1:2]
## [1] "Juan" "Mary"

Si trabajamos con una data.frame, para no tener que acceder a una variable utilizando la expresión objeto$variable, puede usarse la función attach() sobre el objeto. Esto nos permitirá acceder directamente a las variables de la base de datos por su nombre.

attach(data)
## The following objects are masked from data2:
## 
##     Capital, Cliente, Edad
## The following objects are masked from data (pos = 19):
## 
##     Capital, Cliente, Edad
Edad
## [1] 68 34 13

Personalmente desaconsejo hacer esto porque si hacemos attach sobre dos data.frame con una misma variable, estaremos sobreescribiendo el objeto. Veamos que pasa.

data2 <- data.frame(Cliente = c("Altero"), Edad =c(63), Capital = c(4800000))
attach(data2)
## The following objects are masked from data (pos = 3):
## 
##     Capital, Cliente, Edad
## The following objects are masked from data2 (pos = 19):
## 
##     Capital, Cliente, Edad
## The following objects are masked from data (pos = 20):
## 
##     Capital, Cliente, Edad
Edad
## [1] 63

En cambio, si usamos $, no tendremos este problema:

data$Edad
## [1] 68 34 13
data2$Edad
## [1] 63

3.4.3.1 Crear nuevas variables en un data.frame

También podemos crear nuevas variables de la siguiente forma:

data$porcentaje_inversion = c(10,12,1)
data
##   Cliente Edad Capital porcentaje_inversion
## 1    Juan   68   48000                   10
## 2    Mary   34   12000                   12
## 3   Carla   13  123000                    1

¿Qué creéis que saldrá de esto?

data$X = data$porcentaje_inversion * data$Capital

Hacedlo vosotros a ver si lo habéis acertado :).

3.4.4 Factores

Los factores se utilizan para representar variables de naturaleza categórica y pueden ser ordenados o no ordenados. Si vamos a realizar un análisis de regresión puede ser conveniente guardar las variables categóricas como factores (R codificará internamente los distintos niveles del factor como enteros). Además, puede que sea de nuestro interés cambiar el orden de los niveles.

f <- factor(rep(c("Malo","Medio","Bueno"),3))
levels(f)
## [1] "Bueno" "Malo"  "Medio"

Si en un análisis vamos a trabajar con el orden de los factores, tendremos que asegurarnos de que R internamente entienda lo que queremos que haga. Si usamos f como una variable ordenada, R internamente la ordena por orden alfabético:

f
## [1] Malo  Medio Bueno Malo  Medio Bueno Malo  Medio Bueno
## Levels: Bueno Malo Medio
as.numeric(f)
## [1] 2 3 1 2 3 1 2 3 1

¿Tiene sentido este orden?

Quizá fuera mejor algo que ordenase de malo a bueno o al revés. Para ello:

f <- factor(f, levels=c("Malo","Medio","Bueno"))  # reordenar los factores
as.numeric(f)
## [1] 1 2 3 1 2 3 1 2 3

3.4.5 Unos ejercicios

set.seed(123)
valores <- rnorm(1000,100,20)

Probemos a usar las funciones summary(), max(), min(), mean(), sd(), var().

Y recordad las funciones length() y which().

  • Cuantos valores contiene el vector valores
  • Elegid aquellos valores mayores que 100
  • Sacad el número total de valores mayores que 120
df <- data.frame(cliente = paste0(sample(LETTERS,1000,replace = T),
                                  sample(letters,1000,replace = T),
                                  sample(letters,1000,replace = T)),
                 valores = valores,
                 coste = rnorm(1000,80,30))

Recordad la función str(). Probad la función summary() sobre el df.

  • Dadme dos formas de elegir la variable cliente.
  • Elegid aquellos clientes que han costado más de 100.
  • Haced un sumario del valor que tienen aquellos clientes que han costado más de 100