Este curso está diseñado para equipar a los estudiantes de la UNAL sede La Paz con habilidades prácticas en la manipulación eficiente de datos utilizando el lenguaje de programación R a través de los Grupos de Estudio Autónomo. El curso se sustenta en la asignatura de computación estadística ofertado por el docente Luis Hernando Vanegas del departamento de estadística de la sede Bogotá, el cual cursó y aprobó el tutor Juan Daniel Diaz y que aspiro compartir con los estudiantes de estadística de la sede La Paz. Desde la instalación de Rstudio, importación, exportación, manipulación y visualización de datos. Los participantes explorarán cada paso del proceso con sesiones interactivas y aplicadas.

Sesión 0: Introducción a Rstudio y R.

Objetivos de la sesión

Instalación de Rstudio y R, conceptos fundamentales sobre R como calculadora, operaciones matemáticas básicas, creación de vectores y matrices, y sintaxis de R.

¿R un lenguaje de programación diseñado especialmente para el análisis estadístico?

R es un lenguaje de programación utilizado para realizar procedimientos estadísticos y gráficos de alto nivel. Fue creado en 1993 por los profesores e investigadores Robert Gentleman y Ross Ihaka. Inicialmente, el lenguaje se utilizó para apoyar los cursos a cargo de los profesores, pero debido a la utilidad de la herramienta desarrollada, decidieron distribuir copias de R. A partir de 1995 el código fuente de R estuvo disponible para diferentes sistemas operativos.

¿Que es Rstudio?

Como mencionamos anteriormente, R es un lenguaje de programación que nos permite dar instrucciones específicas a una computadora para realizar diversas operaciones. Para simplificar, podríamos decir que es una manera organizada de comunicarnos con el ordenador. Aunque técnicamente podríamos interactuar con R directamente desde una consola o terminal, similar a las que suelen aparecer en las películas sobre hackers, esto puede resultar poco práctico para la mayoría de los usuarios. Es por eso que existe RStudio, una interfaz de usuario más amigable y accesible que se ha convertido en la opción preferida para trabajar con R. RStudio, también de código abierto, ofrece un entorno integrado que facilita la escritura, ejecución y visualización de código R. Además, es constantemente actualizado por un equipo de programadores, lo que garantiza una experiencia óptima para los usuarios.

Instalación R y RStudio

  • Ejercicio 0.1:
    Usando el siguiente enlace descarga e instala R y RStudio en tu ordenador. La descarga e instalación es accesible para Windows, Mac y Linux. Nota 0.1: Para instalar RStudio, es necesario tener instalado R. La descarga e instalación es accesible para Windows, Mac y Linux.

Recuperado de: Pepi Amodeo (2022).

¿Tienes problemas con la instalación? usa esta ayuda.

Si has podido descargar e instalar R y RStudio, simplemente abre este último para comenzar a trabajar. Encontrarás una pantalla similar a la siguiente figura:

IDEFrancisco Urdinez & Andrés Cruz Labrín (2021)

Consola

El panel inferior izquierdo de RStudio es nuestro espacio de comunicación directa con la computadora, donde le pedimos que realice tareas específicas utilizando el lenguaje R. Nos referimos a estas peticiones como comandos. A Continuación se muestra los principales operadores aritméticos.

Operación Operador en R Ejemplo
Suma + a + b
Resta - a - b
Multiplicación * a * b
División / a / b
Potencia ^ a ^ b
Residuo (Módulo) %% a %% b
Cociente entero %/% a %/% b
Raíz cuadrada sqrt() sqrt(a)
Logaritmo natural log() log(a)
Logaritmo base 10 log10() log10(a)
Valor absoluto abs() abs(a)
Seno sin() sin(a)
Coseno cos() cos(a)
Tangente tan() tan(a)

La consola corresponde al entorno computacional de este lenguaje. Es aquí donde nuestro código es interpretado, si escribimos los siguiente en la consola y presionamos Enter, estamos pidiendo que se ejecute esta operación:

  • Ejemplos
# Suma
5+2
[1] 7
#Resta
5-2
[1] 3
# Multiplicación
5*2
[1] 10
# División
5/2
[1] 2.5
# Potencia
5^2 # opción 1
[1] 25
5**2 # opción 2
[1] 25
# División entera
5%%2
[1] 1

Script

El panel superior izquierdo de RStudio puede describirse como un ‘registro de comandos’. Aunque la consola puede ser útil para algunos comandos, los análisis complejos requerirán que llevemos un registro de nuestro código.

Escribir un nuevo script es tan fácil como presionar Ctrl + Shift + N o ir a Archivo > Nuevo archivo > R Script. La pantalla blanca de un nuevo script es similar a un bloc de notas en blanco, con la característica de que cada línea debe ser pensada como un comando. Ten en cuenta que escribir un comando en el script y presionar ‘Enter’ solo realiza un salto de línea. Para ejecutar el comando en una línea, debes presionar Ctrl + Enter (o Cmd + Enter si usas Mac). También es posible seleccionar varias líneas/comandos a la vez y ejecutarlos todos con Ctrl + Enter.

Operadores relacionales

Los operadores lógicos son usados para hacer comparaciones y siempre devuelven como resultado TRUE o FALSE (verdadero o falso, respectivamente).

# Menor que y menor igual que
5 < 2
[1] FALSE
5 <= 2
[1] FALSE
# Mayor que y mayor o igual que
5 > 2
[1] TRUE
5 >= 2
[1] TRUE
# Exactamente igual que y no es igual (sin diferentes) que
5 == 2
[1] FALSE
5 != 2
[1] TRUE

¿Que sucede si intento operar una letra con un número?

Es posible comparar cualquier tipo de dato sin que resulte en un error. Sin embargo, al usar los operadores >, >=, < y <= con cadenas de texto, estos tienen un comportamiento especial. Por ejemplo, “casa” > “barco” nos devuelve TRUE.

# Cadena de texto
"casa" > "barco"
[1] TRUE

Este resultado se debe a que se ha hecho una comparación por orden alfabético. En este caso, la palabra “casa” tendría una posición posterior a “barco”, pues empieza con “c” y esta letra tiene una posición posterior a la “b” en el alfabeto. Por lo tanto, es verdadero que sea “mayor”.

Operadores de asignación

Este es probablemente el operador más importante de todos, pues nos permite asignar datos a variables. Definiendo mi primera variable :

# Todos estos operadores Asigna un valor a una variable
a = 5    # Opción 1
a <-5 # Opción 2
5 -> a
# para pedir que me muestre lo que contiene
a
[1] 5

Entonces podemos operar:

a+2
[1] 7

Nota 0.2: Aunque podemos usar el signo igual para una asignación, a lo largo de este libro utilizaremos <-, por ser característico de R y fácil de reconocer visualmente.

Nota 0.3: Después de realizar la operación de asignación, podemos usar el nombre de la variable para realizar operaciones con ella, como si fuera del tipo de datos que le hemos asignado. Si asignamos un valor a una variable a la que ya habíamos asignado datos, nuestra variable conserva el valor más reciente.

Nota 0.4: En R, usamos NA para representar datos perdidos, mientras que NULL representa la ausencia de datos.

Operadores lógicos

Los operadores lógicos son usados para operaciones de álgebra Booleana, es decir, para describir relaciones lógicas, expresadas como verdadero (TRUE) o falso (FALSO).

# x Ó y es verdadero
T | F
[1] TRUE
# x Y y son verdaderos
TRUE & FALSE
[1] FALSE
# x no es verdadero (negación)
!TRUE
[1] FALSE
# x es verdadero (afirmación)
isTRUE(TRUE)
[1] TRUE
  • | devuelve TRUE si alguno de los datos es TRUE.

  • & solo devuelve TRUE si ambos datos es TRUE.

  • | solo devuelve FALSE si ambos datos son FALSE.

  • & devuelve FALSE si alguno de los datos es FALSE.

Para realizar operaciones lógicas, todos los valores numéricos y complejos distintos a 0 son coercionados a TRUE, mientras que 0 siempre es coercionado a FALSE.

Nota 0.5: En R, los datos pueden ser coercionados, es decir, forzados, para transformarlos de un tipo a otro.

