Primero vamos a establecer un directorio de trabajo. Establecer una ruta de trabajo es un paso práctico más no necesario, que nos va a permitir importar archivos utilizando solo su nombre y extensión (ruta relativa) en lugar de la ruta completa (ruta absoluta). No obstante, siempre podemos usar las rutas completas si tenemos archivos en otras carpetas y simplemente no queremos crear copias que ocupen espacio.
setwd("D:/Daniel/Documents/Introducción a R") # Si usamos el forward slash (/) va a funcionar
setwd("D:\\Daniel\\Documents\\Introducción a R") # Si usamos el backslash (\) con secuencia de escape va a funcionar
setwd("D:\Daniel\Documents\Introducción a R") # Si usamos el backslash (\) sin secuencia de escape no va a funcionar
Para asignarle valor a una variable solo tenemos que utilizar el
símbolo de «asignación» (Var <- Valor). También
podríamos hacerlo con el símbolo de igual (Var = Valor),
pero por convención no se usa porque el igual está reservado para otro
tipo de operaciones. Una variable puede contener un único valor, una
serie de valores en una dimensión (vector), una serie de valores en dos
dimensiones (matrices o marcos de datos), o un objeto en general
(listas, gráficas, rasters, árboles filogenéticos, entre otros).
# El nombre de una variable debe ser una única palabra, es decir, no puede contener espacios (se pueden separar palabras con guion bajo o puntos). Tampoco puede empezar con un número.
a <- 100
b <- "Hola mundo" # Todo lo que esté dentro de comillas dobles va a ser interpretado como texto
c <- '100' # También lo que esté dentro de comillas simples
d <- TRUE # A este tipo de variables se les llama lógicas o booleanas (TRUE/FALSE)
a
## [1] 100
b
## [1] "Hola mundo"
c
## [1] "100"
d
## [1] TRUE
# R siempre nos va a mostrar por defecto la posición de los elementos de una variable dentro de llaves, lo que nos es muy útil para ubicarnos e indexar (lo veremos más adelante), pero no hace parte del contenido de la misma.
Todas las anteriores son variables unidimensionales de un único
elemento, ahora vamos a crear vectores, que no son más que variables de
una sola dimensión pero que contienen múltiples elementos de un mismo
tipo. Para crear un vector usaremos la función c(),
separando cada elemento con una coma.
e <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
f <- c("México", "lindo", "y", "querido")
e
## [1] 1 2 3 4 5 6 7 8 9 10
f
## [1] "México" "lindo" "y" "querido"
En caso de que queramos una secuencia de números podemos hacer «trampa» para no tener que escribirlos uno por uno.
g <- 1:100 # El operador : nos permite crear secuencias numéricas consecutivas
h <- seq(0, 100, by = 5) # La función seq nos permite crear secuencias y establecer el intervalo
g
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
## [19] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
## [37] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
## [55] 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
## [73] 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
## [91] 91 92 93 94 95 96 97 98 99 100
h
## [1] 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90
## [20] 95 100
Ahora vamos a movernos a una segunda dimensión, en R existen dos tipos de objetos bidimensionales, las matrices y los marcos de datos (data frames, en inglés), ambos de apariencia similar pero con diferentes propiedades. En este caso crearemos una matriz, ya que más adelante vamos a cargar y a crear marcos de datos.
i <- matrix(data = 1:25, nrow = 5, ncol = 5, byrow = FALSE) # El valor por defecto siempre es FALSE
j <- matrix(data = 1:25, nrow = 5, ncol = 5, byrow = TRUE)
i # Cuando el argumento byrow = FALSE la matriz se llena de manera vertical
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 6 11 16 21
## [2,] 2 7 12 17 22
## [3,] 3 8 13 18 23
## [4,] 4 9 14 19 24
## [5,] 5 10 15 20 25
j # Cuando el argumento byrow = TRUE la matriz se llena de manera horizontal
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 2 3 4 5
## [2,] 6 7 8 9 10
## [3,] 11 12 13 14 15
## [4,] 16 17 18 19 20
## [5,] 21 22 23 24 25
# La diferencia fundamental entre una matriz y un marco de datos es que las matrices solo pueden contener un único tipo de datos (número, texto o lógicos), mientras que cada columna de un marco de datos puede contener objetos de diferentes tipos.
Finalmente, tenemos objetos en «tres» dimensiones a los que llamamos listas, las cuales en realidad no tienen propiamente tres dimensiones, pero como veremos más adelante en la sección de indexado, requeriremos de una coordenada adicional para poder acceder a sus elementos. Una lista es un objeto más complejo que los anteriores, que nos permite almacenar otros objetos independientemente de si estos son o no del mismo tipo.
k <- list(a, b, c, d, e, f, g, h, i, j)
k
## [[1]]
## [1] 100
##
## [[2]]
## [1] "Hola mundo"
##
## [[3]]
## [1] "100"
##
## [[4]]
## [1] TRUE
##
## [[5]]
## [1] 1 2 3 4 5 6 7 8 9 10
##
## [[6]]
## [1] "México" "lindo" "y" "querido"
##
## [[7]]
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
## [19] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
## [37] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
## [55] 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
## [73] 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
## [91] 91 92 93 94 95 96 97 98 99 100
##
## [[8]]
## [1] 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90
## [20] 95 100
##
## [[9]]
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 6 11 16 21
## [2,] 2 7 12 17 22
## [3,] 3 8 13 18 23
## [4,] 4 9 14 19 24
## [5,] 5 10 15 20 25
##
## [[10]]
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 2 3 4 5
## [2,] 6 7 8 9 10
## [3,] 11 12 13 14 15
## [4,] 16 17 18 19 20
## [5,] 21 22 23 24 25
De manera intuitiva, cuando creamos una variable y le asignamos un valor o valores tenemos una idea de qué tipo de datos son. Sin embargo, existe una función que nos permite validar fácilmente el tipo de nuestras variables, puesto que ciertas funciones solo reciben datos de entrada con tipos o formatos específicos.
class(a)
## [1] "numeric"
# R tiene dos
formas de almacenar números, los enteros (integer) y los
dobles (double = numeric). Los enteros son
números sin parte decimal y requieren de menos espacio para ser
almacenados, mientras que los dobles pueden ser tanto números enteros
como números decimales. Por defecto, todo número y todo cálculo se
almacena y procesa como doble.
Particularmente, cuando tenemos variables numéricas que queremos operar, tenemos que considerar las limitaciones informáticas de la maquina donde estemos trabajando y las limitaciones de R. Por ejemplo, por defecto, R solo muestra en pantalla 7 digitos.
pi # El comando pi nos permite usar el número π
## [1] 3.141593
Pero eso no significa que π tenga solo 6 decimales ni que los cálculos se hagan con el número redondeado. Para R, los cálculos se hacen con hasta 15 decimales de precisión (si es que el número los tiene). Por otro lado, si quisiéramos saber cuáles son los máximos números que nuestra máquina puede almacenar, podemos utilizar la siguiente función.
data.frame("Max Integer" = .Machine$integer.max,
"Min Double" = .Machine$double.xmin,
"Max Double" = .Machine$double.xmax)
## Max.Integer Min.Double Max.Double
## 1 2147483647 2.225074e-308 1.797693e+308
Volviendo al tema, la validación del tipo podemos hacerla para cada una de las variables, pero como somos listos y un poco flojos, vamos a anidar funciones para que R nos arroje un único resultado en forma de tabla.
data.frame("Var" = ls(), # La función ls nos da un listado de todas las variables en el ambiente de trabajo
"Class" = rbind(class(a), # La función rbind combina los elementos a manera de filas (su análoga es cbind)
class(b),
class(c),
class(d),
class(e),
class(f),
class(g),
class(h),
class(i),
class(j),
class(k)))
## Var Class.1 Class.2
## 1 a numeric numeric
## 2 b character character
## 3 c character character
## 4 d logical logical
## 5 e numeric numeric
## 6 f character character
## 7 g integer integer
## 8 h numeric numeric
## 9 i matrix array
## 10 j matrix array
## 11 k list list
# Un objeto
puede tener más de una clase, por ejemplo, las matrices suelen ser de
tipo matrix y array.
En R es posible cargar conjuntos de datos desde fuentes externas y en diferentes formatos, pero la manera más fácil y universal de hacerlo es mediante texto delimitado. El texto delimitado es un archivo de texto plano sin formato, donde existe un caracter que nos delimita entre columnas (comas, puntos y comas, espacios, tabulaciones, entre otros).
Para este ejemplo comenzaremos por cargar un conjunto de datos que contiene errores intencionados, pero que reflejan los errores más comunes que hacen que nuestros datos queden mal cargados y empecemos a tener problemas a la hora de procesarlos.
data <- read.csv("D:/Daniel/Documents/Introducción a R/Datos (malos).csv")
dim(data) # La función dim nos sirve para validar las dimensiones de nuestro marco de datos (filas x columnas)
## [1] 32 1
¿Cuántas filas y columnas esperamos encontrar? ¿Tiene sentido con lo que R nos está diciendo?
Si revisamos nuestros datos en un editor de texto, podemos notar que
las columnas se encuentran separadas por punto y coma (;) haciendo que R
no pueda entender cómo están estructurados. Por defecto, la función
read.csv() asume el formado inglés (coma como separador de
columnas y punto para la posición decimal de los números), pero estos
datos fueron exportados desde Excel en español (punto y coma como
separador de columnas y coma para la posición decimal de los números).
Por lo tanto, tenemos que especificar dentro de la función cuál es
nuestro separador de columnas y cual nuestro separador decimal.
data <- read.csv(file = "D:/Daniel/Documents/Introducción a R/Datos (malos).csv",
header = TRUE, # Este argumento nos sirve para indicar si los datos tienen encabezado
sep = ";", # Aquí indicamos cuál es el separador de columnas
dec = ",") # Aquí indicamos cuál es el separador decimal
dim(data)
## [1] 32 5
Si abrimos nuestros datos ya sea mediante la consola o directamente en R Studio, podremos ver que ahora sí están estructurados a manera de tabla y que se ven aparentemente normales. Pero estos datos aún tienen algunos errores. ¿Pueden encontrarlos a simple vista?
# Pista: Hay tres tipos de errores en los datos.
head(data) # La función head nos muestra solo las primeras 6 filas de la tabla
## País Estado Area Superficie Población
## 1 México Chihuahua 247455 12.6 3741869
## 2 México Sonora 179355 9.2 2944840
## 3 México Coahuila de Zaragoza 151562 7.7 3146771
## 4 México Durango 123317 6,3 1832650
## 5 México Oaxaca 93757 4,8 4132148
## 6 México Tamaulipas 80249 4,1 3527735
tail(data) # La función tail nos muestra solo las últimas 6 filas de la tabla
## País Estado Area Superficie Población
## 27 Mexico Querétaro 11699 0,6 2368467
## 28 México Colima 5627 0,3 731391
## 29 México Aguascalientes 5616 0,3 1425607
## 30 México Morelos 4879 0,2 1971520
## 31 México Tlaxcala 4016 0,2 1342977
## 32 México Ciudad de México 1495 0,1 9209944
Cuando tenemos conjuntos de datos grandes, se hace extremadamente difícil encontrar los errores a simple vista, y si nuestros datos tienen errores, R nos va a comenzar a mostrar mensajes de error tras error, o en casos particulares, cálculos, pero malos. El problema es que, si no estamos familiarizados con los mensajes de error, o no somos muy diestros buscando en foros de internet, va a ser un verdadero dolor de cabeza. Los errores se pueden encontrar y corregir de manera programática (usando código), pero eso implica más tiempo y un conocimiento más profundo de R. Así que lo ideal es que nuestros datos estén curados usando buenas prácticas antes de importarlos.
Vamos a ver cómo podríamos identificar los errores que aún quedan, lo que va a ser muy sencillo porque yo los incluí intencionalmente así que sé cuáles son y cómo ubicarlos. ¿Pero y si no lo supiera?
unique(data$País) # La función unique nos permite ver los valores únicos de un elemento
## [1] "México" "México " "Mexico"
Como vemos, existen tres versiones de México cuando solo debería haber una, lo que va a derivar en duplicados cuando tengamos que agrupar. Este error es bastante común y se produce porque nos quedan espacios al final de las palabras o espacios dobles entre ellas, y al ser «invisibles» son muy difíciles de detectar, o porque algunas palabras quedan con o sin acentos.
Supongamos que ahora quisieramos hacer una operación muy sencilla con los datos, para lo que vamos a sumar el área, la proporción de superficie y la población para obtener el total de cada una.
sum(data$Area) # La función sum nos permite sumar todos los valores de un objeto (en este caso un vector)
## [1] 1961485
sum(data$Superficie)
## Error in sum(data$Superficie): 'type' (character) de argumento no válido
sum(data$Población)
## [1] 126014024
Como vemos, hemos obtenido un error a la hora de sumar la superficie. Este error nos dice algo así como que no se pueden sumar caracteres. ¿Pero no se supone que la superficie era un número?
A continuación, aplicaremos una función que ya habíamos usado, con el objetivo de validar de qué tipo son las columnas de nuestro marco de datos.
data.frame("Col" = colnames(data), # La función colnames nos da un listado con el nombre de las columnas del df
"Class" = c(class(data$País),
class(data$Estado),
class(data$Area),
class(data$Superficie),
class(data$Población)))
## Col Class
## 1 País character
## 2 Estado character
## 3 Area integer
## 4 Superficie character
## 5 Población integer
Lo que R nos está diciendo es que, en efecto, la columna de «Superficie» no es un número y por ende no se puede operar. ¿Pero por qué no es un número? Si revisamos de nuevo los datos con un poco más de cuidado podremos ver que existen números decimales separados con punto y otros con coma.
head(data)
## País Estado Area Superficie Población
## 1 México Chihuahua 247455 12.6 3741869
## 2 México Sonora 179355 9.2 2944840
## 3 México Coahuila de Zaragoza 151562 7.7 3146771
## 4 México Durango 123317 6,3 1832650
## 5 México Oaxaca 93757 4,8 4132148
## 6 México Tamaulipas 80249 4,1 3527735
Este error es muy común, particularmente si copiamos y pegamos datos de diferentes fuentes que tienen diferentes formatos, o si al cargar los datos el separador decimal no coincide con el que le indicamos a la función.
Ya que sabemos dónde y cómo no meter la pata, vamos a cargar los datos corregidos y a validar de nuevo para verificar que en efecto ya no tienen errores.
data <- read.csv(file = "D:/Daniel/Documents/Introducción a R/Datos (buenos).csv",
header = TRUE,
sep = ",",
dec = ".")
dim(data)
## [1] 32 5
unique(data$País)
## [1] "México"
sum(data$Superficie)
## [1] 99.9
data.frame("Col" = colnames(data),
"Class" = c(class(data$País),
class(data$Estado),
class(data$Area),
class(data$Superficie),
class(data$Población)))
## Col Class
## 1 País character
## 2 Estado character
## 3 Area integer
## 4 Superficie numeric
## 5 Población integer
El indexado significa en palabras simples referenciar (o extraer) una parte de un conjunto de datos mediante la posición de los elementos que nos interesan, es decir, usando coordenadas o en algunos casos, nombres. Según el tipo de objeto, la indexación se puede hacer de una o de varias formas, y para este ejercicio, vamos a reutilizar algunas de las variables que ya hemos creado.
Cuando indexamos usando coordenadas, siempre vamos a utilizar las
llaves de la siguiente forma: OBJETO[POSICIÓN], no importa
si es un vector, una matriz, un marco de datos, o una lista, lo que
cambia es lo que ponemos dentro o el número de llaves.
Vamos a comenzar por los vectores, que como ya sabemos tienen una
sola dimensión. Para ello vamos a invocar de nuevo a la variable
f, donde almacenamos las palabras que componen la frase
«México lindo y querido», para luego extraer solo las palabras «México»
y «querido».
f
## [1] "México" "lindo" "y" "querido"
En este caso, como es un vector muy corto es posible identificar a
simple vista que las palabras «México» y «querido» se encuentran en la
posición 1 y 4, respectivamente. Al ser un vector con una sola
dimensión, debemos ingresar la posición del elemento o elementos de
nuestro interés como VECTOR[X], donde X puede ser un valor
único o un vector de valores.
f[1] # Aquí indexamos solo el elemento en la posición 1
## [1] "México"
f[4] # Aquí indexamos solo el elemento en la posición 4
## [1] "querido"
f[c(1, 4)] # Aquí indexamos ambos elementos en las posiciones 1 y 4
## [1] "México" "querido"
Sin embargo, existe una función que nos ayuda a encontrar elementos específicos y que nos será de mucha utilidad cuando tenemos grandes conjuntos de datos. En este punto podemos detenernos también para introducir algunos operadores lógicos que nos permiten hacer filtros o establecer condiciones.
Por ejemplo, cuando queremos determinar si algo coincide exactamente
con un criterio de nuestro interés, vamos a utilizar el símbolo de igual
dos veces (A == B).
which(f == "México") # La función which nos muestra la posición de un elemento que coincida con un criterio
## [1] 1
which(f == "querido")
## [1] 4
Por el contrario, si queremos identificar algo que sea diferente,
podemos utilizar la negación del igual (A != B).
which(f != "México")
## [1] 2 3 4
Si queremos validar que algo coincida con dos o más condiciones podemos emplear el AND si nuestro criterio es excluyente, o el OR si no lo es.
which(f == "México" & f == "querido") # En R el AND se escribe &
## integer(0)
which(f == "México" | f == "querido") # Y el OR se escribe |
## [1] 1 4
Cuando tenemos números, los operadores mayor que
(A > B), mayor o igual que (A >= B),
menor que (A < B), o menor o igual que
(A <= B), nos serán de mucha ayuda. Por ejemplo, vamos a
indexar sobre la variable h donde tenemos una secuencia de
números de 5 en 5 hasta 100, buscando números menores o iguales a
50.
which(h <= 50) # which nos da una posición, no el valor, para obtener los valores debemos indexar las posiciones
## [1] 1 2 3 4 5 6 7 8 9 10 11
pos <- which(h <= 50) # Asignamos las posiciones a una variable
h[pos] # Indexamos las posiciones mediante la variable
## [1] 0 5 10 15 20 25 30 35 40 45 50
h[which(h <= 50)] # También podemos indexar directamente y ahorrarnos una variable
## [1] 0 5 10 15 20 25 30 35 40 45 50
# Los
operadores lógicos no están restringidos a la función
which(), pueden utilizarse dentro de cualquier otra siempre
y cuando tenga sentido usarlos allí.
Cuando indexamos también podemos omitir posiciones indicándolas con un signo menos antes del número o el vector de números. Por ejemplo, si por el contrario quiero los números mayores a 50 y conozco la posición de los menores o iguales, puedo decirle a R que omita estas últimas.
h[-pos] # Indexamos las posiciones mediante la variable pero con el signo - indicamos que las queremos omitir
## [1] 55 60 65 70 75 80 85 90 95 100
Ahora vamos a indexar elementos bidimensionales, como matrices o
marcos de datos. Para ello tendremos que utilizar de nuevo las llaves,
pero esta vez referenciando la posición de la o las filas y columnas que
nos interesan de la forma OBJETO[X, Y], donde X es
corresponde a las filas y Y a las columnas. Para el ejemplo,
utilizaremos la matriz i que contiene los números del 1 al
25 distribuidos en 5 filas y 5 columnas.
i
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 6 11 16 21
## [2,] 2 7 12 17 22
## [3,] 3 8 13 18 23
## [4,] 4 9 14 19 24
## [5,] 5 10 15 20 25
Al igual que con los objetos de una dimensión, aquí podemos indexar un valor único o una serie de valores mediante un vector, pero vamos a comenzar por lo simple. Supongamos que nos interesa únicamente el valor del centro, que en este caso, al tratarse de una matriz de 5 x 5 correspondería al elemento de la tercera fila y la tercera columna.
i[3, 3]
## [1] 13
¿Y que tal si lo que nos interesa es una serie de valores?
i[3, ] # Una opción es dejar vacía la dimensión que nos interesa (= todos los elementos)
## [1] 3 8 13 18 23
i[3, 1:5] # También podemos indicar un rango de valores
## [1] 3 8 13 18 23
i[3, c(1, 2, 3, 4, 5)] # O introducir todo el vector manualmente
## [1] 3 8 13 18 23
Al igual que las matrices, los marcos de datos también se pueden indexar utilizando las coordenadas X e Y del elemento o los elementos que nos interesen. Por ejemplo, si quisiéramos calcular la densidad poblacional del estado de Sonora tendríamos que dividir el valor de población de dicho estado sobre su área.
head(data)
## País Estado Area Superficie Población
## 1 México Chihuahua 247455 12.6 3741869
## 2 México Sonora 179355 9.2 2944840
## 3 México Coahuila de Zaragoza 151562 7.7 3146771
## 4 México Durango 123317 6.3 1832650
## 5 México Oaxaca 93757 4.8 4132148
## 6 México Tamaulipas 80249 4.1 3527735
data[2, 5] / data [2, 3] # Indexamos el valor de población para Sonora y lo dividimos por su área
## [1] 16.41906
# Cuando
hacemos operaciones matemáticas, R utiliza las reglas de prioridad
aritmética, de modo que, 5 + 5 * 5 = 30, mientras que,
(5 + 5) * 5 = 50.
Otra diferencia particular entre los marcos de datos y las matrices
es que, los primeros también permiten indexar columnas enteras usando el
nombre de estas, en cuyo caso el resultado sería un vector. Para ello lo
único que tenemos que hacer es utilizar el símbolo de pesos luego del
nombre de nuestra variable de tipo data.frame seguido del
nombre de la columna que nos interesa
(DATA.FRAME$NOMBRE_COLUMNA).
data$Población / data$Area # Aquí obtendríamos la densidad poblacional para todos los estados
## [1] 15.12141 16.41906 20.76227 14.86129 44.07295 43.95986
## [7] 106.22679 21.54692 10.80311 75.62068 112.25154 52.75045
## [13] 90.16214 55.67465 46.16280 81.03971 52.00933 16.14348
## [19] 41.56101 58.72123 191.89873 201.48112 44.34993 97.14925
## [25] 760.25314 148.12093 202.45038 129.97885 253.84740 404.08280
## [31] 334.40662 6160.49766
También podríamos crear una nueva columna para almacenar los datos, de modo que podamos utilizarlos luego.
data$Densidad <- round(data$Población / data$Area, 2) # La función round nos permite redondear a N cantidad de decimales
head(data)
## País Estado Area Superficie Población Densidad
## 1 México Chihuahua 247455 12.6 3741869 15.12
## 2 México Sonora 179355 9.2 2944840 16.42
## 3 México Coahuila de Zaragoza 151562 7.7 3146771 20.76
## 4 México Durango 123317 6.3 1832650 14.86
## 5 México Oaxaca 93757 4.8 4132148 44.07
## 6 México Tamaulipas 80249 4.1 3527735 43.96
Pero el indexado se puede hacer aún más «divertido», así que vamos a complejizar la forma como indexamos utilizando lo que hemos aprendido hasta ahora. Digamos que queremos saber cuáles son los estados que tienen más de 5 millones de habitantes.
data[which(data$Población >= 5000000), ] # El which nos dará la posición de las filas que cumplen la condición
## País Estado Area Superficie Población Densidad
## 7 México Jalisco 78588 4.0 8348151 106.23
## 10 México Chiapas 73311 3.7 5543828 75.62
## 11 México Veracruz 71826 3.7 8062579 112.25
## 13 México Nuevo León 64156 3.3 5784442 90.16
## 21 México Puebla 34306 1.7 6583278 191.90
## 22 México Guanajuato 30608 1.6 6166934 201.48
## 25 México Estado de México 22351 1.1 16992418 760.25
## 32 México Ciudad de México 1495 0.1 9209944 6160.50
Por último, nos queda aprender a indexar los elementos de una lista.
Como mencionamos anteriormente, las listas son algo así como objetos con
«tres» dimensiones, para lo que necesitaremos una coordenada adicional
de la forma LISTA[[Z]][X, Y], donde Z es equivalente a la
posición del elemento dentro de la lista y se debe escribir con doble
llave, mientras que, X e Y son las posiciones relativas que nos
interesan de un elemento listado según sus dimensiones (si el elemento
es unidimensional solo usamos X, si es bidimensional usamos X e Y).
Pero llevémoslo a la práctica para que quede más claro. Vamos a
reutilizar la lista k que habíamos creado para listar todas
las variables que fuimos generando al comienzo.
k
## [[1]]
## [1] 100
##
## [[2]]
## [1] "Hola mundo"
##
## [[3]]
## [1] "100"
##
## [[4]]
## [1] TRUE
##
## [[5]]
## [1] 1 2 3 4 5 6 7 8 9 10
##
## [[6]]
## [1] "México" "lindo" "y" "querido"
##
## [[7]]
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
## [19] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
## [37] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
## [55] 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
## [73] 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
## [91] 91 92 93 94 95 96 97 98 99 100
##
## [[8]]
## [1] 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90
## [20] 95 100
##
## [[9]]
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 6 11 16 21
## [2,] 2 7 12 17 22
## [3,] 3 8 13 18 23
## [4,] 4 9 14 19 24
## [5,] 5 10 15 20 25
##
## [[10]]
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 2 3 4 5
## [2,] 6 7 8 9 10
## [3,] 11 12 13 14 15
## [4,] 16 17 18 19 20
## [5,] 21 22 23 24 25
Como vemos, esta lista almacena números, cadenas de texto, vectores y matrices, y es lo que mencionamos anteriormente, una lista no está limitada a un mismo tipo de datos. De esta lista queremos extraer por ejemplo el texto «Hola mundo» del elemento en la posición 2, la palabra «lindo» del vector en la posición 6, y la última fila de la matriz en la posición 9.
k[[2]] # Indexamos todo el elemento de la posición 2 con doble llave
## [1] "Hola mundo"
k[[6]][2] # Indexamos el elemento en la posición 6 y luego el valor en la posición 2
## [1] "lindo"
k[[9]][5,] # Indexamos el elemento en la posición 9 y luego todos los valores de la fila 5
## [1] 5 10 15 20 25
Otra forma como podemos filtrar y extraer datos es mediante la
función subset(), la cual nos permite establecer una
condición directamente sin tener que indexar. Vamos de nuevo a extraer
los estados de México que cumplan con tener más de 5 millones de
habitantes.
subset(data, data$Población >= 5000000)
## País Estado Area Superficie Población Densidad
## 7 México Jalisco 78588 4.0 8348151 106.23
## 10 México Chiapas 73311 3.7 5543828 75.62
## 11 México Veracruz 71826 3.7 8062579 112.25
## 13 México Nuevo León 64156 3.3 5784442 90.16
## 21 México Puebla 34306 1.7 6583278 191.90
## 22 México Guanajuato 30608 1.6 6166934 201.48
## 25 México Estado de México 22351 1.1 16992418 760.25
## 32 México Ciudad de México 1495 0.1 9209944 6160.50
En R y al igual que en muchos otros lenguajes de programación, es posible crear ciclos de iteraciones pare repetir un procedimiento, o condicionales para la toma de decisiones según una serie de criterios.
Vamos a comenzar por los ciclos de iteraciones, los cuales se hacen
mediante la función for(){}. Esta función requiere que
especifiquemos una variable iterante y un rango de valores sobre los
cuales iterar, de modo que al terminar cada ciclo la variable aumente en
una unidad y se repita el procedimiento que hemos definido. Por ejemplo,
vamos a transformar el área de los estados de México de km2 a
campos de fútbol (un campo de fútbol = 0.00714 km2) y a
llenar un nuevo vector con esta información.
areas.futbol <- rep(NA, # La función rep nos permite repetir algo N cantidad de veces (en este caso, NA = vacío)
nrow(data)) # La función nrow nos da el número de filas de un objeto bidimensional (su análoga es ncol)
names(areas.futbol) <- data$Estado # La función names nos permite asignarle nombres a los elementos del vector
areas.futbol
## Chihuahua Sonora Coahuila de Zaragoza
## NA NA NA
## Durango Oaxaca Tamaulipas
## NA NA NA
## Jalisco Zacatecas Baja California Sur
## NA NA NA
## Chiapas Veracruz Baja California
## NA NA NA
## Nuevo León Guerrero San Luis Potosí
## NA NA NA
## Michoacán Sinaloa Campeche
## NA NA NA
## Quintana Roo Yucatán Puebla
## NA NA NA
## Guanajuato Nayarit Tabasco
## NA NA NA
## Estado de México Hidalgo Querétaro
## NA NA NA
## Colima Aguascalientes Morelos
## NA NA NA
## Tlaxcala Ciudad de México
## NA NA
# En R existe
una diferencia entre NA (not available) y NaN
(Not a Number), el primero indica un valor vacío, mientras que el
segundo valores imposibles (0 / 0 = NaN).
for(n in 1:nrow(data)){ # La variable iterante puede llevar cualquier nombre
areas.futbol[n] <- data[n, 3] / 0.00714 # Indexamos de manera dinámica y asignamos el resultado de la operación
}
areas.futbol
## Chihuahua Sonora Coahuila de Zaragoza
## 34657563.0 25119747.9 21227170.9
## Durango Oaxaca Tamaulipas
## 17271288.5 13131232.5 11239355.7
## Jalisco Zacatecas Baja California Sur
## 11006722.7 10543977.6 10351400.6
## Chiapas Veracruz Baja California
## 10267647.1 10059663.9 10007002.8
## Nuevo León Guerrero San Luis Potosí
## 8985434.2 8907002.8 8562605.0
## Michoacán Sinaloa Campeche
## 8207142.9 8151260.5 8054201.7
## Quintana Roo Yucatán Puebla
## 6261204.5 5535574.2 4804761.9
## Guanajuato Nayarit Tabasco
## 4286834.7 3901540.6 3463725.5
## Estado de México Hidalgo Querétaro
## 3130392.2 2914986.0 1638515.4
## Colima Aguascalientes Morelos
## 788095.2 786554.6 683333.3
## Tlaxcala Ciudad de México
## 562465.0 209383.8
# Los ciclos de iteraciones son muy útiles, pero para determinados procesos que requieren recorrer vectores o matrices no son la opción más eficiente. Con pocos datos el tiempo de cálculo es ridículamente pequeño, pero a medida que aumenta el volumen, los tiempos pueden crecer al orden de días, semanas y meses.
Ahora vamos a crear un condicional para reemplazar los espacios en
los nombres de los estados por guiones bajos. El condicional
if(){} se puede crear de manera independiente, pero como
queremos validar la condición para los 32 estados tendremos que
incluirlo dentro de un ciclo de iteraciones. Además, vamos a contar
cuántos estados tienen nombres simples y cuántos nombres compuestos.
sim <- 0 # Creamos una variable para contar los nombres simples
com <- 0 # Creamos una variable para contar los nombres compuestos
for(z in 1:length(areas.futbol)){ # La función lenght nos da la longitud de un elemento
if(grepl(" ", names(areas.futbol)[z]) == TRUE){ # La función grepl nos ayuda a buscar patrones en cadenas de texto
names(areas.futbol)[z] <- gsub(" ", "_", names(areas.futbol)[z]) # La función gsub sirve reemplazar cadenas de texto
com <- com + 1
}else{
sim <- sim + 1
}
}
areas.futbol
## Chihuahua Sonora Coahuila_de_Zaragoza
## 34657563.0 25119747.9 21227170.9
## Durango Oaxaca Tamaulipas
## 17271288.5 13131232.5 11239355.7
## Jalisco Zacatecas Baja_California_Sur
## 11006722.7 10543977.6 10351400.6
## Chiapas Veracruz Baja_California
## 10267647.1 10059663.9 10007002.8
## Nuevo_León Guerrero San_Luis_Potosí
## 8985434.2 8907002.8 8562605.0
## Michoacán Sinaloa Campeche
## 8207142.9 8151260.5 8054201.7
## Quintana_Roo Yucatán Puebla
## 6261204.5 5535574.2 4804761.9
## Guanajuato Nayarit Tabasco
## 4286834.7 3901540.6 3463725.5
## Estado_de_México Hidalgo Querétaro
## 3130392.2 2914986.0 1638515.4
## Colima Aguascalientes Morelos
## 788095.2 786554.6 683333.3
## Tlaxcala Ciudad_de_México
## 562465.0 209383.8
paste("México tiene", com, "estados con nombre compuesto", sep = " ") # La función paste nos ayuda a concatenar elementos
## [1] "México tiene 8 estados con nombre compuesto"
paste("México tiene", sim, "estados con nombre simple", sep = " ")
## [1] "México tiene 24 estados con nombre simple"
En R y R Studio podemos guardar y exportar nuestros datos para usarlos luego, en otro equipo o compartirlos con alguien más. Pero es importante saber qué es lo que estamos guardando ya que se pueden guardar tres tipos de archivos diferentes (scripts, sesiones o variables específicas).
Para guardar nuestro script basta con darle al botón de guardar (Save current document) del menú superior, o seguir la ruta File > Save, o usar el atajo de teclado CTRL + S. Si usamos R Studio, el color del título de la pestaña donde tenemos nuestro script nos indica de manera explícita si tiene cambios que no hemos guardado (color rojo y un asterisco), o por el contrario, si ya hemos guardado (color negro sin asterisco). Los scripts son archivos de texto plano que se guardan con extensión .R.
Por otro lado, también podemos guardar nuestra sesión, la cual
contiene todas las variables y elementos que hemos creado, así no
tendremos que volver a correr todo el script para generarlas la próxima
vez. Para guardar una sesión vamos a darle al icono de guardar (Save
workspace as) del menú superior de la ventana donde vemos las
variables, en la pestaña Environment (las sesiones de R se
guardan en formado .RData). También podemos guardar una
sesión de manera programática usando el comando
save.image().
save.image("D:/Daniel/Documents/Introducción a R/Introducción a R.RData")
Otra cosa que podemos guardar es una o varias variables específicas
en formato .RData, usando el comando
save(). Para el ejercicio guardaremos la variable
data que contiene los datos para los estados de México,
pero que hemos modificado para que incluya los valores de densidad
poblacional. Vale la pena mencionar que, en este formato, los datos solo
pueden ser abiertos en R.
save(data, # Indicamos la variable o variables (mediante vector) que queremos guardar
file = "D:/Daniel/Documents/Introducción a R/Data Mexico.RData")
En caso de que queramos hacer el proceso contrario, es decir, importar, podemos hacerlo directamente desde el ícono que se encuentra del lado izquierdo de guardar en el menú de la ventana de variables, que corresponde a una carpeta con una flecha verde (Load workspace). O de manera programática.
load(file = "D:/Daniel/Documents/Introducción a R/Data Mexico.RData")
Por último, podemos exportar nuestros datos en un formato más universal como .csv (texto delimitado), el cual podremos abrir en algún procesador de hojas de cálculo como Excel.
write.csv(data, # Indicamos la variable que queremos guardar
file = "D:/Daniel/Documents/Introducción a R/Datos (modificados).csv",
row.names = FALSE) # Indicamos que no queremos que los datos tengan nombres de filas
Como ya hemos visto, R es una calculadora con esteroides donde podemos hacer casi cualquier cosa. A continuación, veremos algunas funciones adicionales que nos van a ayudar a hacer algunos cálculos y gráficos de estadística básica.
min(data$Población) # La función min nos regresa el valor mínimo de una serie de datos
## [1] 731391
max(data$Población) # La función max nos regresa el valor máximo de una serie de datos
## [1] 16992418
range(data$Población) # La función range nos regresa el rango de una serie de datos
## [1] 731391 16992418
mean(data$Población) # # La función mean nos regresa el valor promedio de una serie de datos
## [1] 3937938
sd(data$Población) # La función sd nos regresa la desviación estandar de una serie de datos
## [1] 3278009
var(data$Población) # La función var nos regresa la varianza de una serie de datos (= sd ^ 2)
## [1] 1.074534e+13
quantile(data$Población, # La función quantile nos regresa los cuantiles de una serie de datos
probs = c(0, 0.25, 0.5, 0.75, 0.1)) # Con el argumento probs indicamos cuales cuantiles queremos
## 0% 25% 50% 75% 10%
## 731391 1851651 3054892 4947592 1246208
cor(data$Población, data$Area, method = "spearman") # La función cor nos permite obtener un índice de correlación
## [1] 0.1829179
cor.test(data[ , 3], data[ , 5], method = "spearman") # La función cor.test nos permite probar correlación
##
## Spearman's rank correlation rho
##
## data: data[, 3] and data[, 5]
## S = 4458, p-value = 0.3149
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## 0.1829179
shapiro.test(data$Población) # La función shapiro.test nos permite probar el supuesto de normalidad
##
## Shapiro-Wilk normality test
##
## data: data$Población
## W = 0.78087, p-value = 1.818e-05
# Histograma de frecuencias
options(scipen = 10) # Elegimos el número decimales antes de que R use notación científica en la gráfica (no es necesario)
hist(data[, 5], # Definimos los datos, en este caso los valores de todas las filas de la columna «Población»
xlab = "Población", # Etiqueta del eje X
ylab = "Frecuencia", # Etiqueta del eje Y
main = "Histograma de frecuencias", # Título principal
col = "lightblue") # Color de las barras (puede ser nombre, HEX o RGB)
# Diagrama de caja y bigotes
options(scipen = 10) # Elegimos el número decimales antes de que R use notación científica en la gráfica (no es necesario)
boxplot(data[, 5], # Definimos los datos, en este caso los valores de todas las filas de la columna «Población»
xlab = "México", # Etiqueta del eje X
ylab = "Población", # Etiqueta del eje Y
main = "Diagrama de caja y bigotes", # Título principal
col = "#ffffff") # Color de las barras (puede ser nombre, HEX o RGB)
Vamos a terminar haciendo una regresión lineal, para lo cual cambiaremos de datos porque los que hemos estado utilizando no tienen variables con relaciones lineales muy buenas. Por defecto, R trae algunos conjuntos de datos precargados, de los cuales seleccionaremos el viejo y confiable iris, que contiene algunas medidas de ancho y longitud para las estructuras florales de tres especies de plantas.
data(iris) # Cargamos el conjunto de datos iris (viene precargado por defecto en R)
head(iris) # Validamos qué datos tenemos
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
iris.lm <- lm(Petal.Width ~ Petal.Length, data = iris) # Creamos el modelo de regresión lineal con la función lm
summary(iris.lm) # Obtenemos los coeficientes del modelo mediante la función summary
##
## Call:
## lm(formula = Petal.Width ~ Petal.Length, data = iris)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.56515 -0.12358 -0.01898 0.13288 0.64272
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -0.363076 0.039762 -9.131 4.7e-16 ***
## Petal.Length 0.415755 0.009582 43.387 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2065 on 148 degrees of freedom
## Multiple R-squared: 0.9271, Adjusted R-squared: 0.9266
## F-statistic: 1882 on 1 and 148 DF, p-value: < 2.2e-16
# Grafica del modelo de regresión lineal
plot(iris$Petal.Length, # Definimos los valores del eje X
iris$Petal.Width, # Definimos los valores del eje Y
xlab = "Longitud del pétalo", # Etiqueta del eje X
ylab = "Ancho del pétalo", # Etiqueta del eje Y
main = "Modelo de regresión lineal", # Título principal
pch = 19, # Código de los símbolos (19 = circulo)
col = "black") # Color de los símbolos
abline(iris.lm, # Agregamos la línea de tendencia con abline y el modelo
col = "red", # Elegimos un color para la línea
lwd = 2) # Establecemos el ancho de la línea
Vamos a poner a prueba todo lo aprendido con un ejercicio simple, en el que tendrán que tomar unos datos en formato Excel (Squamata México.xlsx), curarlos, exportarlos como texto delimitado (.csv), cargarlos a R y generar un script para realizar lo que se pide a continuación.
Crear una nueva columna que contenga los valores del logaritmo base 10 para la masa (en los datos identificada como Mass) de cada una de las especies.
Describir estadísticamente los datos de masa para los reptiles de México (promedio, mediana, desviación estandar y todos los los deciles).
Poner a prueba el supuesto de normalidad para la masa de todos los reptiles y si existe correlación estadísticamente significativa entre la masa y la longitud (en los datos identificada como Max SVL).
Crear una nueva columna vacia que se llame Group, y mediante un ciclo de iteraciones asignarle el valor Snakes si el valor de la columna Monophyletic es igual a Serpentes, si no, asignar el valor Lizards.
Realizar un histograma de frecuencias y un diagrama de caja y bigotes para el logaritmo base 10 de la masa de los lagartos y otro para la masa de las serpientes.
# Pista: Recuerden que los errores más comunes suelen estar asociados a los números o a espacios en blanco, además, para este ejercicio, la columna Species no es el problema.