isTRUE(2)
[1] FALSE
isTRUE(0)
[1] FALSE
# la negación FALSE Y FALSE dará como resultado TRUE
!(FALSE | FALSE)
[1] TRUE
  • Ejercicio 0.2: ¿Qué resultados se obtienen con: 2 | 0 y 2 & FALSE y por qué?

Introducción Básica a las Funciones

Una función es una serie de operaciones a la que les hemos asignados un nombre. Las funciones aceptan argumentos, es decir, especificaciones sobre cómo deben funcionar. Cuando llamamos una función, se realizan las operaciones que contiene, usando los argumentos que hemos establecido.

Para el cálculo de las razones trigonométricas se tiene que considerar al ángulo en pi radianes.

En R reconocemos a una función usando la notación: nombre_de_la_función(). Para el cálculo de las razones trigonométricas se tiene que considerar al ángulo en \(\pi_{rad}\).

factorial(5) # Factorial de n 
[1] 120
choose(5,2) # Combinatorio de n y k.
[1] 10
sin(5) # Seno
[1] -0.9589243
cos(5) # Coseno
[1] 0.2836622
tan(5) # Tangente
[1] -3.380515

¿Como de creo una función en R?

fx <- function(x){
  f <- x^2 + 2
  return(f)
}
fx(x = 0)
[1] 2

Verificar el tipo de un dato

En ocasiones, tenemos datos pero no sabemos de simple vistazo de qué tipo son. Para esto casos, podemos usar la función class() para determinar el tipo de un dato. Esto es de utilidad para asegurarnos que las operaciones que deseamos realizar tendrán los datos apropiados para llevarse a cabo con éxito.

class(a)
[1] "numeric"
Nombre <- "UNAL" ; Nombre
[1] "UNAL"
class(Nombre)
[1] "character"

Ejercicio 0.3: realice la verificación con la familia de funciones is() a la variable a y Nombre

En R, hay varios tipos de objetos que permiten a los usuarios almacenar información para realizar procedimientos estadísticos y gráficos. Los principales objetos en R son vectores, matrices, arreglos, marcos de datos y listas. A continuación, se presentan las características de estos objetos y cómo crearlos.

Estructuras de datos

Las estructuras de datos son objetos que contienen datos. Cuando trabajamos con R, lo que estamos haciendo es manipular estas estructuras. Las estructuras tienen diferentes características. Entre ellas, las que distinguen a una estructura de otra son su número de dimensiones y si son homogeneas o hereterogeneas.

Vectores

Es la estructura de datos más sencilla en R. Un vector es una colección de uno o más datos del mismo tipo. Todos los vectores tienen las siguientes propiedades:

  • Tipo. Un vector tiene el mismo tipo que los datos que contiene. Si tenemos un vector que contiene datos de tipo numérico, el vector será también de tipo numérico, sólo pueden contener datos de un sólo tipo.

  • Largo. Es el número de elementos que contiene un vector. Este corresponde a la dimensión que contiene la estructura.

Creamos vectores usando la función c() (combinar/concatenar). Llamamos esta función y le damos como argumento los elementos que deseamos combinar en un vector, separados por comas.

#Esto es un vector?
is.vector(5)
[1] TRUE
#Cual es su longitud
length(5)
[1] 1
# Vector numérico
R <- c(1, 4, 6, 5, 7, 10)
is.vector(R)
[1] TRUE
length(R)
[1] 6

Lo mismo ocurre con los demás tipos de datos, por ejemplo, con cadenas de texto y datos lógicos.

is.vector("UNAL")
[1] TRUE
# length(Nombre)

# Vector de cadena de texto
caracterisricas <-  c("arbol", "casa", "persona")

# Vector lógico
c(TRUE, TRUE, FALSE, FALSE, TRUE)
[1]  TRUE  TRUE FALSE FALSE  TRUE

Si deseamos agregar un elemento a un vector ya existente, podemos hacerlo combinando nuestro vector original con los elementos nuevos y asignando el resultado a nuestro vector original.

mi_vector <- c(TRUE, FALSE, TRUE)

mi_vector <- c(mi_vector, FALSE)

mi_vector
[1]  TRUE FALSE  TRUE FALSE

Naturalmente, podemos crear vectores que son combinación de vectores.

mi_vector_1 <- c(1, 3, 5)
mi_vector_2 <- c(2, 4, 6)
mi_vector_3 <- c(mi_vector_1, mi_vector_2)
mi_vector_3
[1] 1 3 5 2 4 6

Podemos crear vectores de secuencias numéricas usando :. De un lado de los dos puntos escribimos el número de inicio de la secuencia y del otro el final.

Secuencia <- 1:10
Secuencia
 [1]  1  2  3  4  5  6  7  8  9 10
# Número negativo
-43:-30
 [1] -43 -42 -41 -40 -39 -38 -37 -36 -35 -34 -33 -32 -31 -30
# Número con cifras decimales
67.23:75
[1] 67.23 68.23 69.23 70.23 71.23 72.23 73.23 74.23
# Se conservan los decimales del inicio
-2.48:2
[1] -2.48 -1.48 -0.48  0.52  1.52
# Se redondean los decimales del final
166:170.05
[1] 166 167 168 169 170

Matrices

Pueden ser descritas como vectores multidimensionales. Al igual que un vector, únicamente pueden contener datos de un sólo tipo, pero además de largo, tienen más dimensiones. Específicamente dos dimensiones, un “largo”” y un “alto”. Las matrices son, por lo tanto, una estructura con forma rectangular, con filas y columnas.

La creación de matrices es con la función matrix(). Esta acepta dos argumentos, nrow y ncol. Con ellos especificamos el número de renglones y columnas que tendrá nuestra matriz.

# matrix() sin especificar renglones ni columnas
matrix(1:12)
      [,1]
 [1,]    1
 [2,]    2
 [3,]    3
 [4,]    4
 [5,]    5
 [6,]    6
 [7,]    7
 [8,]    8
 [9,]    9
[10,]   10
[11,]   11
[12,]   12
# Tres renglones y cuatro columnas
matrix(1:12, nrow = 3, ncol = 4)
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
# Dos renglones y cuatro columnas
matrix(1:12, nrow = 4, ncol = 3)
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

Los datos que intentemos agrupar en una matriz serán acomodados en orden, de arriba a abajo, y de izquierda a derecha, hasta formar un rectángulo. Otro procedimiento para crear matrices es la unión vectores con las siguientes funciones: - cbind() para unir vectores, usando cada uno como una columna. - rbind() para unir vectores, usando cada uno como una fila.

vector_1 <- 1:4
vector_2 <- 5:8
vector_3 <- 9:12
vector_4 <- 13:16

Usamos rbind() para crear un matriz, en la que cada vector será un renglón

matriz <- rbind(vector_1, vector_2, vector_3, vector_4)
matriz
         [,1] [,2] [,3] [,4]
vector_1    1    2    3    4
vector_2    5    6    7    8
vector_3    9   10   11   12
vector_4   13   14   15   16

Ejercicio: ¿Si utilizamos cbind(), entonces cada vector será? y ¿cuál es la dimención de la matriz?

Las matrices y arrays pueden tener más de una dimensión.

Operaciones básicas de matriciales en R

Operación Símbolo Descripción Ejemplo
Suma + Suma de elementos correspondientes A + B
Resta - Resta de elementos correspondientes A - B
Multiplicación %*% Multiplicación de matrices A %*% B
Multiplicación por escalar * Multiplicación de cada elemento por un escalar A * 2
Transposición t() Transpone la matriz t(A)
Determinante det() Calcula el determinante de la matriz det(A)
Inversa solve() Calcula la inversa de la matriz solve(A)
Producto Hadamard * Multiplica elemento por elemento A * B
Producto de Kronecker %x% Producto de Kronecker kronecker(A, B)

Data frames o Marco de datos

Los data frames son estructuras de datos de dos dimensiones (rectangulares) que pueden contener datos de diferentes tipos, por lo tanto, son heterogéneas. Esta estructura de datos es la más usada para realizar análisis de datos y seguro te resultará familiar si has trabajado con otros paquetes estadísticos. Si bien los data frames parece similar a una matriz y se puede ver como tal, internamente no es una matriz, es decir, no es un objeto tipo array. Por último, Un data frame está compuesto por vectores.

mi_df <- data.frame(
  "entero" = 1:4, 
  "factor" = c("a", "b", "c", "d"), 
  "numero" = c(1.2, 3.4, 4.5, 5.6),
  "cadena" = as.character(c("a", "b", "c", "d"))
)

mi_df
  entero factor numero cadena
1      1      a    1.2      a
2      2      b    3.4      b
3      3      c    4.5      c
4      4      d    5.6      d

Podemos acceder a la dimensión (dim), a los nombres de las columnas/variables (names) y a la clase de un dataframe.

# Podemos usar dim() en un data frame
dim(mi_df)
[1] 4 4
# names() nos permite ver los nombres de las columnas
names(mi_df)
[1] "entero" "factor" "numero" "cadena"
# La clase de un data frame es data.frame
class(data.frame) 
[1] "function"

Usamos as.data.frame() para coercionar una matriz a este tipo de objeto.

matriz <- matrix(1:12, ncol = 4)

df <- as.data.frame(matriz)

class(df)
[1] "data.frame"
df
  V1 V2 V3 V4
1  1  4  7 10
2  2  5  8 11
3  3  6  9 12

Para acceder a los columnas de un marco de datos se hace uno de los corchetes (parentesis rectangulares) seguido del nombre del dataframe. También se puede acceder por medio de del signo peso ($) seguido del nombre del marco de datos, como muestra a continuación:

df$V2 
[1] 4 5 6
df[,2]
[1] 4 5 6
df[["V2"]]
[1] 4 5 6
df["V2"] 
  V2
1  4
2  5
3  6

Si solo nos interesa la información de ciertas columnas o variables se puede realizar de la siguiente manera:

mi_df[c("factor", "numero")]
  factor numero
1      a    1.2
2      b    3.4
3      c    4.5
4      d    5.6

En caso tal, quisieramos acceder a las columnas y filas especificas se haría uso de los parentesis rectangulares como muestra a continuación:

mi_df[c(1,3), c("factor", "numero")]
  factor numero
1      a    1.2
3      c    4.5

Librerías o Paquetes

R puede ser expandido con paquetes. Cada paquete es una colección de funciones diseñadas para atender una tarea específica. Por ejemplo, hay paquetes para trabajo visualización geoespacial, análisis psicométricos, mineria de datos, interacción con servicios de internet y muchas otras cosas más.

Estos paquetes se encuentran alojados en CRAN, así que pasan por un control riguroso antes de estar disponibles para su uso generalizado. Podemos instalar paquetes usando la función install.packages(), dando como argumento el nombre del paquete que deseamos instalar, entre comillas.

Una vez concluida la instalación de un paquete, podrás usar sus funciones con la función library(). Sólo tienes que llamar esta función usando como argument oel nombre del paquete que quieres utilizar.

install.packages("Nombre del paquete")

library("Nombre del paquete")

Directorio de trabajo

El directorio o carpeta de trabajo es el lugar en nuestra computadora en el que se encuentran los archivos con los que estamos trabajando en R. Este es el lugar donde R buscara archivos para importarlos y al que serán exportados, a menos que indiquemos otra cosa. Puedes encontrar cuál es tu directorio de trabajo con la función getwd(). Sólo tienes que escribir la función en la consola y ejecutarla.

getwd()
[1] "C:/Users/USUARIO/Documents/Universidad/2024-1S/GEA/Cursillo"

Puedes cambiar el directorio de trabajo usando la función setwd(), dando como argumento la ruta del directorio que quieres usar.

setwd("C:\otro_directorio")

Por último, si deseas conocer el contenido de tu directorio de trabajo, puedes ejecutar. la función list.files(), sin argumentos, que devolverá una lista con el nombre de los archivos de tu directorio de trabajo. La función list.dirs(), también sin argumentos` te dará una lista de los directorios dentro del directorio de trabajo.

# Ver archivos
list.files()

# Ver directorios
list.dirs()

Sesión 1: importación, manipulación de cadenas de texto y exportación de conjuntos de datos.

Importación de conjunto de datos.

En este apartado trabajamos la importación de datos usando las librerías readxl y readr para archivos con extensiones xls y xlsx.

install.packages("readxl")
help(readxl)
help(read_excel)
  • Ejemplo 1.1: Ahora, se procede a la importación del conjunto de datos Municipios.
library(readxl)
Municipios <- read_excel("Datos/Municipios.xlsx", col_names = TRUE)
str(Municipios)
tibble [1,118 × 8] (S3: tbl_df/tbl/data.frame)
 $ Departamento: chr [1:1118] "Ant%ioqUia" "Ant%ioquia" "Ant%ioquia" "Ant%io>qUia" ...
 $ Dep         : chr [1:1118] "05" "05" "05" "05" ...
 $ Municipio   : chr [1:1118] "Mede&l'lín" "Abej!&orr*al" "A?br'&iaquí" "Alej#andr&'ía" ...
 $ Depmun      : chr [1:1118] "05001" "05002" "05004" "05021" ...
 $ Superficie  : num [1:1118] 374.8 507.1 297 128.9 84.1 ...
 $ Poblacion   : num [1:1118] 2483545 20258 2710 4669 30777 ...
 $ Irural      : num [1:1118] 5 45 58 48 28 54 38 37 47 58 ...
 $ Region      : chr [1:1118] "Región Eje Cafetero" "Región Eje Cafetero" "Región Eje Cafetero" "Región Eje Cafetero" ...
head(Municipios, n = 5)
# A tibble: 5 × 8
  Departamento Dep   Municipio     Depmun Superficie Poblacion Irural Region    
  <chr>        <chr> <chr>         <chr>       <dbl>     <dbl>  <dbl> <chr>     
1 Ant%ioqUia   05    Mede&l'lín    05001       375.    2483545      5 Región Ej…
2 Ant%ioquia   05    Abej!&orr*al  05002       507.      20258     45 Región Ej…
3 Ant%ioquia   05    A?br'&iaquí   05004       297.       2710     58 Región Ej…
4 Ant%io>qUia  05    Alej#andr&'ía 05021       129.       4669     48 Región Ej…
5 Antioquia    05    Am#a?&'*gá    05030        84.1     30777     28 Región Ej…
tail(Municipios, n = 5)
# A tibble: 5 × 8
  Departamento Dep   Municipio         Depmun Superficie Poblacion Irural Region
  <chr>        <chr> <chr>             <chr>       <dbl>     <dbl>  <dbl> <chr> 
1 Vaup>és      97    Y#*ava'ra?t!é     97889…      4660.      1067     92 Regió…
2 Vi%ch>ada    99    Puerto   Carr#eñ… 99001…     12205.     20474     81 Regió…
3 Vichada      99    La Prima&*'!ve#ra 99524…     18569.      9799     85 Regió…
4 Vi%ch>ada    99    Santa   ?Rosalía  99624…      3692.      4130     81 Regió…
5 Vichada      99    C#u'?mar!ibo      99773…     65597.     76196     87 Regió…
  • Ejemplo 1.2: La función read_excel trae ciertos argumentos por defectos que podemos especificar, como el nombre de las columnas (col_names), especificar la hoja la cual queremos importar (sheet), numeros máximmos de registros (n_max), entre otras.
Municipios <- read_excel("Datos/Municipios.xlsx", sheet = 1, skip = 1,
                         col_names = c("Depto", "Dep", "Mcpio", "Codmun",
                                       "Super", "Pob", "Irural", "Region"),
                         col_types = c("text", "text", "text", "text",
                                       "numeric", "numeric", "numeric", "text"),
                         n_max = 1118)

dim(Municipios)
[1] 1118    8

Existen diferentes extensiones de archivos, por lo tanto la libreria readr nos permite importar extensiones de archivos diferentes a xls y xlsx con la función read_delim.

install.packages("readr")
library("readr")
help(readr)
help(read_delim)
  • Ejemplo 1.3: A continuación importamos stocks, archivo con extensión .dlm.
library(readr)
stocks <- read_delim("Datos/stocks.dlm", delim = " ", col_names = TRUE)
str(stocks)
spc_tbl_ [699 × 8] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ A: chr [1:699] "IBM" "IBM" "IBM" "IBM" ...
 $ B: chr [1:699] "01DEC05" "01NOV05" "03OCT05" "01SEP05" ...
 $ C: num [1:699] 8915 8185 8022 8016 8300 ...
 $ D: num [1:699] 8992 8994 8460 8211 8420 ...
 $ E: num [1:699] 8156 8064 7870 7693 7987 ...
 $ F: num [1:699] 8220 8890 8188 8022 8062 ...
 $ G: chr [1:699] "5.976.252" "5.556.471" "7.019.666" "5.772.280" ...
 $ H: chr [1:699] "81,37" "88,01" "80,86" "79,22" ...
 - attr(*, "spec")=
  .. cols(
  ..   A = col_character(),
  ..   B = col_character(),
  ..   C = col_number(),
  ..   D = col_number(),
  ..   E = col_number(),
  ..   F = col_number(),
  ..   G = col_character(),
  ..   H = col_character()
  .. )
 - attr(*, "problems")=<externalptr> 
  • Ejemplo 1.4: Exploramos algunos argumentos de read_delim, como especificar el separador decimal/agrupamiento y especificar el tipo de variable.
help(cols)
stocks2 <- read_delim("Datos/stocks.dlm", delim = " ",
                     locale = locale(decimal_mark = ",", grouping_mark = "."),
                     col_types = cols("f", col_date("%d%b%y"), "d", "d", "d", 
                                      "d", "n", "d"), skip = 1, 
                     col_names = c("Stock", "Date","Open","High", "Low","Close", 
                                   "Volume"," AdjClose"))
str(stocks2)
spc_tbl_ [699 × 8] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ Stock    : Factor w/ 3 levels "IBM","Intel",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Date     : Date[1:699], format: "2005-12-01" "2005-11-01" ...
 $ Open     : num [1:699] 89.2 81.8 80.2 80.2 83 ...
 $ High     : num [1:699] 89.9 89.9 84.6 82.1 84.2 ...
 $ Low      : num [1:699] 81.6 80.6 78.7 76.9 79.9 ...
 $ Close    : num [1:699] 82.2 88.9 81.9 80.2 80.6 ...
 $ Volume   : num [1:699] 5976252 5556471 7019666 5772280 4801386 ...
 $  AdjClose: num [1:699] 81.4 88 80.9 79.2 79.6 ...
 - attr(*, "spec")=
  .. cols(
  ..   Stock = col_factor(levels = NULL, ordered = FALSE, include_na = FALSE),
  ..   Date = col_date(format = "%d%b%y"),
  ..   Open = col_double(),
  ..   High = col_double(),
  ..   Low = col_double(),
  ..   Close = col_double(),
  ..   Volume = col_number(),
  ..   ` AdjClose` = col_double()
  .. )
 - attr(*, "problems")=<externalptr> 

Manejo de cadenas de texto y limpieza de base de datos.

Usaremos la libería stringr para la manipulación de cadenas de texto y limpieza de base de datos. Para esto, realizaremos uso del conjunto de datos Municipios.

install.packages(stringr)
library(stringr)
help(stringr)
  • Ejemplo 1.5: Realizaremos limpieza a los nombres de los nombres de los Municipios: Se eliminan los caracteres especiales, espacios al incio y al final, se reemplazan todos los espacios por sencillos y se escribe en mayúscula solo la primera letra.
help(str_to_lower)
help(str_replace_all)
help(str_squish)
help(str_to_title)
library(stringr)
Municipios <- within(Municipios,{
  Mcpio2 <- str_to_lower(Mcpio)
  Mcpio2 <- str_replace_all(Mcpio2,"[^a-záéíóúüñ ]","")
  Mcpio2 <- str_squish(Mcpio2)
  Mcpio2 <- str_to_title(Mcpio2)
})
head(Municipios[,c("Mcpio","Mcpio2")], n=10)
# A tibble: 10 × 2
   Mcpio           Mcpio2     
   <chr>           <chr>      
 1 Mede&l'lín      Medellín   
 2 Abej!&orr*al    Abejorral  
 3 A?br'&iaquí     Abriaquí   
 4 Alej#andr&'ía   Alejandría 
 5 Am#a?&'*gá      Amagá      
 6 Am?al#'*fi      Amalfi     
 7 Andes*!         Andes      
 8 &# Angelópoli?s Angelópolis
 9 Ang#'ostura     Angostura  
10 An*'#?orí       Anorí      
#Guardamos los cambios realizados
Municipios <- within(Municipios,{
  Mcpio <- Mcpio2
  rm(Mcpio2)
})
  • Ejemplo 1.6: Realizaremos limpieza a los nombres de los nombres de los Departamentos: Se eliminan los caracteres especiales, espacios al incio y al final, se reemplazan todos los espacios por sencillos y se escribe en mayúscula solo la primera letra.
Municipios <- within(Municipios,{
  Depto2 <- str_to_lower(Depto)
  Depto2 <- str_replace_all(Depto2,"[^a-záéíóúüñ ]","")
  Depto2 <- str_squish(Depto2)
  Depto2 <- str_to_title(Depto2)
})

head(Municipios[,c("Depto","Depto2")], n=5)
# A tibble: 5 × 2
  Depto       Depto2   
  <chr>       <chr>    
1 Ant%ioqUia  Antioquia
2 Ant%ioquia  Antioquia
3 Ant%ioquia  Antioquia
4 Ant%io>qUia Antioquia
5 Antioquia   Antioquia
#Guardamos Departamento2 en Departamento, es decir, guardamos los cambios.
Municipios <- within(Municipios,{
  Depto <- Depto2
  rm(Depto2)
})
  • Ejemplo 1.7: Eliminaremos la palabra Región en cada uno de los registros de la variable Region.
Municipios <- within(Municipios,{
                    Region2 <- str_to_lower(Region)
                    Region2 <- str_replace_all(Region2,"[^a-záéíóúüñ ]","")
                    Region2 <- str_replace_all(Region2, "región ", "")
                    Region2 <- str_squish(Region2)
                    Region2 <- str_to_title(Region2)
                    })
head(Municipios[,c("Region","Region2")], n=5)
# A tibble: 5 × 2
  Region              Region2     
  <chr>               <chr>       
1 Región Eje Cafetero Eje Cafetero
2 Región Eje Cafetero Eje Cafetero
3 Región Eje Cafetero Eje Cafetero
4 Región Eje Cafetero Eje Cafetero
5 Región Eje Cafetero Eje Cafetero
#Guardamos cambios
Municipios <- within(Municipios,{
  Region <- Region2
  rm(Region2)
})

Funciones de texto.

Haremos uso de las expresiones regulares y las funciones subset y str_detect de la librería stringr.

help(subset)
help(str_detect)
  • Ejemplo 1.8: A continuación seleccionamos el conjunto de datos con las variables Departamento, Codmun y Mcpio, y con todos los municipios cuyo nombre incluye el nombre de un santo, por ejemplo, Santa Marta, Santo Tomás, San José y Valle de San Jacinto.
Santos <- subset(Municipios, str_detect(Mcpio, "(^| )San( |ta |to )"),
                 select = c(Depto, Codmun, Mcpio))

head(Santos, n = 5)
# A tibble: 5 × 3
  Depto     Codmun Mcpio                 
  <chr>     <chr>  <chr>                 
1 Antioquia 05647  San Andrés De Cuerquía
2 Antioquia 05649  San Carlos            
3 Antioquia 05652  San Francisco         
4 Antioquia 05656  San Jerónimo          
5 Antioquia 05658  San José De La Montaña
  • Ejemplo 1.9: Ahora, se busca el conjunto de datos con todas las variables excepto Pob, Super y Irural, y con todos los municipios cuyos nombres tienen “A” como primera letra y “e” como cuarta letra.
Aes <- subset(Municipios, str_detect(Mcpio, "^(A|Á).{2}(e|é).*"),
              select = -c(Pob,Super,Irural))

head(Aes, n=5)
# A tibble: 5 × 5
  Depto     Dep   Mcpio       Codmun  Region        
  <chr>     <chr> <chr>       <chr>   <chr>         
1 Antioquia 05    Andes       05034   Eje Cafetero  
2 Antioquia 05    Angelópolis 05036   Eje Cafetero  
3 Antioquia 05    Argelia     05055   Eje Cafetero  
4 Antioquia 05    Armenia     05059   Eje Cafetero  
5 Boyacá    15    Almeida     15022.0 Centro Oriente
  • Ejemplo 1.10: ¿Cuáles municipios tienen nombres que constan de más de 4 palabras?
Mas4 <- subset(Municipios, str_count(Mcpio, " ") > 3)
Mas4
# A tibble: 5 × 8
  Depto        Dep   Mcpio                      Codmun Super   Pob Irural Region
  <chr>        <chr> <chr>                      <chr>  <dbl> <dbl>  <dbl> <chr> 
1 Antioquia    05    San José De La Montaña     05658   126.  3706     50 Eje C…
2 Cundinamarca 25    San Juan De Río Seco       25662…  314.  8324     47 Centr…
3 Cundinamarca 25    Villa De San Diego De Uba… 25843…  102. 44802     29 Centr…
4 Chocó        27    El Cantón Del San Pablo    27135…  380.  6210     54 Pacíf…
5 Chocó        27    El Litoral Del San Juan    27250… 4135. 22242     67 Pacíf…

Exportación de datos.

Realizamos la exportación de todos las modificaciones realizadas al conjunto de datos.

  • Ejemplo 1.16: csv (Comma Separated Values)
help(write_csv)
write_csv(Municipios, "Municipios.csv")
  • Ejemplo 1.17: tsv (Tabluar Separated Values)
help(write_tsv)
write_tsv(Municipios, "Municipios.tsv")
  • Ejemplo 1.18: xlsx (archivo excel)
library(openxlsx)
write.xlsx(Municipios, "Municipios.xlsx")
  • Ejemplo 1.19: archivos tipo text
help(write_delim)
write_delim(Municipios, "Municipios.txt", delim="@")

Sesión 2: Unión, agrupamiento y manipulación de conjuntos de datos usando dplyr.

A continuación se muestra el proceso de instalación de la librería dplyr asi como su ayuda. Esta tiene la particularidad de usar un operador %>% el cual se conoce como pip y se puede entender como el que indica que sigue otra operación teniendo en cuenta lo que se realizó en la linea anterior.

install.packages("dplyr")
library("dplyr")
help(dplyr)

Agrupamiento y manipulación

En el ejemplo que sigue haremos uso del marco de datos Municipios que se exportó en la Sesión 1 después de haberse realizado la limpieza.

  • Ejemplo 2.1: Conjunto de datos con las variables Departamento, Dep, Depmun, Municipio, Superficie y Poblacion que incluye todos los municipios de los departamentos de Antioquia y Caldas con más de 20 mil habitantes y donde los mismos están ordenados con respecto a Dep y de mayor a menor con respecto a Poblacion.
library(dplyr)
Municipios %>%
  select(Depto, Dep, Codmun, Mcpio, Super, Pob) %>%
  filter(Dep %in% c("05", "17") & Pob > 20000) %>%
  arrange(Dep,desc(Pob)) %>% 
  head(n = 5) %>%
  as.data.frame()
      Depto Dep Codmun    Mcpio     Super     Pob
1 Antioquia  05  05001 Medellín 374.83062 2483545
2 Antioquia  05  05088    Bello 147.75842  538527
3 Antioquia  05  05360   Itagui  19.64956  283794
4 Antioquia  05  05266 Envigado  77.99670  236114
5 Antioquia  05  05615 Rionegro 195.94015  139553
  • Ejemplo 2.2: Conjunto de datos con las variables Departamento, Dep, Depmun, Municipio, Superficie y Poblacion que incluye todos los municipios con superficies menores a 300km\(^2\) excepto los de Boyacá y Santander, y que están ordenados por Dep y de mayor a menor por Poblacion. Incluye una variable definida como “Baja” para los municipios con denspobl menor a 30, “Alta” para los municipios con denspobl mayor a 85, y “Media” para los demás.
Municipios %>%
  mutate(denspobl = Pob/Super) %>%
  filter(Dep != "15" & Dep != "68" & Super < 300) %>%
  mutate(denspoblC=case_when(denspobl > 85 ~ "Alta",
                             denspobl < 30 ~ "Baja",
                             TRUE ~ "Media")) %>%
  arrange(Dep,desc(Pob)) %>%
  select(Depto, Dep, Codmun, Mcpio, Super, Pob, denspoblC) %>%
  head(n = 5) %>%
  as.data.frame()
      Depto Dep Codmun    Mcpio     Super    Pob denspoblC
1 Antioquia  05  05088    Bello 147.75842 538527      Alta
2 Antioquia  05  05360   Itagui  19.64956 283794      Alta
3 Antioquia  05  05266 Envigado  77.99670 236114      Alta
4 Antioquia  05  05615 Rionegro 195.94015 139553      Alta
5 Antioquia  05  05631 Sabaneta  15.83532  85484      Alta
  • Ejemplo 2.3: ¿Existen dos o más municipios con el mismo Codmun?
Municipios %>% 
  group_by(Codmun) %>%
  summarise(repetidos = n()) %>% 
  filter(repetidos > 1)
# A tibble: 0 × 2
# ℹ 2 variables: Codmun <chr>, repetidos <int>
  • Ejemplo 2.4: ¿De los departamentos con dos o más municipios cual es el menos homogeneo (de acuerdo al coeficiente de variación) con respecto a la superficie de los municipios que lo componen?
Municipios %>% 
  group_by(Dep,Depto) %>%
  summarise(mun=n(),cv = sd(Super)/mean(Super)) %>%
  filter(mun > 1) %>% 
  arrange(desc(cv)) %>%
  head(n=1)
# A tibble: 1 × 4
# Groups:   Dep [1]
  Dep   Depto     mun    cv
  <chr> <chr>   <int> <dbl>
1 18    Caquetá    16  1.94
  • Ejemplo 2.5: Conjunto de datos con todos los departamentos, ordenados de mayor a menor con respecto a su población, que incluye la siguiente información: número de municipios, superficie, población, densidad poblacional, índice de ruralidad promedio ponderado por población, y una variable igual a “Grande” para los departamentos con población mayor a 1.5 millones de habitantes, “Pequeño” para departamentos con población menor a 300 mil habitantes, y “Mediano” para el resto.
Municipios %>%
  group_by(Dep, Depto) %>%
  summarise(nmunicipios=n(), totpob=sum(Pob),
            totsup = sum(Super),denspob=totpob/totsup,
            Irural = sum(Irural*Pob)/totpob,
            totpobC = case_when(totpob > 1.5e6 ~ "Grande",
                              totpob < 3e5 ~ "Pequeño",
                              TRUE ~ "Mediano")) %>%
  arrange(desc(totpob)) %>%
  head(n = 5) %>%
  as.data.frame()
  Dep           Depto nmunicipios  totpob    totsup   denspob   Irural totpobC
1  11      Bogotá D C           1 7592871  1622.853 4678.7188  6.00000  Grande
2  05       Antioquia         125 6550206 62804.709  104.2948 21.31877  Grande
3  76 Valle Del Cauca          42 4506768 20665.545  218.0813 22.57578  Grande
4  25    Cundinamarca         116 3085522 22370.489  137.9282 26.53255  Grande
5  08       Atlántico          23 2638151  3315.752  795.6418 10.92753  Grande
  • Ejemplo 2.6:¿cual es la región con el mayor número de departamentos?
Municipios %>% 
  group_by(Region) %>%
  summarise(Deps=n_distinct(Dep)) %>%
  arrange(desc(Deps)) %>%
  head(n=1)
# A tibble: 1 × 2
  Region  Deps
  <chr>  <int>
1 Caribe     8
  • Ejemplo 2.7: Conjunto de datos con los municipios cuyos nombres constan de una sola palabra e inician con “A” (con o sin tilde) o terminan con “o” (con o sin tilde).
Municipios %>% 
  select(Dep, Depto, Mcpio, Codmun) %>%
  filter(str_detect(Mcpio,"^(A|Á)|(o|ó)$") & str_count(Mcpio," ") == 0) %>%
  head(n = 5) %>%
  as.data.frame()
  Dep     Depto      Mcpio Codmun
1  05 Antioquia  Abejorral  05002
2  05 Antioquia   Abriaquí  05004
3  05 Antioquia Alejandría  05021
4  05 Antioquia      Amagá  05030
5  05 Antioquia     Amalfi  05031

Unión de conjunto de datos

Tomado de: Rahman, Z. (2020). JOINs [Image]. LearnSQL. https://learnsql.com/blog/learn-and-practice-sql-joins/.

A <- data.frame(id=c(1,2,2,3), cod=c(23,65,60,87),
                name=c("Leia","Luke","Yoda","Hank"))
B <- data.frame(id=c(2,3,4), cod=c(65,87,29), age=c(23,20,25))
A
  id cod name
1  1  23 Leia
2  2  65 Luke
3  2  60 Yoda
4  3  87 Hank
B
  id cod age
1  2  65  23
2  3  87  20
3  4  29  25
A %>% inner_join(B, by=c("id"="id","cod"="cod"))
  id cod name age
1  2  65 Luke  23
2  3  87 Hank  20
B %>% inner_join(A, by=c("id"="id","cod"="cod"))
  id cod age name
1  2  65  23 Luke
2  3  87  20 Hank
A %>% left_join(B, by=c("id"="id","cod"="cod"))
  id cod name age
1  1  23 Leia  NA
2  2  65 Luke  23
3  2  60 Yoda  NA
4  3  87 Hank  20
A %>% right_join(B, by=c("id"="id","cod"="cod"))
  id cod name age
1  2  65 Luke  23
2  3  87 Hank  20
3  4  29 <NA>  25
A %>% full_join(B, by=c("id"="id","cod"="cod"))
  id cod name age
1  1  23 Leia  NA
2  2  65 Luke  23
3  2  60 Yoda  NA
4  3  87 Hank  20
5  4  29 <NA>  25

A continuación tengase en cuenta el siguiente conjunto de datos Brasil contiene tres hojas: Municipios, Estados y Costeros. La hoja Municipios lista los municipios del Brasil, en que las columnas CODESTAD, CODMUNIC, NOMMUNIC, POB y SUP corresponden a código del estado, código del municipio, nombre del municipio, número de habitantes y superficie (en kilómetros cuadrados), respectivamente. La hoja Estados lista los estados del Brasil, en que las columnas CODESTAD, NOMESTAD y REGIAO corresponden a código del estado, nombre del estado y región, respectivamente. La hoja Costeros lista los municipios costeros del Brasil, en que la columna CODMUNIC corresponde al código del municipio. Tenga en cuenta que los nombres de los municipios, estados y regiones están escritos en portugués, y en esa lengua, a diferencia del español, se tiene que: (i) existen los siguientes acentos: â, ê, ô, á, é, í, ó, ú, ã, õ, à; (ii) no existe la letra ñ; y (iii) existe la letra ç.

A continuación se crea una función para “limpiar” la base de datos, la cual recibe un vector x el cual se le aplica lo visto en la Sesión 1 y regresa el vector arreglado.

Municipalidad <- read_excel("Datos/Brasil.xlsx", sheet = 1)
head(Municipalidad, n = 5)
# A tibble: 5 × 5
  CODESTAD CODMUNIC NOMMUNIC                    POB   SUP
     <dbl>    <dbl> <chr>                     <dbl> <dbl>
1       11  1100015 A/+lta Flor?esta D'Oeste  22516 7067.
2       11  1100023 Ar¡*i/quemes             111148 4427.
3       11  1100031 Cab¡i^xi¡                  5067 1314.
4       11  1100049 Cac$oa{|l                 86416 3793 
5       11  1100056 Ce~:rej=eiras             16088 2783.
Estados <- read_excel("Datos/Brasil.xlsx", sheet = 2)
head(Estados, n = 5)
# A tibble: 5 × 3
  NOMESTAD    REGIAO       CODESTAD
  <chr>       <chr>           <dbl>
1 Ro|n¿dônia* Região Norte       11
2 Ac(=re)     Região Norte       12
3 Am]^azonas¡ Região Norte       13
4 R_(orai=ma  Região Norte       14
5 Par_?~á     Região Norte       15
Costeros <- read_excel("Datos/Brasil.xlsx", sheet = 3)
Costeros <- within(Costeros, Costero <- 1)
head(Costeros, n = 5)
# A tibble: 5 × 2
  CODMUNIC Costero
     <dbl>   <dbl>
1  4323804       1
2  4323002       1
3  4322327       1
4  4321832       1
5  4321667       1
clean <- function(x){
  x <- str_to_lower(x)
  x <- str_replace_all(x,"[^a-zâêôáéíóúãõàç'\\- ]","")
  x <- str_squish(x)
  x <- str_to_title(x)
  return(x)
}

Municipalidad <- Municipalidad %>%
  mutate(NOMMUNIC = clean(NOMMUNIC)) %>%
  as.data.frame()

Estados <- Estados %>%
  mutate(NOMESTAD = clean(NOMESTAD),
         REGIAO = clean(REGIAO)) %>%
  as.data.frame()

Ahora, procede unir los tres conjuntos de datos por medio del identificador (id) que tiene cada uno de marco de datos. ¿Cuál es el tipo de unión que nos sirve para este caso?

Municipalidad2 <- Municipalidad %>% left_join(Estados, by = c("CODESTAD" = "CODESTAD")) %>%
  as.data.frame()
head(Municipalidad2, n = 5)
  CODESTAD CODMUNIC              NOMMUNIC    POB      SUP NOMESTAD       REGIAO
1       11  1100015 Alta Floresta D'oeste  22516 7067.127 Rondônia Região Norte
2       11  1100023             Ariquemes 111148 4426.571 Rondônia Região Norte
3       11  1100031                Cabixi   5067 1314.352 Rondônia Região Norte
4       11  1100049                Cacoal  86416 3793.000 Rondônia Região Norte
5       11  1100056            Cerejeiras  16088 2783.300 Rondônia Região Norte
Municipalidad3 <- Municipalidad2 %>% left_join(Costeros, by =c("CODMUNIC" = "CODMUNIC")) %>%
  mutate(Costero = ifelse(is.na(Costero), 0, Costero)) %>% 
  as.data.frame()
head(Municipalidad3, n = 5)
  CODESTAD CODMUNIC              NOMMUNIC    POB      SUP NOMESTAD       REGIAO
1       11  1100015 Alta Floresta D'oeste  22516 7067.127 Rondônia Região Norte
2       11  1100023             Ariquemes 111148 4426.571 Rondônia Região Norte
3       11  1100031                Cabixi   5067 1314.352 Rondônia Região Norte
4       11  1100049                Cacoal  86416 3793.000 Rondônia Região Norte
5       11  1100056            Cerejeiras  16088 2783.300 Rondônia Região Norte
  Costero
1       0
2       0
3       0
4       0
5       0
  • Ejemplo 2.8: ¿Cuál es la región (REGIAO) con el menor número de municipios?
Municipalidad3 %>%
  group_by(REGIAO) %>%
  summarise(Nmun = n()) %>%
  arrange(Nmun)
# A tibble: 5 × 2
  REGIAO               Nmun
  <chr>               <int>
1 Região Norte          450
2 Região Centro-Oeste   467
3 Região Sul           1191
4 Região Sudeste       1668
5 Região Nordeste      1794
  • Ejercicio 2.1: ¿Cuál es la región (REGIAO) con la mayor población?

  • Ejercicio 2.2: ¿Cuál es la región (REGIAO) con la menor superficie?

  • Ejemplo 2.9: ¿Cuál es el estado (CODESTAD y NOMESTAD) con el mayor número de municipios costeros?

Municipalidad3 %>%
  filter(Costero==1) %>%
  group_by(CODESTAD, NOMESTAD) %>%
  summarise(Costeros = n()) %>%
  arrange(desc(Costeros))
# A tibble: 17 × 3
# Groups:   CODESTAD [17]
   CODESTAD NOMESTAD            Costeros
      <dbl> <chr>                  <int>
 1       29 Bahia                     53
 2       15 Pará                      47
 3       42 Santa Catarina            41
 4       21 Maranhão                  40
 5       43 Rio Grande Do Sul         39
 6       33 Rio De Janeiro            33
 7       24 Rio Grande Do Norte       29
 8       27 Alagoas                   27
 9       28 Sergipe                   24
10       23 Ceará                     23
11       32 Espírito Santo            18
12       26 Pernambuco                17
13       35 São Paulo                 16
14       25 Paraíba                   13
15       16 Amapá                     11
16       41 Paraná                     7
17       22 Piauí                      5
  • Ejercicio 2.3: ¿Cuál es el estado (CODESTAD y NOMESTAD) con la mayor superficie en municipios costeros?

  • Ejercicio 2.4: ¿Cuál es el estado (CODESTAD y NOMESTAD) con la mayor población en municipios costeros?

  • Ejercicio 2.5: ¿Cuál es el estado (CODESTAD y NOMESTAD) con la mayor densidad poblacional en municipios costeros?

Sesión 3: Visualización de gráficas usando ggplot2.

ggplot2 es la mejor librería para graficar en R, ya que genera graficas más estéticas que las funciones de base. Además que su uso es intuitivo y permite potenciar el proceso de análisis de datos. Antes de iniciar es importante instalar y llamar ggplot2. Para los ejemplos que siguen téngase en cuenta el siguiente el conjunto de datos Advertising.

#install.packages("ggplot2")
library(ggplot2)
advertising <- read_excel("Datos/Advertising.xlsx")
str(advertising)
tibble [200 × 4] (S3: tbl_df/tbl/data.frame)
 $ TV       : num [1:200] 230.1 44.5 17.2 151.5 180.8 ...
 $ radio    : num [1:200] 37.8 39.3 45.9 41.3 10.8 48.9 32.8 19.6 2.1 2.6 ...
 $ newspaper: num [1:200] 69.2 45.1 69.3 58.5 58.4 75 23.5 11.6 1 21.2 ...
 $ sales    : num [1:200] 22.1 10.4 9.3 18.5 12.9 7.2 11.8 13.2 4.8 10.6 ...
ggplot(advertising,aes(x = TV, y = sales)) +
       geom_point() +
        labs(title = "Ventas versus inversión en publicidad en TV", 
             x = "Publicidad en TV", y = "Ventas") +
       theme_gray()

La figura anterior, es muy básica, por lo tanto a continuación se personaliza el gráfico anterior. Utilizando el color para especificar el color de los puntos, shape para la forma de estos y size para el tamaño. Dentro de aes proviene de aesthetic el describe cómo se asignan las variables de los datos a las visuales. En labs se establece todo lo relacionado con las etiquetas. Y en theme controla casi cualquier elemento visual de la gráfica que no esté controlado por el datos y, por lo tanto, es importante para la apariencia.

ggplot(advertising,aes(x = TV, y = sales)) +
  geom_point(color = "red", shape = 16, size = 3) +
  labs(title = "Ventas versus inversión en publicidad en TV", x = "Publicidad en TV", y = "Ventas") +
  scale_x_continuous(breaks = seq(from=0, to=300, by=50)) +
  scale_y_continuous(breaks=seq(from=0, to=30, by=5)) +
  theme(plot.title = element_text(family="sans", face="bold", size=20, vjust=0.5, 
                                  hjust=0.5, color="black", angle=0),
        axis.title.x = element_text(family="sans", face="bold", size=18, vjust=0.5, 
                                    hjust=0.5, color="blue", angle=0),
        axis.title.y = element_text(family="sans", face="bold", size=18, vjust=0.5, 
                                    hjust=0.5, color="blue", angle=0),
        axis.text.x = element_text(family="mono", face="bold", size=14, vjust=0.5, 
                                   hjust=0.5, color="black", angle=0),
        axis.text.y = element_text(family="mono", face="bold", size=14, vjust=0.5, 
                                   hjust=0.5, color="black", angle=0),
        panel.background = element_rect(fill="gray92"),
        panel.grid.major = element_line(color="white", size=1),
        panel.grid.minor = element_line(color="white", size=0.5)
        ) 

Ahora, crearemos un tema personalizado para el gráfico.

mitema <- theme(plot.title=element_text(family="sans", face="bold", size=20, 
                                         vjust=0.5, hjust=0.5, color="black", angle=0),
                axis.title.x=element_text(family="sans", face="bold", size=18, 
                                          vjust=0.5, hjust=0.5, color="blue", angle=0),
                axis.title.y=element_text(family="sans", face="bold", size=18,
                                          vjust=0.5, hjust=0.5, color="blue", angle=90),
                axis.text.x=element_text(family="mono", face="bold", size=14, 
                                         vjust=0.5, hjust=0.5, color="black", angle=0),
                axis.text.y=element_text(family="mono", face="bold", size=14, 
                                         vjust=0.5, hjust=0.5, color="black", angle=0),
                panel.background=element_rect(fill="gray92"),
                panel.grid.major=element_line(color="white",size=1),
                panel.grid.minor=element_line(color="white",size=0.5)
                )
  • hjust, vjust: valores en el intervalo [0,1]
  • face: “plain”, “bold”, “italic”, “bold.italic”
  • legend.position: “none”, “left”, “right”, “bottom”, “top”.
  • legend direction: “horizontal”, “vertical”.
  • Ahora creamos un gráfico de dispersión de las ventas del producto (sales) versus la inversión en publicidad en televisión para el mismo (TV). Se usan colores y marcadores diferentes dependiendo de la inversión en publicidad en radio (radioC).
names <- c("Baja","Media","Alta")
advertising <- within(advertising,radioC <- cut_number(radio,n=3,labels=names))
with(advertising,table(radioC))
radioC
 Baja Media  Alta 
   68    65    67 
ggplot(advertising, aes(x=TV, y=sales, color=radioC, shape=radioC)) +
  geom_point(size=3.5) +
  labs(title="Ventas versus inversión en publicidad para TV",x="Publicidad para TV",
       y="Ventas",color="Publicidad\n en radio",shape="Publicidad\n en radio") +
       scale_x_continuous(breaks=seq(from=0, to=300, by=50)) +
       scale_y_continuous(breaks=seq(from=0, to=30, by=5)) +
       scale_color_manual(values=c("blue","black","red")) +
       scale_shape_manual(values=c(15,16,17)) + mitema

Ahora añadimos una regresión polinómica en cada uno a las ventas por publicidad en TV y publicidad en radio.

ggplot(advertising, aes(x=TV, y=sales, color=radioC, shape=radioC)) +
  geom_point(size=3.5) +
  geom_smooth(method="lm", formula=y ~ poly(x,degree=3), se=FALSE, size=1, linetype="solid") +
  labs(title="Ventas versus inversión en publicidad para TV",x="Publicidad para TV",
       y="Ventas",color="Publicidad\n en radio",shape="Publicidad\n en radio") +
       scale_x_continuous(breaks=seq(from=0, to=300, by=50)) +
       scale_y_continuous(breaks=seq(from=0, to=30, by=5)) +
       scale_color_manual(values=c("blue","black","red")) +
       scale_shape_manual(values=c(15,16,17)) + mitema

  • shape: entero en el intervalo [0,25].
  • size: números no negativos.
  • linetype: “solid”, “dashed”, “dotted”, “dotdash”, “longdash”, “twodash”.
  • se: TRUE, FALSE.

PREGUNTA: ¿cómo haría si quiero un tipo de linea por cada valor de la publicidad en radio?

Se crea una nueva variable newspaperC de 4 niveles, por cada nivel se grafica la ventas en función de la inversión de publicidad en TV diferenciado por el nivel de publicidad en radio con su respectiva linea de tendencia polinómica.

names <- c("Baja", "Media baja", "Media alta", "Alta")
advertising <- within(advertising,newspaperC <- cut_number(newspaper, n=4, labels=names))
with(advertising,table(newspaperC))
newspaperC
      Baja Media baja Media alta       Alta 
        50         50         51         49 
ggplot(advertising,aes(x=TV, y=sales, color=radioC)) +
  geom_point(size=3.5, shape=16) +
  geom_smooth(method="lm", formula=y ~ poly(x,degree=3), se=FALSE, size=1, linetype="solid") + 
  labs(title="Ventas versus inversión en publicidad para TV", x="Publicidad para TV",
       y="Ventas", subtitle="Publicidad en periodico", color="Publicidad\n en radio") +
  scale_color_manual(values=c("blue", "black", "red")) +
  facet_wrap(vars(newspaperC), ncol=2, nrow=2, strip.position="top",dir="h", scales="free") + mitema + 
  theme(plot.subtitle=element_text(face="bold", size=16, vjust=0.5, hjust=0.5, color="salmon"),
        strip.background=element_rect(color="black", fill="gray80", size=1),
        strip.text=element_text(face="bold", size=13, vjust=0.5, hjust=0.5, color="black", angle=0)
        )

  • scales: “fixed”, “free”, “free_x”, “free_y”.
  • strip.position: “top”, “bottom”, “left”, “right”.

En estos datos, sacados de Diggle P.J., Heagarty P., Liang K.-Y. y Zeger S.L. (2002) Analysis of Longitudinal Data. Oxford University Press, Oxford, el principal objetivo del análisis es evaluar el efecto de la contaminación por ozono en el crecimiento de los árboles. En un primer grupo (treat=“normal”), se cultivaron un total de 54 árboles en una atmósfera enriquecida con ozono, mientras que en un segundo grupo (treat=“enriquecida con ozono”), 25 árboles se cultivaron en una atmósfera normal. El tamaño de cada árbol (size) se observó 13 veces a lo largo del tiempo (days), es decir, 152, 174, 201, 227, 258, 469, 496, 528, 556, 579, 613, 639 y 674 días desde el inicio del experimento.

#install.packages("glmtoolbox")
library(glmtoolbox)
data(spruces)
str(spruces)
'data.frame':   1027 obs. of  4 variables:
 $ tree : Factor w/ 79 levels "N1T01","N1T02",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ days : num  152 174 201 227 258 469 496 528 556 579 ...
 $ size : num  92.8 156 177.7 223.6 225.9 ...
 $ treat: Factor w/ 2 levels "normal","ozone-enriched": 1 1 1 1 1 1 1 1 1 1 ...
ggplot(spruces,aes(x=factor(days), y=size, fill=treat)) +
  geom_boxplot(outlier.shape=16, outlier.color="red", outlier.size=1, color="black", linetype="solid") + 
  labs(x="Días desde el comienzo del experimento", y="Tamaño", fill="Atmósfera") + 
  scale_fill_manual(values=c("salmon2","green2")) + mitema + 
  theme(legend.position="right",
        legend.text=element_text(size=14,hjust=0),
        legend.title=element_text(face="bold",size=15)
        )

  • Adicione + coord_flip() al final de código.
  • Reemplace geom_boxplot() por geom_violin().

Sesión 4: Continuación de visualización de gráficas y generación de mapas usando ggplot2.

En esta sección, exploraremos la tarea de la cartografía mediante el uso de las herramientas que nos ofrecen las bibliotecas sf, ggplot2, y dplyr en R. Estas herramientas nos permiten importar, manipular y visualizar datos espaciales de manera eficiente y elegante. La biblioteca sf nos brinda la capacidad de importar archivos que contienen información geoespacial, representada comúnmente como polígonos que definen áreas geográficas. Con sf, podemos cargar datos espaciales en formato simple y flexible. Y por último, nos apoyaremos en ggplot2 para la visualización y generación de los mapas.

install.packages("sf")
library("sf")
help("sf")

A continuación téngase en cuenta el conjunto de datos Municipios trabajados en la sesión 1 y los archivos en esta carpeta, los cuales servirán de insumos para el mapa a continuación.

depto <- Municipios %>% group_by(Depto, Dep) %>%
  summarise(Irural=sum(Pob*Irural)/sum(Pob)) %>%
  as.data.frame()

deptoshp <- st_read("Datos/Mapas/COL/MGN_DPTO_POLITICO.shp",quiet=TRUE)

todo <- deptoshp %>% left_join(depto, by = c('DPTO_CCDGO' = 'Dep')) %>%
  select(DPTO_CCDGO, Depto, Irural, geometry) %>%
  st_as_sf()

head(todo)
Simple feature collection with 6 features and 3 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -77.12783 ymin: 3.730633 xmax: -71.94885 ymax: 11.10537
Geodetic CRS:  MAGNA-SIRGAS
  DPTO_CCDGO      Depto   Irural                       geometry
1         05  Antioquia 21.31877 MULTIPOLYGON (((-76.41355 8...
2         08  Atlántico 10.92753 MULTIPOLYGON (((-74.84946 1...
3         11 Bogotá D C  6.00000 MULTIPOLYGON (((-74.07059 4...
4         13    Bolívar 31.39577 MULTIPOLYGON (((-76.17318 9...
5         15     Boyacá 37.21662 MULTIPOLYGON (((-72.17368 7...
6         17     Caldas 29.41638 MULTIPOLYGON (((-74.67154 5...

A continuación se presentan el mapa de los departamentos de Colombia que muestra las diferencias entre el Índice de ruralidad ponderado por población. Se destacan los nombres de los departamentos con un índice de ruralidad mayor a 70.

ggplot(data = todo, aes(fill = Irural)) +
  geom_sf() + geom_sf_text(aes(label = ifelse(Irural >70, Depto, "")),
                           col = "black", fontface="bold", size=4,
                           fun.geometry = function(x) st_centroid(x)) + 
  labs(x = "Longitud", y ="Latitud", title = "Colombia", fill = "Índice de\nRuralidad") +
  scale_fill_gradient(low = "white", high = "red", n.breaks = 5) + 
  theme(plot.title = element_text(hjust = 0.5, face = "bold"))

El mapa anterior está bien, pero lo podríamos mejorar para que sea veas más estético como añadirle los países vecinos, añadir el colo de los océanos.

mundo <- st_read("Datos/Mapas/Mundo/Paises_Mundo.shp", quiet = TRUE)
mundocol <- mundo %>%
  filter(PAÍS %in% c("Perú","Brasil","Venezuela","Ecuador","Panamá"))
box <- st_bbox(todo)
ggplot() + geom_sf(data = mundocol) + 
  geom_sf(data = todo, aes(fill = Irural)) + 
  coord_sf(xlim=c(box$xmin,box$xmax), ylim=c(box$ymin,box$ymax), expand=FALSE) + 
  labs(x = "Longitud", y ="Latitud", title = "Colombia", fill = "Índice de\nRuralidad") +
  scale_fill_gradient(low = "white", high = "red", n.breaks = 5) + 
  theme(plot.title = element_text(hjust = 0.5, face = "bold")) + 
  annotate("text", x=c(-74.5,-68,-78,-69,-78.5), y=c(-2.5,0,-1,9,9), colour="blue",
           label=c("Perú","Brasil","Ecuador","Venezuela","Panamá")) + 
  theme(panel.background=element_rect(fill="lightblue"))

En en el siguiente enlace se encuentran los puntajes promedios de las pruebas saber 11 del período 2022-2. Por lo tanto, realizamos una clasificación de los departamentos en 5 grupos teniendo en cuenta los puntajes usando la tecnica de kmeans.

icfes <- readxl::read_excel("Datos/IcfesDepto.xlsx")
set.seed(1)
clusters <- kmeans(icfes$Media, centers = 5)
icfes$cluster <- clusters$cluster
centros <- clusters$centers
icfes$etiquetas <- paste("Grupo", icfes$cluster, ": ", round(centros[icfes$cluster], 0))
icfes %>% right_join(deptoshp, by = c('Dep' = 'DPTO_CCDGO')) %>%
  filter(Dep != "88") %>%
  select(Dep, Depto, Media, cluster, etiquetas, geometry) %>%
  st_as_sf() %>%
  ggplot() + geom_sf(aes(fill=etiquetas)) + 
  scale_fill_manual(values =  c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd")) +
  labs(title = 'Agrupamiento de Departamentos', x = 'Longitud', y = 'Latitud', fill = '') + 
  theme(plot.title = element_text(hjust = 0.5, face = 'bold'),
        legend.position = "bottom")

  • Ejercicio: en el siguiente enlace se encuentra el Indice de la Pobreza Monetaria - IPM para el año 2018 en colombia. En este solo se obutvó información de 23 de los 32 departamentos de Colombia. Por lo tanto, realice un mapa donde se muestre la diferencia de IPM entre los departamentos del país y que se muestren los países vecinos.

REFERENCIAS