options(warn = -1)
suppressWarnings(suppressPackageStartupMessages(library(kableExtra)))
# Crear los datos
categorias <- c("Gratuito", "Usuarios", "Enfoque", "Ajuste de Modelos",
"Visualización GrÔfica", "Interacción con la Nube", "Documentación")
R <- c("Gratuito", "AcadĆ©micos", "EstadĆstico", "SĆ", "SĆ", "SĆ", "Buena")
Python <- c("Gratuito", "Desarrolladores", "Multipropósito", "En desarrollo",
"En desarrollo", "SĆ", "Menos completa")
# Crear el data frame combinando los datos
df_combined <- data.frame(CategorĆa = categorias, R = R, Python = Python)
# Crear la tabla utilizando kableExtra con mayor ancho
df_combined %>%
kbl(col.names = c("CategorĆa", "R", "Python"), booktabs = TRUE, align = "l") %>%
kable_styling(full_width = FALSE, position = "center", font_size = 24) %>%
row_spec(0, bold = TRUE, background = "#D3D3D3") %>%
kable_paper("striped", full_width = F) %>%
column_spec(1, width = "8cm") %>% # Ajustar ancho de la primera columna
column_spec(2, width = "8cm") %>% # Ajustar ancho de la segunda columna
column_spec(3, width = "8cm") # Ajustar ancho de la tercera columna
| CategorĆa | R | Python |
|---|---|---|
| Gratuito | Gratuito | Gratuito |
| Usuarios | AcadƩmicos | Desarrolladores |
| Enfoque | EstadĆstico | Multipropósito |
| Ajuste de Modelos | SĆ | En desarrollo |
| Visualización GrÔfica | Sà | En desarrollo |
| Interacción con la Nube | Sà | Sà |
| Documentación | Buena | Menos completa |
Para comenzar a usar R, el primer paso es instalarlo en tu computadora. R es compatible con casi todas las plataformas, incluyendo los sistemas operativos mƔs comunes. Windows, Mac OS X y Linux. Links de descarga para R y RStudio.
RStudio es un entorno de desarrollo integrado (IDE) disponible para R, el cual tiene un buen editor con resaltado de sintaxis, un visor de objetos de R y un gran nĆŗmero de caracterĆsticas agradables que estĆ”n integradas.Ademas, esta dedicado a la computación estadĆstica y grĆ”ficos.
El Tidyverse es una colección de paquetes del R que permiten preparar, procesar y graficar bases de datos. Se destacan los siguientes:
ggplot: permite crear visualizaciones elegantes de los datos de una manera relativamente sencilla.
stringr: permite manipular cadenas de caracteres con el fin de realizar sustituciones, detectar duplicados, analizar patrones, etc.
tidyr: tiene como
objetivo obtener datos ordenados. Destacan funciones como
gather para crear factores con base en nombres de columnas
y separate para crear factores separando los caracteres de
una columna.
readr: permite importar
y exportar bases de datos en diferentes formatos y tiene implementada la
función problems que detecta problemas en nuestras
bases.
Para mÔs información visitar la pÔgina web:
https://www.tidyverse.org/packages/
# Crear los datos
comandos <- c("x == y", "x != y", "x > y", "x < y", "x >= y", "x <= y", "&", "|", "!", "isTRUE(A)")
significado <- c("x es igual a y", "x no es igual a y", "x es mayor que y", "x es menor que y",
"x es mayor o igual que y", "x es menor o igual que y", "y", "o", "No", "EvalĆŗa si A es cierta")
# Crear el data frame
tabla <- data.frame(Comando = comandos, Significado = significado)
# Visualizar la tabla utilizando kableExtra con mayor ancho
tabla %>%
kbl(col.names = c("Comando", "Significado"), booktabs = TRUE) %>%
kable_styling(full_width = TRUE, position = "center", font_size = 24) %>%
row_spec(0, bold = TRUE, background = "#D3D3D3") %>%
kable_paper("striped", full_width = F) %>%
column_spec(1, width = "6cm") %>% # Ajustar ancho de la primera columna
column_spec(2, width = "12cm") # Ajustar ancho de la segunda columna
| Comando | Significado |
|---|---|
| x == y | x es igual a y |
| x != y | x no es igual a y |
| x > y | x es mayor que y |
| x < y | x es menor que y |
| x >= y | x es mayor o igual que y |
| x <= y | x es menor o igual que y |
| & | y |
# Asignación de valores
a <- 1
b <- 3
# Operaciones y comentarios explicativos
# Āæb es diferente de a?
b != a # TRUE
## [1] TRUE
# Āæes a igual a b?
isTRUE(a == b) # FALSE
## [1] FALSE
# Negar que a es menor que b
!(a < b) # FALSE
## [1] FALSE
# Āæes a menor que b o b menor que a?
(a < b | b < a) # TRUE
## [1] TRUE
# Āæes a menor o igual a b o b igual a a?
(a <= b & b == a) # FALSE
## [1] FALSE
R es un lenguaje orientado a objetos. Los objetos pueden ser usados para guardar valores y pueden madificarse mediante funciones como por ejemplo sumar dos objetos o calcular la media.
X <- 4
Y <- 2
Puedes usar el programa R como una calculadora, basta con conocer cuÔles son los signos y comandos a utilizar para realizar las operaciones. Copia los comandos en tu script de R y ejecútalos para ver los resultados.
#suma
Z <- X +Y
Z
## [1] 6
#multiplicación
2*2
## [1] 4
#división
2/2
## [1] 1
#potencia
4^2
## [1] 16
#raĆz cuadrada
sqrt(16)
## [1] 4
En R, la sintƔxis del condicional consiste en:
if (A): evalĆŗa si se cumple la
condición A.else if (B): si no se cumple la
condición o condiciones anteriores, entonces evalúe si se cumple la
condición B.else: si no se cumple ninguna de las
condiciones anteriores entonces haga lo siguiente.Ejemplo:
a<-9
if (a<0){
print("a es negativo")
}else if (a>0){
print("a es positivo")
}else{
print("a es igual a cero")
}
## [1] "a es positivo"
Usado para repetir un bloque especĆfico de código, siguiendo una secuencia dada.
suma<-0
for (i in 1:10){
suma<-suma+i
}
suma
## [1] 55
lista<-c("a","b","c","d")
for (j in lista){
print(j)
}
## [1] "a"
## [1] "b"
## [1] "c"
## [1] "d"
vector<-c(0.1, 0.3,-0.4, 1.5,-2.1)
for (k in vector){
print(abs(k))
}
## [1] 0.1
## [1] 0.3
## [1] 0.4
## [1] 1.5
## [1] 2.1
x1<-cbind(1:3,4:6) # Matriz 1
x2<-cbind(3:5,4:6) # Matriz 2
x3<-cbind(5:7,9:11) # Matriz 3
l1<-list(x1,x2,x3) # Lista de matrices
for(i in l1){
print(i)
}
## [,1] [,2]
## [1,] 1 4
## [2,] 2 5
## [3,] 3 6
## [,1] [,2]
## [1,] 3 4
## [2,] 4 5
## [3,] 5 6
## [,1] [,2]
## [1,] 5 9
## [2,] 6 10
## [3,] 7 11
# Imprimir solo los impares del 1 al 10
for (i in 1:10) {
if (i %% 2 == 0) {
next # Saltar los nĆŗmeros pares
}
print(i) # Imprimir los nĆŗmeros impares
}
## [1] 1
## [1] 3
## [1] 5
## [1] 7
## [1] 9
Usado para repetir un bloque especĆfico de código siempre que una condición dada sea verdadera.
a <- 1
while (a < 5) {
a <- a + 1
print(a)
}
## [1] 2
## [1] 3
## [1] 4
## [1] 5
a<-1
b<-3
while(a<15 &b>0.2){
a<-a+1
b<-round(b/2,2)
print(c(a,b))
}
## [1] 2.0 1.5
## [1] 3.00 0.75
## [1] 4.00 0.38
## [1] 5.00 0.19
El bucle repeat funciona igual que el while, pero considera un bucle infinito que se ārompeā con un break.
a <- 1
b <- 3
repeat {
a <- a + 1
b <- round(b / 2, 2)
print(c(a, b))
if (a > 15 | b < 0.2) {
break
}
}
## [1] 2.0 1.5
## [1] 3.00 0.75
## [1] 4.00 0.38
## [1] 5.00 0.19
En una función tenemos tres tipos de elementos:
Argumentos: tambiƩn conocidos como valores de entrada.
Cuerpo: operaciones que han de realizarse. Se deben localizar entre corchetes.
Resultado: también conocidos como valores de salida y que se ubican en la última expresión que se ejecuta.
Nota: Tratar al mÔximo de no usar nombres de posibles funciones que ya existen para no entrar en conflictos con los códigos en R.
# Sumar los nĆŗmeros impares hasta un m:
sumar<-function(m){
sum1<-0
for (i in 1:m){
if(!i%%2){
next
}else{
sum1<-sum1+i
}
}
return(sum1)
}
sumar(5)
## [1] 9
sumar(7)
## [1] 16
Ejemplo de una función en R
multiplos<-function(a,b,n){
a1<-vector() #MĆŗltiplos de a menores o iguales a n
b1<-vector() #MĆŗltiplos de b menores o iguales a n
ca1<-1
cb1<-1
for (i in 1:n){
if(!i%%a){
a1[ca1]<-i
ca1<-ca1+1
}
if(!i%%b){
b1[cb1]<-i
cb1<-cb1+1
}else{
next
}
}
return(list(Multiplos_a=a1,Multiplos_b=b1))
}
multiplos(3,5,20) # a=3, b=5, n=20
## $Multiplos_a
## [1] 3 6 9 12 15 18
##
## $Multiplos_b
## [1] 5 10 15 20
multiplos(4,7,35) # a=4, b=7, n=35
## $Multiplos_a
## [1] 4 8 12 16 20 24 28 32
##
## $Multiplos_b
## [1] 7 14 21 28 35
Una forma prƔctica para guardar y luego utilizar mis funciones de manera prƔctica consiste en:
Considerar un Ćŗnico archivo .R que
contenga todas las funciones que vamos creando.
Utilizar la función source para
leer el archivo con mis funciones y poder utilizarlas. Esta función
tiene como argumento principal la url de mi computador
donde he guardado mi archivo .R que contiene las funciones
creadas.
Ejmplo:
# Ejemplo de una función en R
sumar <- function(a, b) {
return(a + b)
}
restar <- function(a, b) {
return(a - b)
}
Cargar las funciones desde el archivo
source("ruta/a/tu/archivo/mis_funciones.R")
# Ahora puedes utilizar las funciones cargadas
resultado_suma <- sumar(3, 5)
resultado_resta <- restar(10, 4)
print(resultado_suma)
## [1] 8
print(resultado_resta)
## [1] 6
Como parte del desarrollo de habilidades fundamentales en programación y anÔlisis de datos, esta actividad tiene como objetivo que construyas tus propias funciones en R para resolver tareas comunes del anÔlisis de datos de manera automatizada, estructurada y reutilizable.
La base de datos que debes usar para esta actividad se encuentra disponible en el siguiente enlace:
base_ciencia_datos_estudiantes.csv
Puedes descargarla directamente desde el navegador o leerla directamente en R con la siguiente instrucción, usando el enlace RAW:
url <- "https://raw.githubusercontent.com/Kalbam/Datos/main/base_ciencia_datos_estudiantes.csv"
#datos <- read.csv(url)
a.Tenga como entradas un ādata frameā, una variable y un valor particular de la variable.
Identifique si la entrada correspondiente de la variable es un nombre o un nĆŗmero de columna.
Arroje como resultado un nuevo ādata frameā filtrado por el valor particular de la variable de interĆ©s.
Generalizar esta función con varias variables y varias categorĆas a la base de datos estudiada.
DiseƱa mĆnimo cinco (5) funciones propias en R que sean Ćŗtiles para el anĆ”lisis de datos. Las funciones pueden estar enfocadas en:
Aplica tus funciones a la base de datos proporcionada. Muestra ejemplos reales de uso y explica en quĆ© situaciones se utilizarĆa cada función.
Escribe y documenta tu código correctamente. Usa nombres claros para tus funciones, comenta su propósito y estructura adecuadamente tu script.
Debes entregar dos archivos separados:
mis_funciones.R o .Rmd: contiene
Ćŗnicamente las cinco funciones creadas por ti.uso_funciones.R o .Rmd: contiene ejemplos
donde llamas y aplicas esas funciones sobre un conjunto de datos.Lo primero que tenemos que hacer es cargar los paquetes que vamos a utilizar para el anƔlisis. En este caso vamos a usar:
library(tidyverse)# Incluye paquetes de importación, visualización entre otros
## āā Attaching core tidyverse packages āāāāāāāāāāāāāāāāāāāāāāāā tidyverse 2.0.0 āā
## ā dplyr 1.1.4 ā readr 2.1.5
## ā forcats 1.0.0 ā stringr 1.5.1
## ā ggplot2 3.5.2 ā tibble 3.2.1
## ā lubridate 1.9.4 ā tidyr 1.3.1
## ā purrr 1.0.4
## āā Conflicts āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā tidyverse_conflicts() āā
## ā dplyr::filter() masks stats::filter()
## ā dplyr::group_rows() masks kableExtra::group_rows()
## ā dplyr::lag() masks stats::lag()
## ā¹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)# Manipulación de Datos
library(ggplot2)# Visualización de datos
library(readxl)# Importación de datos
require(tibble)# Tablas
Recordar que si no ha instalado estos paquetes debe correr primero el
comando:
install.packages("nombre del paquete")
R ya incorpora una serie de bases de datos que te pueden resultar de utilidad para empezar a explorar las posibilidades de anĆ”lisis estadĆstico que te ofrece este programa.
Como ejemplo vamos a explorara la base de datos llamada ācarsā.
# Cargar la base
data(cars)
# Visualizar los encabezados
head(cars)
# Resumir con algunas estadĆsticas las variables de la base
summary(cars)
## speed dist
## Min. : 4.0 Min. : 2.00
## 1st Qu.:12.0 1st Qu.: 26.00
## Median :15.0 Median : 36.00
## Mean :15.4 Mean : 42.98
## 3rd Qu.:19.0 3rd Qu.: 56.00
## Max. :25.0 Max. :120.00
Puedes agregar fƔcilmente grƔficos a tu anƔlisis. Por ejemplo:
data(pressure)
head(pressure)
hist(pressure$temperature)
boxplot(pressure$temperature)
Dentro de un amplio nĆŗmero de extensiones que se pueden encontrar en bases de datos se encuentran:
Algunos paquetes utilizados para importar y exportar bases de datos en R son:
Algunos paquetes utilizados para importar y exportar bases de datos en R y Python son:
.csv..csv, Excel,
SAS, SPSS, html,
stata, sql, etc.Ejemplo: Para los ejemplos de esta sección utilizaremos un dataset que contiene información sobre el Hurto de Motocicletas
https://github.com/Kalbam/Datos/raw/main/Delito_Hurto_Motocicletas.csv
Otra forma
datos<-read.csv("Delito_Hurto_Motocicletas.csv",
sep=",",header=TRUE,
fileEncoding = "UTF-8")
Nombres de variables
names(datos)# nombres de variables
## [1] "FECHA" "DEPARTAMENTO" "MUNICIPIO" "DIA"
## [5] "HORA" "BARRIO" "ZONA" "CLASE.SITIO"
## [9] "EDAD" "GENERO" "ARMA.EMPLEADA" "MOVIL.AGRESOR"
## [13] "MOVIL.VICTIMA" "MARCA" "MODELO" "LINEA"
## [17] "COLOR" "ESTADO.CIVIL" "PROFESIONES" "ESCOLARIDAD"
## [21] "CODIGO.DANE" "X2015"
Dimensiones
dim(datos)
## [1] 27223 22
Categorias de Departamento
table(datos$DEPARTAMENTO)
##
## AMAZONAS ANTIOQUIA ARAUCA
## 92 25 6107 196
## ATLĆNTICO BOLĆVAR BOYACĆ CALDAS
## 1254 402 63 111
## CAQUETĆ CASANARE CAUCA CESAR
## 348 449 1584 1010
## CHOCĆ CĆRDOBA CUNDINAMARCA GUAINĆA
## 318 521 3402 3
## GUAJIRA GUAVIARE HUILA MAGDALENA
## 852 27 841 375
## META NARIĆO NORTE DE SANTANDER PUTUMAYO
## 854 721 1637 245
## QUINDĆO RISARALDA SAN ANDRĆS SANTANDER
## 155 295 74 584
## SUCRE TOLIMA VALLE VAUPĆS
## 420 357 3882 3
## VICHADA
## 16
Tipos de datos
str(datos)
## 'data.frame': 27223 obs. of 22 variables:
## $ FECHA : chr "01/01/2015 12:00:00 AM" "01/01/2015 12:00:00 AM" "01/01/2015 12:00:00 AM" "01/01/2015 12:00:00 AM" ...
## $ DEPARTAMENTO : chr "ANTIOQUIA" "ANTIOQUIA" "ANTIOQUIA" "ANTIOQUIA" ...
## $ MUNICIPIO : chr "CAREPA" "COPACABANA" "EL BAGRE" "MARINILLA" ...
## $ DIA : chr "Jueves" "Jueves" "Jueves" "Jueves" ...
## $ HORA : chr "7:30" "14:45" "4:00" "0:00" ...
## $ BARRIO : chr "PUEBLO NUEVO" "VDA. CABUYAL" "BIJAO" "CENTRO" ...
## $ ZONA : chr "URBANA" "URBANA" "URBANA" "URBANA" ...
## $ CLASE.SITIO : chr "VIAS PUBLICAS" "VIAS PUBLICAS" "VIAS PUBLICAS" "VIAS PUBLICAS" ...
## $ EDAD : int 26 22 29 26 61 28 29 39 33 24 ...
## $ GENERO : chr "MASCULINO" "MASCULINO" "FEMENINO" "FEMENINO" ...
## $ ARMA.EMPLEADA: chr "LLAVE MAESTRA" "ARMA DE FUEGO" "LLAVE MAESTRA" "LLAVE MAESTRA" ...
## $ MOVIL.AGRESOR: chr "A PIE" "PASAJERO MOTOCICLETA" "A PIE" "A PIE" ...
## $ MOVIL.VICTIMA: chr "A PIE" "CONDUCTOR MOTOCICLETA" "A PIE" "A PIE" ...
## $ MARCA : chr "YAMAHA" "YAMAHA" "AUTECO" "YAMAHA" ...
## $ MODELO : num 2012 2000 2008 1997 2006 ...
## $ LINEA : chr "FZ16" "RX 115" "PLATINO" "DT125" ...
## $ COLOR : chr "NEGRO" "BLANCO" "NEGRO" "AZUL" ...
## $ ESTADO.CIVIL : chr "UNION LIBRE" "SOLTERO" "CASADO" "UNION LIBRE" ...
## $ PROFESIONES : chr "POLICIA" "NO REPORTADO" "NO REPORTADO" "NO REPORTADO" ...
## $ ESCOLARIDAD : chr "TECNICO" "SECUNDARIA" "PRIMARIA" "TECNICO" ...
## $ CODIGO.DANE : int 5147000 5212000 5250000 5440000 5607000 85010000 85001000 20250000 20001000 20001000 ...
## $ X2015 : int 1 1 1 1 1 1 1 1 1 1 ...
Se realiza un diagrama de barras para verificar cómo se encuentra la base de datos.
barplot(table(datos$DEPARTAMENTO),las=2)
Se organizan las barras de mayor a menor frecuencia.
barplot(sort(table(datos$DEPARTAMENTO),decreasing=TRUE),
las=2)
Se realiza un resumen de la variable municipio y luego se visualizan los municipios con frecuencia mayor a 150.
resum_1<-table(datos$MUNICIPIO)
barplot(sort(resum_1[resum_1>150],decreasing=TRUE),
las=2)
Visualización de la variable Genero
barplot(table(datos$GENERO))
resum_2<-table(datos$GENERO)
resum_2
##
## FEMENINO MASCULINO NO REPORTADO
## 92 4710 22420 1
names(resum_2)
## [1] "" "FEMENINO" "MASCULINO" "NO REPORTADO"
Se organizan los valores NA de la
variable GENERO en a1
a1<-names(resum_2)[1]
a1
## [1] ""
Se guardan todos los valores similares de la base en a2
a2<-which(datos$GENERO==a1)
length(a2)
## [1] 92
Se visualizan los primeros 10 valores NA de la base
de datos y se observa que en la fila 81,366,728⦠1798 se encuentran
NA de la variable
GENERO
a2[1:10]
## [1] 81 366 728 924 1331 1364 1490 1525 1785 1798
datos$FECHA[a2[1]]
## [1] "04/01/2015 12:00:00 AM,VALLE,CALI (CT),Domingo,19:40,OBRERO E9,URBANA,\"DROGUERIAS, FARMACIAS\",36,MASCULINO,LLAVE MAESTRA,A PIE,A PIE,SUZUKI,2009,viva 115,AZUL,UNION LIBRE,NO REPORTADO,SECUNDARIA,76001000,1"
datos$FECHA[a2[2]]
## [1] ",NORTE DE SANTANDER,PAMPLONA,Domingo,0:45,BARRIO EL CAMELLON,URBANA,\"BARES, CANTINAS Y SIMILARES\",27,MASCULINO,LLAVE MAESTRA,A PIE,A PIE,YAMAHA,2003,CRIPTON 110,AZUL,SOLTERO,NO REPORTADO,TECNICO,54518000,1"
kable(datos[70:87,], caption= "Tabla 1: Base de datos, Delito de Hurto de Motocicleta") %>%
kable_styling(full_width = F) %>%
column_spec(2, width = "20em") %>%
scroll_box(width = "900px", height = "450px")
| FECHA | DEPARTAMENTO | MUNICIPIO | DIA | HORA | BARRIO | ZONA | CLASE.SITIO | EDAD | GENERO | ARMA.EMPLEADA | MOVIL.AGRESOR | MOVIL.VICTIMA | MARCA | MODELO | LINEA | COLOR | ESTADO.CIVIL | PROFESIONES | ESCOLARIDAD | CODIGO.DANE | X2015 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 70 | 04/01/2015 12:00:00 AM | HUILA | HOBO | Domingo | 12:30 | CENTRO | URBANA | VIAS PUBLICAS | 34 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | HONDA | 1990 | LINEA STANDARD | AZUL | UNION LIBRE | NO REPORTADO | PRIMARIA | 41349000 | 1 |
| 71 | 04/01/2015 12:00:00 AM | MAGDALENA | SANTA MARTA (CT) | Domingo | 13:30 | URBANIZACION MARIA CECILIA | URBANA | VIAS PUBLICAS | 23 | MASCULINO | ARMA DE FUEGO | PASAJERO MOTOCICLETA | A PIE | YAMAHA | 2013 | FZ16 | NEGRO GRAFITO | SOLTERO | NO REPORTADO | SECUNDARIA | 47001000 | 1 |
| 72 | 04/01/2015 12:00:00 AM | META | FUENTE DE ORO | Domingo | 8:00 | VEREDA PUERTO POVEDA | RURAL | PLAYA | 23 | MASCULINO | LLAVE MAESTRA | A PIE | CONDUCTOR MOTOCICLETA | BAJAJ | 2014 | DISCOVER | ROJO | UNION LIBRE | NO REPORTADO | PRIMARIA | 50287000 | 1 |
| 73 | 04/01/2015 12:00:00 AM | META | VILLAVICENCIO (CT) | Domingo | 1:00 | VALLES DE ARAGON | URBANA | VIAS PUBLICAS | 25 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | YAMAHA | 2015 | FZ16 | NEGRO | SOLTERO | NO REPORTADO | SECUNDARIA | 50001000 | 1 |
| 74 | 04/01/2015 12:00:00 AM | PUTUMAYO | MOCOA (CT) | Domingo | 16:00 | OBRERO I | URBANA | VIAS PUBLICAS | 64 | MASCULINO | CONTUNDENTES | A PIE | A PIE | BAJAJ | 2010 | DISCOVER | AZUL | CASADO | NO REPORTADO | SECUNDARIA | 86001000 | 1 |
| 75 | 04/01/2015 12:00:00 AM | RISARALDA | PEREIRA (CT) | Domingo | 15:00 | VDA. SAN JOSE | RURAL | PARQUEADERO | 24 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | YAMAHA | 1997 | RX 100 | MARRON | SOLTERO | NO REPORTADO | SECUNDARIA | 66001000 | 1 |
| 76 | 04/01/2015 12:00:00 AM | SUCRE | SINCELEJO (CT) | Domingo | 11:30 | CENTRO | URBANA | VIAS PUBLICAS | 47 | MASCULINO | LLAVE MAESTRA | A PIE | CONDUCTOR MOTOCICLETA | AUTECO | 2014 | BOXER | NEGRO | CASADO | NO REPORTADO | SECUNDARIA | 70001000 | 1 |
| 77 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 7:00 | UNION DE VIVIENDA POPULAR E16 | URBANA | FRENTE A RESIDENCIAS - VIA PUBLICA | 24 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | AKT | 2008 | AK 100 | NEGRO | UNION LIBRE | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 78 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 9:00 | SANTA ELENA E10 | URBANA | VIAS PUBLICAS | 33 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | TVS | 2007 | SPORT | NEGRO | UNION LIBRE | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 79 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 16:00 | VISTAHERMOSA E1 | URBANA | VIAS PUBLICAS | 28 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | AUTECO | 2011 | BOXER | BLANCO | SOLTERO | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 80 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 18:00 | LA LIBERTAD E10 | URBANA | VIAS PUBLICAS | 31 | FEMENINO | ARMA DE FUEGO | A PIE | A PIE | YAMAHA | 2015 | BWS | NEGRO | UNION LIBRE | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 81 | 04/01/2015 12:00:00 AM,VALLE,CALI (CT),Domingo,19:40,OBRERO E9,URBANA,āDROGUERIAS, FARMACIASā,36,MASCULINO,LLAVE MAESTRA,A PIE,A PIE,SUZUKI,2009,viva 115,AZUL,UNION LIBRE,NO REPORTADO,SECUNDARIA,76001000,1 | NA | NA | NA | NA | |||||||||||||||||
| 82 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 20:30 | ATANASIO GIRARDOT E8 | URBANA | VIAS PUBLICAS | 34 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | HONDA | 2013 | ECO | NEGRO | SOLTERO | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 83 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 22:40 | LOS GUAYACANES E5 | URBANA | VIAS PUBLICAS | 21 | FEMENINO | ARMA DE FUEGO | CONDUCTOR MOTOCICLETA | CONDUCTOR MOTOCICLETA | YAMAHA | 2014 | CRYPTON 115 | AZUL | SOLTERO | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 84 | 04/01/2015 12:00:00 AM | VALLE | TRUJILLO | Domingo | 0:40 | CENTRO | URBANA | VIAS PUBLICAS | 23 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | YAMAHA | 1996 | DT125 | BLANCO | SOLTERO | NO REPORTADO | SECUNDARIA | 76828000 | 1 |
| 85 | 05/01/2015 12:00:00 AM | ANTIOQUIA | ENVIGADO | Lunes | 22:00 | EL DORADO | URBANA | VIAS PUBLICAS | 28 | MASCULINO | ARMA DE FUEGO | PASAJERO MOTOCICLETA | CONDUCTOR MOTOCICLETA | BMW | 2015 | RNO REPORTADO1200C | ROJO | SOLTERO | NO REPORTADO | SUPERIOR | 5266000 | 1 |
| 86 | 05/01/2015 12:00:00 AM | ANTIOQUIA | GUARNE | Lunes | 0:00 | SAN FRANCISCO | URBANA | VIAS PUBLICAS | 26 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | AUTECO | 2014 | PULSAR | AMARILLO | SOLTERO | NO REPORTADO | SECUNDARIA | 5318000 | 1 |
| 87 | 05/01/2015 12:00:00 AM | ANTIOQUIA | TURBO | Lunes | 9:00 | CENTRO | URBANA | VIAS PUBLICAS | 33 | FEMENINO | NO REPORTADO | A PIE | A PIE | BAJAJ | 2011 | DISCOVER | NEGRO | CASADO | NO REPORTADO | SECUNDARIA | 5837000 | 1 |
stringr
El paquete stringr es parte del ecosistema de R y forma
parte de la familia de paquetes tidyverse, desarrollado por
Hadley Wickham y otros colaboradores. EstĆ” diseƱado especĆficamente para
facilitar el trabajo con cadenas de texto (strings) en R.
stringrstr_to_upper()) o minĆŗsculas
(str_to_lower()).stringr
facilita la bĆŗsqueda de patrones dentro de cadenas utilizando
expresiones regulares, que son una herramienta poderosa para encontrar y
manipular texto basado en patrones especĆficos.str_detect() permiten identificar si un patrón especĆfico
existe dentro de una cadena.str_trim() se
usa para eliminar espacios en blanco al principio y al final de una
cadena.str_split() divide una cadena en partes basadas en un
delimitador, y str_c() concatena mĆŗltiples cadenas en una
sola.str_length()
devuelve el nĆŗmero de caracteres en una cadena.str_count()
cuenta cuĆ”ntas veces un patrón especĆfico aparece en una cadena.datos$FECHA[a2[1]]
## [1] "04/01/2015 12:00:00 AM,VALLE,CALI (CT),Domingo,19:40,OBRERO E9,URBANA,\"DROGUERIAS, FARMACIAS\",36,MASCULINO,LLAVE MAESTRA,A PIE,A PIE,SUZUKI,2009,viva 115,AZUL,UNION LIBRE,NO REPORTADO,SECUNDARIA,76001000,1"
require(stringr)
str_match(datos$FECHA[a2[1]], '"(.*?)"')[,2]
## [1] "DROGUERIAS, FARMACIAS"
a3<-vector()
for(i in 1:length(a2)){
a3[i]<-str_match(datos$FECHA[a2[i]],'"(.*?)"')[,2]
}
resum3<-summary(as.factor(a3))
resum3
## BARES, CANTINAS Y SIMILARES CHICO I, II, III
## 37 1
## COLEGIOS, ESCUELAS COR, LOS ANGELES
## 33 1
## DROGUERIAS, FARMACIAS HOTELES, RESIDENCIAS, Y SIMILARES.
## 1 14
## LA ADIELA I,II,III,IV VDA. QUILCACE, ANTES DEL PUENTE
## 2 2
## VIA, PUERTO ASIS
## 1
a31<-str_replace_all(a3,",","-")
resum4<-summary(as.factor(a31))
resum4
## BARES- CANTINAS Y SIMILARES CHICO I- II- III
## 37 1
## COLEGIOS- ESCUELAS COR- LOS ANGELES
## 33 1
## DROGUERIAS- FARMACIAS HOTELES- RESIDENCIAS- Y SIMILARES.
## 1 14
## LA ADIELA I-II-III-IV VDA. QUILCACE- ANTES DEL PUENTE
## 2 2
## VIA- PUERTO ASIS
## 1
b1<-str_replace(datos$FECHA[a2[1]],a3[1],a31[1])
b1
## [1] "04/01/2015 12:00:00 AM,VALLE,CALI (CT),Domingo,19:40,OBRERO E9,URBANA,\"DROGUERIAS- FARMACIAS\",36,MASCULINO,LLAVE MAESTRA,A PIE,A PIE,SUZUKI,2009,viva 115,AZUL,UNION LIBRE,NO REPORTADO,SECUNDARIA,76001000,1"
b2<-str_replace(b1,'\"','' )
b2<-str_replace(b2,'\",',',' )
b2
## [1] "04/01/2015 12:00:00 AM,VALLE,CALI (CT),Domingo,19:40,OBRERO E9,URBANA,DROGUERIAS- FARMACIAS,36,MASCULINO,LLAVE MAESTRA,A PIE,A PIE,SUZUKI,2009,viva 115,AZUL,UNION LIBRE,NO REPORTADO,SECUNDARIA,76001000,1"
a32<-vector()
for(i in 1:length(a2)){
b1<-str_replace(datos$FECHA[a2[i]],a3[i],a31[i])
b2<-str_replace(b1,'\"','')
b2<-str_replace(b2,'\",',',' )
a32[i]<-b2
}
require(tidyr)
#datos21<-separate(data.frame(a32),a32, sep=",",into=colnames(datos))
#datos21 <- separate(data.frame(a32 = a32), col = "a32", sep = ",", into = colnames(datos))
datos21 <- tidyr::separate(data.frame(a32 = a32), col = "a32", sep = ",", into = colnames(datos))
datos[a2,]<-datos21
datos<-droplevels(datos)
table(datos$GENERO)# DESPUĆS DE PROCESAR LOS DATOS
##
## FEMENINO MASCULINO NO REPORTADO
## 4728 22494 1
kable(datos[70:87,], caption= "Tabla 1: Base de datos, Delito de Hurto de Motocicleta") %>%
kable_styling(full_width = F) %>%
column_spec(2, width = "20em") %>%
scroll_box(width = "900px", height = "450px")
| FECHA | DEPARTAMENTO | MUNICIPIO | DIA | HORA | BARRIO | ZONA | CLASE.SITIO | EDAD | GENERO | ARMA.EMPLEADA | MOVIL.AGRESOR | MOVIL.VICTIMA | MARCA | MODELO | LINEA | COLOR | ESTADO.CIVIL | PROFESIONES | ESCOLARIDAD | CODIGO.DANE | X2015 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 70 | 04/01/2015 12:00:00 AM | HUILA | HOBO | Domingo | 12:30 | CENTRO | URBANA | VIAS PUBLICAS | 34 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | HONDA | 1990 | LINEA STANDARD | AZUL | UNION LIBRE | NO REPORTADO | PRIMARIA | 41349000 | 1 |
| 71 | 04/01/2015 12:00:00 AM | MAGDALENA | SANTA MARTA (CT) | Domingo | 13:30 | URBANIZACION MARIA CECILIA | URBANA | VIAS PUBLICAS | 23 | MASCULINO | ARMA DE FUEGO | PASAJERO MOTOCICLETA | A PIE | YAMAHA | 2013 | FZ16 | NEGRO GRAFITO | SOLTERO | NO REPORTADO | SECUNDARIA | 47001000 | 1 |
| 72 | 04/01/2015 12:00:00 AM | META | FUENTE DE ORO | Domingo | 8:00 | VEREDA PUERTO POVEDA | RURAL | PLAYA | 23 | MASCULINO | LLAVE MAESTRA | A PIE | CONDUCTOR MOTOCICLETA | BAJAJ | 2014 | DISCOVER | ROJO | UNION LIBRE | NO REPORTADO | PRIMARIA | 50287000 | 1 |
| 73 | 04/01/2015 12:00:00 AM | META | VILLAVICENCIO (CT) | Domingo | 1:00 | VALLES DE ARAGON | URBANA | VIAS PUBLICAS | 25 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | YAMAHA | 2015 | FZ16 | NEGRO | SOLTERO | NO REPORTADO | SECUNDARIA | 50001000 | 1 |
| 74 | 04/01/2015 12:00:00 AM | PUTUMAYO | MOCOA (CT) | Domingo | 16:00 | OBRERO I | URBANA | VIAS PUBLICAS | 64 | MASCULINO | CONTUNDENTES | A PIE | A PIE | BAJAJ | 2010 | DISCOVER | AZUL | CASADO | NO REPORTADO | SECUNDARIA | 86001000 | 1 |
| 75 | 04/01/2015 12:00:00 AM | RISARALDA | PEREIRA (CT) | Domingo | 15:00 | VDA. SAN JOSE | RURAL | PARQUEADERO | 24 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | YAMAHA | 1997 | RX 100 | MARRON | SOLTERO | NO REPORTADO | SECUNDARIA | 66001000 | 1 |
| 76 | 04/01/2015 12:00:00 AM | SUCRE | SINCELEJO (CT) | Domingo | 11:30 | CENTRO | URBANA | VIAS PUBLICAS | 47 | MASCULINO | LLAVE MAESTRA | A PIE | CONDUCTOR MOTOCICLETA | AUTECO | 2014 | BOXER | NEGRO | CASADO | NO REPORTADO | SECUNDARIA | 70001000 | 1 |
| 77 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 7:00 | UNION DE VIVIENDA POPULAR E16 | URBANA | FRENTE A RESIDENCIAS - VIA PUBLICA | 24 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | AKT | 2008 | AK 100 | NEGRO | UNION LIBRE | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 78 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 9:00 | SANTA ELENA E10 | URBANA | VIAS PUBLICAS | 33 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | TVS | 2007 | SPORT | NEGRO | UNION LIBRE | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 79 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 16:00 | VISTAHERMOSA E1 | URBANA | VIAS PUBLICAS | 28 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | AUTECO | 2011 | BOXER | BLANCO | SOLTERO | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 80 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 18:00 | LA LIBERTAD E10 | URBANA | VIAS PUBLICAS | 31 | FEMENINO | ARMA DE FUEGO | A PIE | A PIE | YAMAHA | 2015 | BWS | NEGRO | UNION LIBRE | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 81 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 19:40 | OBRERO E9 | URBANA | DROGUERIAS- FARMACIAS | 36 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | SUZUKI | 2009 | viva 115 | AZUL | UNION LIBRE | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 82 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 20:30 | ATANASIO GIRARDOT E8 | URBANA | VIAS PUBLICAS | 34 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | HONDA | 2013 | ECO | NEGRO | SOLTERO | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 83 | 04/01/2015 12:00:00 AM | VALLE | CALI (CT) | Domingo | 22:40 | LOS GUAYACANES E5 | URBANA | VIAS PUBLICAS | 21 | FEMENINO | ARMA DE FUEGO | CONDUCTOR MOTOCICLETA | CONDUCTOR MOTOCICLETA | YAMAHA | 2014 | CRYPTON 115 | AZUL | SOLTERO | NO REPORTADO | SECUNDARIA | 76001000 | 1 |
| 84 | 04/01/2015 12:00:00 AM | VALLE | TRUJILLO | Domingo | 0:40 | CENTRO | URBANA | VIAS PUBLICAS | 23 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | YAMAHA | 1996 | DT125 | BLANCO | SOLTERO | NO REPORTADO | SECUNDARIA | 76828000 | 1 |
| 85 | 05/01/2015 12:00:00 AM | ANTIOQUIA | ENVIGADO | Lunes | 22:00 | EL DORADO | URBANA | VIAS PUBLICAS | 28 | MASCULINO | ARMA DE FUEGO | PASAJERO MOTOCICLETA | CONDUCTOR MOTOCICLETA | BMW | 2015 | RNO REPORTADO1200C | ROJO | SOLTERO | NO REPORTADO | SUPERIOR | 5266000 | 1 |
| 86 | 05/01/2015 12:00:00 AM | ANTIOQUIA | GUARNE | Lunes | 0:00 | SAN FRANCISCO | URBANA | VIAS PUBLICAS | 26 | MASCULINO | LLAVE MAESTRA | A PIE | A PIE | AUTECO | 2014 | PULSAR | AMARILLO | SOLTERO | NO REPORTADO | SECUNDARIA | 5318000 | 1 |
| 87 | 05/01/2015 12:00:00 AM | ANTIOQUIA | TURBO | Lunes | 9:00 | CENTRO | URBANA | VIAS PUBLICAS | 33 | FEMENINO | NO REPORTADO | A PIE | A PIE | BAJAJ | 2011 | DISCOVER | NEGRO | CASADO | NO REPORTADO | SECUNDARIA | 5837000 | 1 |
readr
El paquete readr es una herramienta esencial en R para
la lectura de archivos de datos de manera rƔpida y eficiente. Forma
parte del tidyverse, una colección de paquetes que
comparten una filosofĆa de diseƱo comĆŗn y son desarrollados para
trabajar juntos de manera armoniosa.
readrreadr permite la importación de archivos de texto
delimitados como CSV (read_csv()), TSV
(read_tsv()), y otros formatos similares de manera
eficiente y rÔpida. Utiliza una implementación optimizada en C++ que
permite un desempeño superior en comparación con funciones base de R
como read.csv().readr es su
capacidad para inferir automƔticamente los tipos de datos de cada
columna. Esto significa que, al leer un archivo, el paquete detecta si
una columna contiene nĆŗmeros, fechas, texto, etc., y realiza la
conversión adecuada automÔticamente.readr permite leer y procesar los datos de manera perezosa,
lo que es útil para manejar archivos de gran tamaño sin sobrecargar la
memoria.readr incluye funciones para leer
otros formatos como archivos delimitados por espacios
(read_table()), archivos de ancho fijo
(read_fwf()), y archivos de datos R
(read_rds()).suppressWarnings({
suppressPackageStartupMessages(library(readr))
suppressPackageStartupMessages(library(dplyr))
suppressPackageStartupMessages(library(magrittr))
datos2A <- read_csv("Delito_Hurto_Motocicletas.csv",
locale = locale(encoding = "UTF-8"),
skip_empty_rows = TRUE)
})
## Rows: 27223 Columns: 22
## āā Column specification āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
## Delimiter: ","
## chr (17): FECHA, DEPARTAMENTO, MUNICIPIO, DIA, BARRIO, ZONA, CLASE SITIO, G...
## dbl (4): EDAD, MODELO, CODIGO DANE, 2015
## time (1): HORA
##
## ā¹ Use `spec()` to retrieve the full column specification for this data.
## ā¹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Al visualizar la base de datos cargada
View(datos2A) se observa que la fila 81
presenta problemas.
Con la ayuda de este paquete y su respectiva función
problems se identifica el problema de la
base de datos, en un solo paso.
probs1 <- problems(datos2A)
probs1
probs1 <- problems(datos2A)
# Ajustar las filas reportadas restando 1, dado que me presento ese problema.
probs1_adjusted <- probs1
probs1_adjusted$row <- probs1$row - 1
El comando names(probs1) se utiliza
para mostrar los nombres de las columnas o las variables en el objeto
probs1. En este caso, el resultado que se muestra en la salida indica
que probs1 es un objeto (probablemente un data frame o lista) que
contiene las siguientes variables o columnas:
names(probs1_adjusted)
## [1] "row" "col" "expected" "actual" "file"
Se revelan las salidas con problemas y se verifican que el problema se presenta en las misma filas identificadas manualmente en los pasos anteriores.
probs1_adjusted$row[1:10]# Mostrar las primeras 10 filas con problemas identificadas por probs1
## [1] 81 366 728 924 1331 1364 1490 1525 1785 1798
a2[1:10] # Obtenido "manualmente"
## [1] 81 366 728 924 1331 1364 1490 1525 1785 1798
Variable Estado Civil
barplot(sort(table(datos$ESTADO.CIVIL),
decreasing=TRUE),las=2)
Variable Color
barplot(sort(table(datos$COLOR),
decreasing=TRUE),las=2)
Se muestran solo los primeros 20 colores.
barplot(sort(table(datos$COLOR),
decreasing = TRUE)[1:20],las=2)
Variable MOVIL.AGRESOR
barplot(sort(table(datos$MOVIL.AGRESOR),
decreasing = TRUE),las=2)
Variable HORA
barplot(sort(table(datos$HORA),
decreasing=TRUE)[1:20],las=2)
Se carga el paquete tidyr para manipular un conjunto de datos creado y llamado datos22. Primero, se divide la columna HORA en dos nuevas columnas, HORA1 y MINS1, separando los valores en base al sĆmbolo ā:ā. Luego, las nuevas columnas se convierten a formato numĆ©rico para permitir operaciones matemĆ”ticas. Finalmente, se crea una nueva columna HORAS, que representa la hora en formato decimal, sumando la parte de las horas con los minutos divididos entre 60. Este proceso facilita el anĆ”lisis de las horas al convertirlas en un formato numĆ©rico continuo.
suppressPackageStartupMessages(library(tidyr))
#datos22<-separate(datos,HORA, sep=":",into=c("HORA1","MINS1" ))
datos22 <- tidyr::separate(datos, col = HORA, sep = ":", into = c("HORA1", "MINS1"))
datos22$HORA1<-as.numeric(datos22$HORA1)
datos22$MINS1<-as.numeric(datos22$MINS1)
datos22$HORAS<-datos22$HORA1+(datos22$MINS1/60)
Variable HORA, segĆŗn nĆŗmero de
robos.
require(ggplot2)
ggplot(datos22,aes(x=HORAS))+
geom_density(alpha=0.4,fill="blue")
Variable HORA, segĆŗn nĆŗmero
MOVIL AGRESOR, APIE,PASAJERO
MOTOCICLETA,CONDUCTORMOTOCICLETA.
datos23<-subset(datos22,subset=(MOVIL.AGRESOR==c("APIE","PASAJERO MOTOCICLETA","CONDUCTOR MOTOCICLETA")))
ggplot(datos23,aes(x=HORAS,fill=MOVIL.AGRESOR))+geom_density(alpha=0.4)
Variable HORA, segĆŗn nĆŗmero
ESTADO CIVIL.
datos24<-subset(datos22,subset=(ESTADO.CIVIL==c("SOLTERO","UNIONLIBRE","CASADO")))
ggplot(datos24,aes(x=HORAS,fill=ESTADO.CIVIL))+geom_density(alpha=0.4)
Variable HORA, segĆŗn nĆŗmero
GENERO.
datos25<-subset(datos22,subset=(GENERO!=c("NO REPORTADO")))
ggplot(datos25,aes(x=HORAS,fill=GENERO))+geom_density(alpha=0.4)
Seleccione alguna de las funciones en R para leer las bases de datos que se encuentran en la cuenta de GitHub. Los archivos de datos que necesita son:
url_DHM <- "https://raw.githubusercontent.com/Kalbam/Datos/main/Delito_Hurto_Automotores.csv"
url_master <- "https://raw.githubusercontent.com/Kalbam/Datos/main/master.csv"
Utilice las funciones de R para explorar estos archivos y analizar su contenido.
Explore posibles problemas leyendo las bases de datos y
regĆstrelos brevemente. Use las funciones head,
tail, summary, barplot,
etc.
¿Qué solución propone para resolver dichos problemas y apliquelas?
En la vida real, los archivos de datos suelen contener problemas como comas internas en los campos, comillas innecesarias, valores faltantes, errores de codificación y nombres de columnas poco claros. Aprender a identificar y resolver estos problemas es fundamental para un anÔlisis de datos confiable.
He subido varios archivos CSV problemƔticos al repositorio del curso,
que puedes encontrar aquĆ:
https://github.com/Kalbam/Data_cleaning_practice
read.csv(), readr::read_csv(),
data.table::fread(), etc.).ggplot2 o
funciones base de R.Consideremos nuevamente la base de datos que contiene información acerca de incautaciones de bebidas alcohólicas fraudulentas y de contrabando en cierta ciudad. Puede acceder a la base de datos en el siguiente enlace: BASE_DATOS.xlsx.
url_base_Datos <-"https://raw.github.com/Kalbam/Datos/blob/main/BASE_DATOS.xlsx"
Las variables se describen como:
require(readxl)
direccion="BASE_DATOS.xlsx"
DATOS<-read_excel(direccion,sheet="datos")
NOTA: La función read_excel importa un objeto tibble, que es en esencia un data.frame, pero que cuenta con algunas ventajas estéticas en las presentaciones e informes.
Verifiquemos que leĆmos bien los datos viendo el encabezado y la cola de los datos:
head(DATOS)
tail(DATOS)
Las dimensiones, los nombres de las columnas y la estructura de la base de datos se obtienen con los códigos:
dim(DATOS) # dimensiones de los datos
## [1] 300 5
colnames(DATOS) # Nombres de las columnas o variables
## [1] "TL" "PI" "GAE" "GAQ" "CE"
str(DATOS)
## tibble [300 Ć 5] (S3: tbl_df/tbl/data.frame)
## $ TL : chr [1:300] "Aguardiente" "Aguardiente" "Aguardiente" "Aguardiente" ...
## $ PI : num [1:300] 21470 26422 19737 30240 28374 ...
## $ GAE: num [1:300] 29 29 29 29 38 38 29 38 38 38 ...
## $ GAQ: num [1:300] 25.1 29 25.3 29 33.5 ...
## $ CE : num [1:300] 251 262 289 266 232 ...
Otra función que funciona igual a str es
glimpse, que hace parte del paquete
tibble:
require(tibble)
glimpse(DATOS)
## Rows: 300
## Columns: 5
## $ TL <chr> "Aguardiente", "Aguardiente", "Aguardiente", "Aguardiente", "Tequiā¦
## $ PI <dbl> 21470, 26422, 19737, 30240, 28374, 33601, 33207, 33029, 30964, 309ā¦
## $ GAE <dbl> 29, 29, 29, 29, 38, 38, 29, 38, 38, 38, 29, 29, 29, 35, 29, 38, 29ā¦
## $ GAQ <dbl> 25.143, 29.000, 25.288, 29.000, 33.516, 31.198, 29.000, 33.060, NAā¦
## $ CE <dbl> 250.6536, 261.7272, 289.1134, 266.3064, 231.7978, 248.8542, 252.57ā¦
summary(DATOS)
## TL PI GAE GAQ
## Length:300 Min. : 16308 Min. :29.00 Min. :24.27
## Class :character 1st Qu.: 26682 1st Qu.:29.00 1st Qu.:26.04
## Mode :character Median : 29390 Median :35.00 Median :30.07
## Mean : 31846 Mean :33.54 Mean :30.74
## 3rd Qu.: 32751 3rd Qu.:38.00 3rd Qu.:34.10
## Max. :430091 Max. :40.00 Max. :40.00
## NA's :5 NA's :2
## CE
## Min. : 0.0
## 1st Qu.:239.4
## Median :250.3
## Mean :249.6
## 3rd Qu.:261.7
## Max. :296.5
## NA's :3
summary(DATOS$PI)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 16308 26682 29390 31846 32751 430091 5
summary(DATOS$GAE)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 29.00 29.00 35.00 33.54 38.00 40.00
summary(DATOS$GAQ)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 24.27 26.04 30.07 30.74 34.10 40.00 2
Para hacer una tabla de frecuencias de las variables categóricas es necesario transformar primero la variable en factor:
# Se transforma primero en factor
TL_FACTOR<- as.factor(DATOS$TL)
# Se obtiene el resumen
summary(TL_FACTOR)
## Aguardiente Aguardientre Ron Run Tequila Whiski
## 131 1 60 1 85 1
## Whisky NA's
## 20 1
Otra forma de obtener una tabla de frecuencias para las variables
categóricas consiste en utilizar la función table del
paquete base, que se carga automƔticamente cuando abrimos
una sesión del R o del R-Studio:
table(DATOS$TL)
##
## Aguardiente Aguardientre Ron Run Tequila Whiski
## 131 1 60 1 85 1
## Whisky
## 20
Para extraer los nombres de las categorĆas de una variable categórica
usamos la función labels del paquete base:
unique(DATOS$TL)
## [1] "Aguardiente" "Tequila" "Ron" "Whisky" "Run"
## [6] NA "Whiski" "Aguardientre"
Reemplazar los nombres de las categorĆas mal codificadas, utilizamos
la función str_replace del paquete stringr y
lo guardamos ya corregido directamente en la variable TL de DATOS:
require(stringr)
DATOS$TL <- str_replace(DATOS$TL, "Aguardientre", "Aguardiente")
unique(DATOS$TL)
## [1] "Aguardiente" "Tequila" "Ron" "Whisky" "Run"
## [6] NA "Whiski"
Reemplazar los nombres de las categorĆas mal codificadas, utilizamos
la función str_replace del paquete stringr y
lo guardamos ya corregido directamente en la variable TL de DATOS:
require(stringr)
reemplazos <- c("Run" = "Ron", "Whiski" = "Whisky")
DATOS$TL <- str_replace_all(DATOS$TL, reemplazos)
unique(DATOS$TL)
## [1] "Aguardiente" "Tequila" "Ron" "Whisky" NA
is.na(DATOS[1:10,]) # Identifica cuƔles valores son NA
## TL PI GAE GAQ CE
## [1,] FALSE FALSE FALSE FALSE FALSE
## [2,] FALSE FALSE FALSE FALSE FALSE
## [3,] FALSE FALSE FALSE FALSE FALSE
## [4,] FALSE FALSE FALSE FALSE FALSE
## [5,] FALSE FALSE FALSE FALSE FALSE
## [6,] FALSE FALSE FALSE FALSE FALSE
## [7,] FALSE FALSE FALSE FALSE FALSE
## [8,] FALSE FALSE FALSE FALSE FALSE
## [9,] FALSE FALSE FALSE TRUE FALSE
## [10,] FALSE FALSE FALSE FALSE FALSE
is.na(DATOS$TL) # Identifica cuƔles valores son NA en TL
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [49] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [61] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [73] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [85] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [97] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [109] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
## [121] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [133] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [145] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [157] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [169] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [181] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [193] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [205] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [217] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [229] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [241] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [253] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [265] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [277] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [289] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
NAS_TL<-is.na(DATOS$TL) #GuardamosenNAS_TL
DATOS[NAS_TL,] #FiltramosporlosNASdeTL
NAS_PI<-is.na(DATOS$PI) # Guardamos en NAS_PI
DATOS[NAS_TL | NAS_PI,] # Filtramos por los NAS de TL y PI
La función complete.cases del paquete stats
nos muestra cuƔles filas tienen datos en TODAS sus
columnas:
complete.cases(DATOS)
## [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE TRUE TRUE TRUE
## [13] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [25] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [37] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [49] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [61] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [73] FALSE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [85] TRUE TRUE TRUE TRUE TRUE TRUE FALSE TRUE TRUE TRUE TRUE TRUE
## [97] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE
## [109] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE TRUE TRUE TRUE
## [121] TRUE TRUE TRUE TRUE TRUE TRUE TRUE FALSE TRUE TRUE TRUE TRUE
## [133] TRUE TRUE TRUE TRUE TRUE TRUE FALSE TRUE TRUE TRUE TRUE TRUE
## [145] TRUE TRUE TRUE TRUE TRUE FALSE TRUE TRUE TRUE TRUE TRUE TRUE
## [157] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [169] TRUE TRUE TRUE TRUE TRUE TRUE FALSE TRUE TRUE TRUE TRUE TRUE
## [181] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [193] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [205] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [217] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [229] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [241] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [253] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [265] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [277] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [289] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
Cuando negamos estos casos completos obtenemos las filas donde hay al menos un valor faltante, es decir,
!complete.cases(DATOS)
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
## [13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [49] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [61] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [73] TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [85] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
## [97] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE
## [109] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
## [121] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE
## [133] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
## [145] FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
## [157] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [169] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE FALSE
## [181] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [193] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [205] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [217] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [229] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [241] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [253] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [265] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [277] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [289] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
Guardamos los valores anteriores en un vector para luego filtrar por
las filas donde hay al menos un valor NA:
NAS_ALL <- !complete.cases(DATOS)
DATOS[NAS_ALL, ] # Filas donde hay al menos un valor NA
suppressWarnings(require(Amelia))
## Cargando paquete requerido: Amelia
## Cargando paquete requerido: Rcpp
## ##
## ## Amelia II: Multiple Imputation
## ## (Version 1.8.3, built: 2024-11-07)
## ## Copyright (C) 2005-2025 James Honaker, Gary King and Matthew Blackwell
## ## Refer to http://gking.harvard.edu/amelia/ for more information
## ##
suppressWarnings(missmap(DATOS))
summary(DATOS)# Analicemos las mediadas de tendencia
## TL PI GAE GAQ
## Length:300 Min. : 16308 Min. :29.00 Min. :24.27
## Class :character 1st Qu.: 26682 1st Qu.:29.00 1st Qu.:26.04
## Mode :character Median : 29390 Median :35.00 Median :30.07
## Mean : 31846 Mean :33.54 Mean :30.74
## 3rd Qu.: 32751 3rd Qu.:38.00 3rd Qu.:34.10
## Max. :430091 Max. :40.00 Max. :40.00
## NA's :5 NA's :2
## CE
## Min. : 0.0
## 1st Qu.:239.4
## Median :250.3
## Mean :249.6
## 3rd Qu.:261.7
## Max. :296.5
## NA's :3
par(mfrow=c(1,4))
boxplot(DATOS$PI,main="PI")
boxplot(DATOS$GAE,main="GAE")
boxplot(DATOS$GAQ,main="GAQ")
boxplot(DATOS$CE,main="CE")
De los grƔficos anteriores podemos ver que:
Filtramos los datos sin los valores atĆpicos de la variable
PI y de la variable CE, filtrando con
la función filter del paquete dplyr con el
siguiente código:
require(dplyr)
filter(DATOS, PI > 100000)
filter(DATOS, CE < 200)
Reemplazamos los valores atĆpicos por valores NA (lo
cual es equivalente a eliminarlos):
DATOS$PI[DATOS$PI > 100000] <- NA
DATOS$CE[DATOS$CE < 200] <- NA
Repetimos los grƔficos boxplot:
par(mfrow=c(1,4))
boxplot(DATOS$PI,main="PI")
boxplot(DATOS$GAE,main="GAE")
boxplot(DATOS$GAQ,main="GAQ")
boxplot(DATOS$CE,main="CE")
Si se supone que estas variables tienen Distribución normal, se modifican por la Media, de lo contrario seria por la Mediana, como técnica bÔsica de Imputación de datos.
Calculamos la media de las columnas numéricas con la función
colMeans del paquete base. Para esto primero
excluimos la variable categórica TL que se encuentra en
la columna 1:
medias <- colMeans(DATOS[,-1], na.rm = TRUE)
medias
## PI GAE GAQ CE
## 29518.52560 33.54000 30.74141 250.49003
El argumento na.rm=TRUE permite
calcular las medias de los datos que no son NA.
Para esto usamos la función replace_na del paquete
tidyr:
require(tidyr) # Cargamos el paquete
# Creamos una lista con los reemplazos:
reemplazos <- list(PI = medias[1], GAE = medias[2], GAQ = medias[3], CE = medias[4])
# Reemplazamos y guardamos en DATOS:
DATOS <- replace_na(DATOS, reemplazos)
summary(DATOS)
## TL PI GAE GAQ
## Length:300 Min. :16308 Min. :29.00 Min. :24.27
## Class :character 1st Qu.:26710 1st Qu.:29.00 1st Qu.:26.04
## Mode :character Median :29519 Median :35.00 Median :30.22
## Mean :29519 Mean :33.54 Mean :30.74
## 3rd Qu.:32459 3rd Qu.:38.00 3rd Qu.:34.07
## Max. :42022 Max. :40.00 Max. :40.00
## CE
## Min. :210.9
## 1st Qu.:239.7
## Median :250.5
## Mean :250.5
## 3rd Qu.:261.5
## Max. :296.5
unique(DATOS$TL)
## [1] "Aguardiente" "Tequila" "Ron" "Whisky" NA
FACTOR_TL<-as.factor(DATOS$TL) #Convertimos en factorTL
FRECUENCIAS_TL<-summary(FACTOR_TL) #Tabla de frecuencias
barplot(FRECUENCIAS_TL) #Graficamos las frecuencias
Si decidimos reemplazar por el valor mÔs frecuente, lo cual NO es recomendable, lo hacemos con el código:
reemplazos <- list(TL = "Aguardiente")
DATOS <- replace_na(DATOS, reemplazos)
# Verificamos el cambio:
unique(DATOS$TL)
## [1] "Aguardiente" "Tequila" "Ron" "Whisky"
FACTOR_TL<-as.factor(DATOS$TL) #Convertimos enfactor TL
FRECUENCIAS_TL<-summary(FACTOR_TL) #Tabla de frecuencias
barplot(FRECUENCIAS_TL) #Graficamos las frecuencias
suppressWarnings(missmap(DATOS))
%>% y
%<>%
Los desarrolladores del R-Studio cada vez crean herramientas que buscan simplificar los códigos que utilizamos en R.
Dos herramientas muy Ćŗtiles son:
%>%.%<>%.Dichos operadores se encuentran en el paquete
magrittr.
La mejor manera de ver el uso de los pipes de continuidad y de asignación compuesta es a través de ejemplos.
Considere que tenemos una variable x que tiene un valor
igual a (-3). Queremos aplicar a x:
suppressWarnings(require(magrittr)) #Cargamos el paquete
x<-(-3)
r_1<-abs(x) #r_1 es el primer resultado.
r_2<-log(r_1) #r_2 es el segundo resultado.
r_3<-sqrt(r_2)#r_3es el tercer y Ćŗltimo resultado.
r_3#Este es el resultado finalque se querĆa obtener
## [1] 1.048147
En un solo renglón esto podrĆa ser escrito como:
x<-(-3)
r_3<-sqrt(log(abs(x))) #Las tres funciones se aplican
r_3
## [1] 1.048147
Ousando el pipe de continuidad %>%
serĆa:
x<-(-3)
r_3<-x %>%abs %>%log %>%sqrt #Las tres funciones se aplican
r_3
## [1] 1.048147
Si deseamos reemplazar el valor de x por el resultado
final, utilizamos el pipe compuesto %<>%, es
decir,
x <- (-3)
x %<>% abs %<>% log %<>% sqrt # Las tres funciones se aplican
# El valor de x ya no es (-3) sino el resultado final deseado:
x
## [1] 1.048147
Anteriormente vimos que cómo obtener un grÔfico de barra con los códigos:
FACTOR_TL <- as.factor(DATOS$TL) # Convertimos en factor TL
FRECUENCIAS_TL <- summary(FACTOR_TL) # Tabla de frecuencias
barplot(FRECUENCIAS_TL) # Graficamos las frecuencias
Los tres renglones anteriores se pueden resumir con los pipes:
DATOS$TL %>% as.factor %>% summary %>% barplot
%>% y %<>% para
los nombres de las variables.En esta sección, utilizaremos operadores para modificar y manejar los
nombres de las variables en la base de datos master.csv.
Esto es particularmente Ćŗtil cuando los nombres de las columnas no son
intuitivos o contienen caracteres que pueden complicar su manipulación
en R.
Consideremos nuevamente la base de datos master.csv:
read.csv del paquete
utils:
En esta sección, utilizaremos operadores para modificar y manejar los
nombres de las variables en la base de datos master.csv.
Esto es particularmente Ćŗtil cuando los nombres de las columnas no son
intuitivos o contienen caracteres que pueden complicar su manipulación
en R.
Consideremos nuevamente la base de datos master.csv:
read.csv del paquete
utils desde el siguiente enlace:direccion <- "https://raw.github.com/Kalbam/Datos/blob/main/master.csv"
#master<-read_csv("master.csv")
master <- readr::read_csv("https://raw.githubusercontent.com/Kalbam/Datos/main/master.csv")
## Rows: 27820 Columns: 12
## āā Column specification āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
## Delimiter: ","
## chr (5): country, sex, age, country-year, generation
## dbl (6): year, suicides_no, population, suicides/100k pop, HDI for year, gdp...
## num (1): gdp_for_year ($)
##
## ā¹ Use `spec()` to retrieve the full column specification for this data.
## ā¹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
master %>% names # Equivalente a: names(master)
## [1] "country" "year" "sex"
## [4] "age" "suicides_no" "population"
## [7] "suicides/100k pop" "country-year" "HDI for year"
## [10] "gdp_for_year ($)" "gdp_per_capita ($)" "generation"
Para facilitar la manipulación de las variables en R, es recomendable
limpiar los nombres de las columnas, eliminando caracteres especiales y
estandarizando el formato. Para esto, utilizaremos la función
clean_names del paquete janitor.
suppressWarnings(require(janitor)) # Contiene la función clean_names
## Cargando paquete requerido: janitor
##
## Adjuntando el paquete: 'janitor'
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
master %<>% clean_names # Equivalente a: master <- clean_names(master)
viejos <- master %>% names
viejos
## [1] "country" "year" "sex"
## [4] "age" "suicides_no" "population"
## [7] "suicides_100k_pop" "country_year" "hdi_for_year"
## [10] "gdp_for_year" "gdp_per_capita" "generation"
require(dplyr) # Este paquete contiene la función rename
require(magrittr)
master %<>% rename(
pais = viejos[1],
anio = viejos[2],
sexo = viejos[3],
edad = viejos[4],
num_suic = viejos[5],
poblacion = viejos[6],
suic_x100k = viejos[7],
pais_anio = viejos[8],
idh_anio = viejos[9],
pib_anio = viejos[10],
pib_pcap = viejos[11],
generacion = viejos[12]
)
Después de realizar los cambios en los nombres de las variables, podemos verificar el resultado ejecutando el siguiente código:
master %>% names
## [1] "pais" "anio" "sexo" "edad" "num_suic"
## [6] "poblacion" "suic_x100k" "pais_anio" "idh_anio" "pib_anio"
## [11] "pib_pcap" "generacion"
str(master)
## spc_tbl_ [27,820 Ć 12] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ pais : chr [1:27820] "Albania" "Albania" "Albania" "Albania" ...
## $ anio : num [1:27820] 1987 1987 1987 1987 1987 ...
## $ sexo : chr [1:27820] "male" "male" "female" "male" ...
## $ edad : chr [1:27820] "15-24 years" "35-54 years" "15-24 years" "75+ years" ...
## $ num_suic : num [1:27820] 21 16 14 1 9 1 6 4 1 0 ...
## $ poblacion : num [1:27820] 312900 308000 289700 21800 274300 ...
## $ suic_x100k: num [1:27820] 6.71 5.19 4.83 4.59 3.28 2.81 2.15 1.56 0.73 0 ...
## $ pais_anio : chr [1:27820] "Albania1987" "Albania1987" "Albania1987" "Albania1987" ...
## $ idh_anio : num [1:27820] NA NA NA NA NA NA NA NA NA NA ...
## $ pib_anio : num [1:27820] 2.16e+09 2.16e+09 2.16e+09 2.16e+09 2.16e+09 ...
## $ pib_pcap : num [1:27820] 796 796 796 796 796 796 796 796 796 796 ...
## $ generacion: chr [1:27820] "Generation X" "Silent" "Generation X" "G.I. Generation" ...
## - attr(*, "spec")=
## .. cols(
## .. country = col_character(),
## .. year = col_double(),
## .. sex = col_character(),
## .. age = col_character(),
## .. suicides_no = col_double(),
## .. population = col_double(),
## .. `suicides/100k pop` = col_double(),
## .. `country-year` = col_character(),
## .. `HDI for year` = col_double(),
## .. `gdp_for_year ($)` = col_number(),
## .. `gdp_per_capita ($)` = col_double(),
## .. generation = col_character()
## .. )
## - attr(*, "problems")=<externalptr>
Cuando leemos la base de datos master.csv con la función
read.csv tenemos un problema con las comas de la variable
PIB por aƱo, lo cual hace que sea reconocida como una variable
categórica. Esto lo vemos observando los primeros 5 datos de dicha
variable:
master$pib_anio[1:5]
## [1] 2156624900 2156624900 2156624900 2156624900 2156624900
En la base de datos master.csv encontramos la variable
pais_anio la cual aparentemente no da mucha
información.
Para eliminarla podemos utilizar la función select del
paquete dplyr:
require(dplyr)
master %<>% select(-pais_anio) # - elimina
Verificamos que sà quedó eliminada:
master %>% names
## [1] "pais" "anio" "sexo" "edad" "num_suic"
## [6] "poblacion" "suic_x100k" "idh_anio" "pib_anio" "pib_pcap"
## [11] "generacion"
La función select también es útil para seleccionar un
subconjunto de variables deseadas:
master_sub <- master %>% select(anio, pais, num_suic)
head(master_sub) # Equivale a: master_sub %>% head
La función mutate del paquete dplyr es útil
para crear nuevas variables. Por ejemplo, si deseamos crear una variable
nueva que considere el porcentaje de suicidios, denotada por
p_suic, hacemos:
master %<>% mutate(p_suic = 100 * (num_suic / poblacion))
master %>% names
## [1] "pais" "anio" "sexo" "edad" "num_suic"
## [6] "poblacion" "suic_x100k" "idh_anio" "pib_anio" "pib_pcap"
## [11] "generacion" "p_suic"
Para obtener un resumen estadĆstico bĆ”sico de la variable que
acabamos de crear, p_suic, tenemos al menos dos
opciones:
# FORMA 1:
master$p_suic %>% summary
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000000 0.0009187 0.0059909 0.0128161 0.0166177 0.2249719
Algunas de las clases de variables que se manejan en R son:
as.numeric(): Convierte una variable a tipo numƩrico
(double).as.logical(): Convierte una variable a tipo
lógico.as.integer(): Convierte una variable a tipo
entero.as.factor(): Convierte una variable a tipo factor.as.character(): Convierte una variable a tipo carƔcter
(character).as.ordered(): Convierte una variable a tipo factor
asumiendo un orden o jerarquĆa entre los niveles.Otro tipo o clase de variables bastante utilizados en distintas bases
de datos tienen que ver con fechas. El paquete lubridate
tiene distintas opciones, entre las que resaltan:
as_date(): Convierte una variable de tipo carƔcter o
factor a tipo fecha.dmy(): Convierte una variable de tipo carƔcter o factor
a tipo fecha dĆa-mes-aƱo. TambiĆ©n estĆ”n las funciones mdy()
(mes-dĆa-aƱo), myd() (mes-aƱo-dĆa), ymd()
(aƱo-mes-dĆa), ydm() (aƱo-dĆa-mes) o dym()
(dĆa-aƱo-mes).NOTA: Luego de aplicar el formato fecha anterior, se
pueden aplicar las funciones: day() para extraer el dĆa,
month() para extraer el mes y year() para
extraer el aƱo.
require(tibble)
master %>% glimpse
## Rows: 27,820
## Columns: 12
## $ pais <chr> "Albania", "Albania", "Albania", "Albania", "Albania", "Albā¦
## $ anio <dbl> 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987,ā¦
## $ sexo <chr> "male", "male", "female", "male", "male", "female", "femaleā¦
## $ edad <chr> "15-24 years", "35-54 years", "15-24 years", "75+ years", "ā¦
## $ num_suic <dbl> 21, 16, 14, 1, 9, 1, 6, 4, 1, 0, 0, 0, 2, 17, 1, 14, 4, 8, ā¦
## $ poblacion <dbl> 312900, 308000, 289700, 21800, 274300, 35600, 278800, 25720ā¦
## $ suic_x100k <dbl> 6.71, 5.19, 4.83, 4.59, 3.28, 2.81, 2.15, 1.56, 0.73, 0.00,ā¦
## $ idh_anio <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,ā¦
## $ pib_anio <dbl> 2156624900, 2156624900, 2156624900, 2156624900, 2156624900,ā¦
## $ pib_pcap <dbl> 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, 796,ā¦
## $ generacion <chr> "Generation X", "Silent", "Generation X", "G.I. Generation"ā¦
## $ p_suic <dbl> 0.0067114094, 0.0051948052, 0.0048325854, 0.0045871560, 0.0ā¦
Transformemos las variables pais, sexo,
edad y generacion en variables de tipo factor.
Para esto utilizaremos la función mutate_at del paquete
dplyr:
require(dplyr)
master %<>% mutate_at(vars(pais, sexo, edad, generacion), as.factor)
El equivalente serĆa:
master$pais <- as.factor(master$pais)
master$sexo <- as.factor(master$sexo)
#master$edad <- as.factor(master$edad)
master$generacion <- as.factor(master$generacion)
Es utilizando el mutate_if:
master %<>% mutate_if(is.character, as.factor)
require(tibble)
master %>% glimpse
## Rows: 27,820
## Columns: 12
## $ pais <fct> Albania, Albania, Albania, Albania, Albania, Albania, Albanā¦
## $ anio <dbl> 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987,ā¦
## $ sexo <fct> male, male, female, male, male, female, female, female, malā¦
## $ edad <fct> 15-24 years, 35-54 years, 15-24 years, 75+ years, 25-34 yeaā¦
## $ num_suic <dbl> 21, 16, 14, 1, 9, 1, 6, 4, 1, 0, 0, 0, 2, 17, 1, 14, 4, 8, ā¦
## $ poblacion <dbl> 312900, 308000, 289700, 21800, 274300, 35600, 278800, 25720ā¦
## $ suic_x100k <dbl> 6.71, 5.19, 4.83, 4.59, 3.28, 2.81, 2.15, 1.56, 0.73, 0.00,ā¦
## $ idh_anio <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,ā¦
## $ pib_anio <dbl> 2156624900, 2156624900, 2156624900, 2156624900, 2156624900,ā¦
## $ pib_pcap <dbl> 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, 796,ā¦
## $ generacion <fct> Generation X, Silent, Generation X, G.I. Generation, Boomerā¦
## $ p_suic <dbl> 0.0067114094, 0.0051948052, 0.0048325854, 0.0045871560, 0.0ā¦
Ya vimos antes la función filter del paquete
dplyr para filtrar bases de datos y extraer subconjuntos de
bases de datos. AdemÔs de esta función, existe la función
subset del paquete base y que permite
seleccionar subconjuntos de datos. Como ejemplo, supongamos que queremos
extraer las variables:
master_sub <- subset(master,
subset = (anio < 2000 & pais == "Colombia"),
select = c(pais, anio, pib_anio, suic_x100k, sexo))
head(master_sub)
glimpse(master_sub)
## Rows: 180
## Columns: 5
## $ pais <fct> Colombia, Colombia, Colombia, Colombia, Colombia, Colombia,ā¦
## $ anio <dbl> 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,ā¦
## $ pib_anio <dbl> 34894411352, 34894411352, 34894411352, 34894411352, 3489441ā¦
## $ suic_x100k <dbl> 17.02, 11.13, 8.31, 8.02, 7.67, 3.73, 1.98, 1.72, 1.29, 1.0ā¦
## $ sexo <fct> male, male, male, male, male, female, female, female, femalā¦
summary(master_sub)
## pais anio pib_anio suic_x100k
## Colombia :180 Min. :1985 Min. :3.489e+10 Min. : 0.000
## Albania : 0 1st Qu.:1988 1st Qu.:3.921e+10 1st Qu.: 0.980
## Antigua and Barbuda: 0 Median :1992 Median :4.928e+10 Median : 2.575
## Argentina : 0 Mean :1992 Mean :6.228e+10 Mean : 4.518
## Armenia : 0 3rd Qu.:1996 3rd Qu.:9.251e+10 3rd Qu.: 7.795
## Aruba : 0 Max. :1999 Max. :1.067e+11 Max. :18.830
## (Other) : 0
## sexo
## female:90
## male :90
##
##
##
##
##
Para eliminar las categorĆas o paĆses que ya no aparecen en el
subconjunto de datos master_sub y que se heredaron de los
datos originales, master, utilizamos la función
droplevels del paquete base:
master_sub <- droplevels(master_sub)
Haciendo un resumen del subconjunto de datos, vemos que ya no aparecen las otras categorĆas o paĆses de la base de datos original:
summary(master_sub)
## pais anio pib_anio suic_x100k sexo
## Colombia:180 Min. :1985 Min. :3.489e+10 Min. : 0.000 female:90
## 1st Qu.:1988 1st Qu.:3.921e+10 1st Qu.: 0.980 male :90
## Median :1992 Median :4.928e+10 Median : 2.575
## Mean :1992 Mean :6.228e+10 Mean : 4.518
## 3rd Qu.:1996 3rd Qu.:9.251e+10 3rd Qu.: 7.795
## Max. :1999 Max. :1.067e+11 Max. :18.830
En muchas situaciones resulta muy Ćŗtil obtener resĆŗmenes estadĆsticos por subgrupos de datos que se obtienen de manera ānaturalā al considerar variables categóricas como por ejemplo: sexo, aƱo, paĆs, etc.
El programa R ofrece mĆŗltiples opciones y herramientas para obtener
dichos resĆŗmenes y entre estos destacan las funciones
group_by y summarise del paquete
dplyr que hace parte de la āfamiliaā
tidyverse.
Recuerde que la base de datos master_sub es un
subconjunto de master correspondiente a Colombia. Un
resumen por gƩnero se obtiene como:
require(dplyr)
master_sub %>%
group_by(sexo) %>%
summarise(
media = mean(suic_x100k),
mediana = median(suic_x100k),
n = n()
)
Un resumen por aƱo se obtiene como:
master_sub %>%
group_by(anio) %>%
summarise(
media = mean(suic_x100k),
mediana = median(suic_x100k),
n = n()
)
En algunos casos es necesario unir varios conjuntos o bases de datos
a través de una variable que funciona como un código de concatenación.
En R, existe la función merge del paquete
base. Para ver cómo funciona, considere los siguientes
conjuntos de datos:
dat1 = data.frame(
Nombre = c('Carlos', 'Juan', 'Sara', 'Pedro', 'Luis', 'Ana'),
Edad = c(21, 23, 19, 25, 19, 26)
)
dat2 = data.frame(
Id = c(1029, 2516, 8437, 9289, 7373),
Nombre = c('Ana', 'Carlos', 'Sara', 'Pedro', 'Luis')
)
dat3 = data.frame(
Genero = c('F', 'M', 'M', 'M', 'F', 'M'),
Nombre = c('Ana', 'Carlos', 'Juan', 'Pedro', 'Sara', 'Luis')
)
dat4 = data.frame(
Nombre = c('Carlos', 'Juan', 'Sara', 'Pedro', 'Luis', 'Ana'),
Programa = c('Estadistica', 'Matematicas', 'Ing. Sistemas', 'Ing. Civil', 'Economia', 'Ing. Ambiental')
)
Para visualizar los conjuntos de datos usamos la función
kable del paquete knitr:
require(knitr)
## Cargando paquete requerido: knitr
kable(dat1)
| Nombre | Edad |
|---|---|
| Carlos | 21 |
| Juan | 23 |
| Sara | 19 |
| Pedro | 25 |
| Luis | 19 |
| Ana | 26 |
kable(dat3)
| Genero | Nombre |
|---|---|
| F | Ana |
| M | Carlos |
| M | Juan |
| M | Pedro |
| F | Sara |
| M | Luis |
Aplicamos la función merge para unir los conjuntos de
datos dat1 y dat2:
datos_1 <- merge(dat1, dat2, by = 'Nombre')
kable(datos_1)
| Nombre | Edad | Id |
|---|---|---|
| Ana | 26 | 1029 |
| Carlos | 21 | 2516 |
| Luis | 19 | 7373 |
| Pedro | 25 | 9289 |
| Sara | 19 | 8437 |
Con la opción sort=FALSE descartamos que el conjunto de datos los organice en orden alfabético por Nombre.
datos_1<-merge(dat1,dat2,by='Nombre', sort=FALSE)
kable(datos_1)
| Nombre | Edad | Id |
|---|---|---|
| Carlos | 21 | 2516 |
| Sara | 19 | 8437 |
| Pedro | 25 | 9289 |
| Luis | 19 | 7373 |
| Ana | 26 | 1029 |
Para que la unión de las bases de datos considere todos los registros, usamos la opción all=TRUE. En este caso, Juan aparece, pero con un NA en la variable Id.
datos_1<-merge(dat1,dat2,by='Nombre', all=TRUE)
kable(datos_1)
| Nombre | Edad | Id |
|---|---|---|
| Ana | 26 | 1029 |
| Carlos | 21 | 2516 |
| Juan | 23 | NA |
| Luis | 19 | 7373 |
| Pedro | 25 | 9289 |
| Sara | 19 | 8437 |
Unimos los conjuntos dat1 y dat2 con dat3:
datos_3<-merge(datos_1,dat3,by='Nombre', all=TRUE)
kable(datos_3)
| Nombre | Edad | Id | Genero |
|---|---|---|---|
| Ana | 26 | 1029 | F |
| Carlos | 21 | 2516 | M |
| Juan | 23 | NA | M |
| Luis | 19 | 7373 | M |
| Pedro | 25 | 9289 | M |
| Sara | 19 | 8437 | F |
Note que la función merge solo permite unir dos bases de datos a la vez. Unimos los conjuntos dat1, dat2 y dat3 con dat4:
datos_4<-merge(datos_3, dat4, by='Nombre', all=TRUE)
kable(datos_4)
| Nombre | Edad | Id | Genero | Programa |
|---|---|---|---|---|
| Ana | 26 | 1029 | F | Ing. Ambiental |
| Carlos | 21 | 2516 | M | Estadistica |
| Juan | 23 | NA | M | Matematicas |
| Luis | 19 | 7373 | M | Economia |
| Pedro | 25 | 9289 | M | Ing. Civil |
| Sara | 19 | 8437 | F | Ing. Sistemas |
Para concatenar todas las bases de datos en una sola lĆnea, podemos utilizar la función Reduce del paquete base.
dat1 = data.frame(
Nombre=c('Carlos','Juan','Sara','Pedro','Luis','Ana'),
Edad=c(21,23,19,25,19,26))
dat2 = data.frame(
Id=c(1029,2516,8437,9289,7373),
Nombre=c('Ana','Carlos','Sara','Pedro','Luis'))
dat3 = data.frame(
Genero=c('F','M','M','M','F','M'),
Nombre=c('Ana','Carlos','Juan','Pedro','Sara','Luis'))
dat4 = data.frame(
Nombre=c('Carlos','Juan','Sara','Pedro','Luis','Ana'),
Programa=c('Estadistica','Matematicas','Ing. Sistemas',
'Ing. Civil','Economia','Ing. Ambiental'))
La función Reduce tiene como primer argumento una función y como segundo argumento una lista con las bases de datos:
datos_4<-Reduce(merge, list(dat1,dat2,dat3,dat4))
kable(datos_4)
| Nombre | Edad | Id | Genero | Programa |
|---|---|---|---|---|
| Ana | 26 | 1029 | F | Ing. Ambiental |
| Carlos | 21 | 2516 | M | Estadistica |
| Luis | 19 | 7373 | M | Economia |
| Pedro | 25 | 9289 | M | Ing. Civil |
| Sara | 19 | 8437 | F | Ing. Sistemas |
Note que no se incluye a Juan en la base de datos final, ya que la función merge por defecto incluye solo datos completos.
Para cambiar las opciones del merge, debemos crear una función auxiliar que permita incluir todos los casos:
merge_aux <- function(...) merge (..., all=TRUE)
datos_4<-Reduce(merge_aux, list(dat1,dat2,dat3,dat4))
kable(datos_4)
| Nombre | Edad | Id | Genero | Programa |
|---|---|---|---|---|
| Ana | 26 | 1029 | F | Ing. Ambiental |
| Carlos | 21 | 2516 | M | Estadistica |
| Juan | 23 | NA | M | Matematicas |
| Luis | 19 | 7373 | M | Economia |
| Pedro | 25 | 9289 | M | Ing. Civil |
| Sara | 19 | 8437 | F | Ing. Sistemas |
Se consideran los datos de importación de vehĆculos reportados por
Fasecolda, que estƔn divididos en cuatro bases de
datos: DB1.csv, DB2.csv, DB3.csv
y DB4.csv.
Estos archivos pueden descargarse desde el siguiente enlace de GitHub: Datos de Importación de VehĆculos.
BD_1<-read_csv("DB1.csv")
## Rows: 12433 Columns: 3
## āā Column specification āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
## Delimiter: ","
## chr (1): Marca
## dbl (2): Codigo, Peso
##
## ā¹ Use `spec()` to retrieve the full column specification for this data.
## ā¹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
names(BD_1)
## [1] "Codigo" "Marca" "Peso"
BD_2<-read.csv("DB2.csv",header=TRUE)
names(BD_2)
## [1] "Codigo" "Estado"
BD_3<-read.csv("DB3.csv",header=TRUE)
names(BD_3)
## [1] "Importado" "AireAcondicionado" "Codigo"
## [4] "Cilindraje" "Potencia"
BD_4<-read.csv("DB4.csv",header=TRUE)
names(BD_4)
## [1] "Estado" "Nacionalidad" "Combustible" "Puertas" "Transmision"
## [6] "Codigo"
merge_aux <- function(...) merge (..., all=T)
Vehiculos <- Reduce(merge_aux,list(BD_1,BD_2,BD_3,BD_4))
Vehiculos %>% names
## [1] "Codigo" "Estado" "Marca"
## [4] "Peso" "Importado" "AireAcondicionado"
## [7] "Cilindraje" "Potencia" "Nacionalidad"
## [10] "Combustible" "Puertas" "Transmision"
Vehiculos %>% glimpse
## Rows: 12,433
## Columns: 12
## $ Codigo <dbl> 10001001, 10001002, 10001003, 10001004, 10004001, 10ā¦
## $ Estado <chr> "Activo", "Activo", "Activo", "Activo", "Activo", "Aā¦
## $ Marca <chr> "TATA", "TATA", "TATA", "TATA", "TATA", "TATA", "TATā¦
## $ Peso <dbl> 980, 0, 1005, 1070, 0, 2225, 1070, 1521, 1477, 1338,ā¦
## $ Importado <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1ā¦
## $ AireAcondicionado <int> 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0ā¦
## $ Cilindraje <int> 1405, 1400, 1405, 1405, 1984, 2956, 1405, 3800, 3100ā¦
## $ Potencia <int> 43, 0, 75, 84, 0, 113, 83, 170, 160, 120, 205, 205, ā¦
## $ Nacionalidad <chr> "IND", "IND", "IND", "IND", "IND", "IND", "IND", "USā¦
## $ Combustible <chr> "DSL", "DSL", "", "", "", "", "", "GSL", "GSL", "GSLā¦
## $ Puertas <int> 5, 5, 5, 5, 2, 5, 5, 4, 2, 4, 4, 4, 4, 4, 4, 2, 0, 2ā¦
## $ Transmision <chr> "4X2", "4X2", "", "", "", "4X4", "", "4X2", "4X2", "ā¦
Vehiculos %>% summary
## Codigo Estado Marca Peso
## Min. : 101001 Length:12433 Length:12433 Min. : 0
## 1st Qu.: 3201240 Class :character Class :character 1st Qu.: 268
## Median : 6201016 Mode :character Mode :character Median : 1355
## Mean : 8406562 Mean : 1778
## 3rd Qu.: 9401013 3rd Qu.: 2005
## Max. :40301001 Max. :41000
## Importado AireAcondicionado Cilindraje Potencia
## Min. :0.0000 Min. :0.0000 Min. : 0 Min. : 0.0
## 1st Qu.:1.0000 1st Qu.:0.0000 1st Qu.: 1339 1st Qu.: 80.0
## Median :1.0000 Median :1.0000 Median : 1998 Median :122.0
## Mean :0.8407 Mean :0.5501 Mean : 2585 Mean :134.7
## 3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.: 3246 3rd Qu.:175.0
## Max. :1.0000 Max. :1.0000 Max. :15950 Max. :662.0
## Nacionalidad Combustible Puertas Transmision
## Length:12433 Length:12433 Min. :0.000 Length:12433
## Class :character Class :character 1st Qu.:2.000 Class :character
## Mode :character Mode :character Median :3.000 Mode :character
## Mean :2.952
## 3rd Qu.:5.000
## Max. :6.000
Vehiculos$Transmision %>%as.factor%>%summary
## 2X1 3X1 3X2 4x2 4X2 4X4 6X2 6x4 6X4 6X6 8X4
## 700 1402 59 15 4 7603 2147 14 1 463 2 23
Vehiculos$Transmision %>%as.factor %>% summary %>%
sort(decreasing=TRUE) %>%barplot(las=1)
suppressWarnings(require(editrules))
## Cargando paquete requerido: editrules
## Cargando paquete requerido: igraph
##
## Adjuntando el paquete: 'igraph'
## The following objects are masked from 'package:lubridate':
##
## %--%, union
## The following objects are masked from 'package:dplyr':
##
## as_data_frame, groups, union
## The following objects are masked from 'package:purrr':
##
## compose, simplify
## The following object is masked from 'package:tidyr':
##
## crossing
## The following object is masked from 'package:tibble':
##
## as_data_frame
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
## The following object is masked from 'package:base':
##
## union
##
## Adjuntando el paquete: 'editrules'
## The following objects are masked from 'package:igraph':
##
## blocks, normalize
## The following object is masked from 'package:dplyr':
##
## contains
## The following object is masked from 'package:purrr':
##
## reduce
## The following objects are masked from 'package:tidyr':
##
## contains, separate
reglas <-c("Peso>0",
"Potencia>0",
"Cilindraje>0",
"Transmision%in%c('2X1','3X1','3X2','4x2','4X2','4X4',
'6X2','6x4','6X4','6X6','8X4')",
"Combustible%in%c('DSL','ELT','GAS','GSL','HBD')")
Condiciones<-editset(reglas)
Condiciones
##
## Data model:
## dat1 : Combustible %in% c('DSL', 'ELT', 'GAS', 'GSL', 'HBD')
## dat2 : Transmision %in% c('2X1', '3X1', '3X2', '4x2', '4X2', '4X4', '6X2', '6x4', '6X4', '6X6', '8X4')
##
## Edit set:
## num1 : 0 < Peso
## num2 : 0 < Potencia
## num3 : 0 < Cilindraje
Para chequear cuƔles elementos de nuestra base de datos cumplen o no
con las reglas que impusimos, utilizamos la función
violatedEdits del paquete editrules:
Errores <- violatedEdits(c(Condiciones), Vehiculos)
Errores[1:6, ] # Presentamos las primeras 6 filas
## edit
## record num1 num2 num3 dat1 dat2
## 1 FALSE FALSE FALSE FALSE FALSE
## 2 TRUE TRUE FALSE FALSE FALSE
## 3 FALSE FALSE FALSE TRUE TRUE
## 4 FALSE FALSE FALSE TRUE TRUE
## 5 TRUE TRUE FALSE TRUE TRUE
## 6 FALSE FALSE FALSE TRUE FALSE
plot(Errores) #Graficamoslos%deerrores
Para ubicar los errores, usamos la función
localizeErrors del paquete editrules:
Localiza <- localizeErrors(Condiciones, Vehiculos)$adapt
# Lo transformamos en data frame:
Localiza <- data.frame(Localiza)
Visualizamos Localiza:
head(Localiza) # TRUE significa incumple alguna regla
Para ver cuƔntas filas hay con por lo menos un problema, utilizamos
la función apply del paquete base junto con
una función auxiliar creada con la función any, también del
paquete base:
any_aux <- function(x) any(x == TRUE) # Función auxiliar
# Verifica fila por fila (a eso se refiere el 1)
verificacion <- apply(Localiza, 1, any_aux)
# Para saber cuƔles filas tienen al menos un problema:
filas <- which(verificacion)
# Para contar cuƔntas filas hay con al menos un problema:
length(filas)
## [1] 2785
#% de filas con al menos un problema:
100*(length(filas)/nrow(Vehiculos))
## [1] 22.40006
Continuemos analizando entonces la base de datos master.csv y para ello la procesamos:
direccion<-"master.csv"
master <- read_csv(direccion)
## Rows: 27820 Columns: 12
## āā Column specification āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
## Delimiter: ","
## chr (5): country, sex, age, country-year, generation
## dbl (6): year, suicides_no, population, suicides/100k pop, HDI for year, gdp...
## num (1): gdp_for_year ($)
##
## ā¹ Use `spec()` to retrieve the full column specification for this data.
## ā¹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
viejos <- master %>% names
master %<>% rename(pais=viejos[1], anio=viejos[2],
sexo=viejos[3], edad=viejos[4],
num_suic=viejos[5], poblacion=viejos[6],
suic_x100k=viejos[7], pais_anio=viejos[8],
idh_anio=viejos[9], pib_anio=viejos[10],
pib_pcap=viejos[11], generacion=viejos[12]
)
master %<>% select(-pais_anio)
master %<>% mutate(pib_anio_mill=pib_anio/1000000)
master %<>% mutate_if(is.character, as.factor)
master %>% glimpse
## Rows: 27,820
## Columns: 12
## $ pais <fct> Albania, Albania, Albania, Albania, Albania, Albania, Alā¦
## $ anio <dbl> 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 19ā¦
## $ sexo <fct> male, male, female, male, male, female, female, female, ā¦
## $ edad <fct> 15-24 years, 35-54 years, 15-24 years, 75+ years, 25-34 ā¦
## $ num_suic <dbl> 21, 16, 14, 1, 9, 1, 6, 4, 1, 0, 0, 0, 2, 17, 1, 14, 4, ā¦
## $ poblacion <dbl> 312900, 308000, 289700, 21800, 274300, 35600, 278800, 25ā¦
## $ suic_x100k <dbl> 6.71, 5.19, 4.83, 4.59, 3.28, 2.81, 2.15, 1.56, 0.73, 0.ā¦
## $ idh_anio <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ā¦
## $ pib_anio <dbl> 2156624900, 2156624900, 2156624900, 2156624900, 21566249ā¦
## $ pib_pcap <dbl> 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, 796, 7ā¦
## $ generacion <fct> Generation X, Silent, Generation X, G.I. Generation, Booā¦
## $ pib_anio_mill <dbl> 2156.625, 2156.625, 2156.625, 2156.625, 2156.625, 2156.6ā¦
master$edad %>%levels
## [1] "15-24 years" "25-34 years" "35-54 years" "5-14 years" "55-74 years"
## [6] "75+ years"
master$generacion %>%levels
## [1] "Boomers" "G.I. Generation" "Generation X" "Generation Z"
## [5] "Millenials" "Silent"
Se redifen los niveles
master$edad %<>%factor(levels=c("5-14years","15-24years",
"25-34years","35-54years","55-74years","75+years"))
master$generacion %<>%factor(levels=c('G.I.Generation','Silent',
'Boomers','GenerationX',
'Millenials','GenerationZ'))
master_col <- subset(master,subset=(pais=="Colombia"))
master_col %<>% droplevels
master_col %>% glimpse
## Rows: 372
## Columns: 12
## $ pais <fct> Colombia, Colombia, Colombia, Colombia, Colombia, Colombā¦
## $ anio <dbl> 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 19ā¦
## $ sexo <fct> male, male, male, male, male, female, female, female, feā¦
## $ edad <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ā¦
## $ num_suic <dbl> 21, 113, 193, 256, 188, 117, 45, 3, 31, 12, 12, 10, 13, ā¦
## $ poblacion <dbl> 123400, 1015200, 2323700, 3190200, 2451100, 3140700, 227ā¦
## $ suic_x100k <dbl> 17.02, 11.13, 8.31, 8.02, 7.67, 3.73, 1.98, 1.72, 1.29, ā¦
## $ idh_anio <dbl> 0.573, 0.573, 0.573, 0.573, 0.573, 0.573, 0.573, 0.573, ā¦
## $ pib_anio <dbl> 34894411352, 34894411352, 34894411352, 34894411352, 3489ā¦
## $ pib_pcap <dbl> 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 13ā¦
## $ generacion <fct> NA, NA, Boomers, NA, Silent, NA, Boomers, NA, Silent, NAā¦
## $ pib_anio_mill <dbl> 34894.41, 34894.41, 34894.41, 34894.41, 34894.41, 34894.ā¦
master_col %>% summary
## pais anio sexo edad num_suic
## Colombia:372 Min. :1985 female:186 NA's:372 Min. : 0.00
## 1st Qu.:1992 male :186 1st Qu.: 28.75
## Median :2000 Median : 79.50
## Mean :2000 Mean :142.69
## 3rd Qu.:2008 3rd Qu.:221.25
## Max. :2015 Max. :629.00
##
## poblacion suic_x100k idh_anio pib_anio
## Min. : 123400 Min. : 0.000 Min. :0.5730 Min. :3.489e+10
## 1st Qu.:1619744 1st Qu.: 1.087 1st Qu.:0.6290 1st Qu.:4.928e+10
## Median :3390041 Median : 2.855 Median :0.6925 Median :9.820e+10
## Mean :2978339 Mean : 5.402 Mean :0.6703 Mean :1.445e+11
## 3rd Qu.:4092334 3rd Qu.: 9.200 3rd Qu.:0.7150 3rd Qu.:2.338e+11
## Max. :6566323 Max. :21.910 Max. :0.7200 Max. :3.802e+11
## NA's :252
## pib_pcap generacion pib_anio_mill
## Min. :1299 Silent : 82 Min. : 34894
## 1st Qu.:1664 Boomers : 68 1st Qu.: 49280
## Median :2796 Millenials: 72 Median : 98204
## Mean :3709 NA's :150 Mean :144464
## 3rd Qu.:5643 3rd Qu.:233822
## Max. :8731 Max. :380192
##
Se pueden obtener resumenes
resum_col_1<-master_col %>%group_by(anio) %>%
summarise(pib_anio_mill=mean(pib_anio_mill),
suic_x100k=mean(suic_x100k))
resum_col_1 %<>%select(anio,pib_anio_mill,suic_x100k)
resum_col_1
ggplot2
El paquete ggplot2 es una de las herramientas mƔs
poderosas y flexibles en R para la visualización de datos. Desarrollado
por Hadley Wickham, ggplot2 se basa en la gramƔtica de
grƔficos (Grammar of Graphics), un enfoque que proporciona un marco
coherente para construir visualizaciones.
Sistema de capas: ggplot2 permite
construir grƔficos a travƩs de capas. Puedes empezar con datos bƔsicos y
agregar capas adicionales para personalizar la apariencia, como lĆneas
de tendencia, etiquetas, tĆtulos y temas.
Altamente personalizable: Cada aspecto de un
grƔfico en ggplot2 puede ser personalizado. Desde los
colores y formas de los puntos hasta los temas y leyendas, el paquete
ofrece un control detallado sobre cómo se presentan los datos.
Facilidad de uso con dplyr y
tidyr: ggplot2 se integra
perfectamente con otros paquetes del ecosistema tidyverse, como
dplyr y tidyr, lo que facilita la manipulación
y preparación de los datos antes de visualizarlos.
library(ggplot2)
ggplot(resum_col_1,aes(pib_anio_mill,suic_x100k))+
geom_point(col="blue",cex=2)
ggplot(resum_col_1,aes(anio,suic_x100k))+
geom_line(col="red")+
labs(title = "Evolución de la Tasa de Suicidios por 100.000 Habitantes en Colombia")
ggplot(resum_col_1,aes(anio,pib_anio_mill))+
geom_line(col="black") +
labs(title = "Evolución de la Tasa de Suicidios por 100.000 Habitantes en Colombia")
ggplotPara considerar varios paneles, utilizamos la función
ggarrange del paquete ggpubr. Antes de usarla,
debemos definir los grƔficos que queremos ubicar en dichos paneles:
fig_1 <- ggplot(resum_col_1, aes(pib_anio_mill, suic_x100k)) +
geom_point(col = "blue", cex = 2)
fig_2 <- ggplot(resum_col_1, aes(anio, pib_anio_mill)) +
geom_line(col = "black")
fig_3 <- ggplot(resum_col_1, aes(anio, suic_x100k)) +
geom_line(col = "red")
require(ggpubr)
## Cargando paquete requerido: ggpubr
ggarrange(fig_1,fig_2,fig_3,labels= c("A","B","C"),
ncol=2,nrow=2)
resum_col_2<-master_col %>%group_by(anio,sexo) %>%
summarise(pib_anio_mill=mean(pib_anio_mill),
suic_x100k=mean(suic_x100k))
## `summarise()` has grouped output by 'anio'. You can override using the
## `.groups` argument.
resum_col_2 %<>%select(sexo,anio,pib_anio_mill,
suic_x100k)
resum_col_2
ggplot(resum_col_2, aes(x = anio, y = suic_x100k, color = sexo)) +
geom_point(cex = 3) +
labs(
title = "Tasa de Suicidios por 100.000 Habitantes en Colombia según Género",
x = "AƱo",
y = "Tasa de Suicidios por 100.000 Habitantes",
color = "GƩnero"
)
ggplot(resum_col_2, aes(x = anio, y = suic_x100k, color = sexo)) +
geom_line(cex = 1) +
labs(
title = "Evolución de la Tasa de Suicidios por 100.000 Habitantes en Colombia según Género",
x = "AƱo",
y = "Tasa de Suicidios por 100.000 Habitantes",
color = "GƩnero"
)
### Resumen 3
resum_col_3<-master_col %>%group_by(generacion,sexo) %>%
summarise(num_suic=sum(num_suic))
## `summarise()` has grouped output by 'generacion'. You can override using the
## `.groups` argument.
resum_col_3 %<>%select(generacion,sexo,num_suic)
resum_col_3
ggplot(resum_col_3, aes(x = generacion, y = num_suic, fill = sexo)) +
geom_bar(stat = "identity") +
geom_text(aes(label = num_suic),
position = position_stack(vjust = 0.5),
color = "white") +
labs(
title = "Número de Suicidios por Generación y Género en Colombia",
x = "Generación",
y = "NĆŗmero de Suicidios",
fill = "GƩnero"
)
ggplot(resum_col_3,aes(x=generacion,y=num_suic,fill=sexo))+
geom_bar(stat="identity",position="dodge")+
geom_text(aes(label=num_suic),
position=position_dodge(width=0.9),vjust=-0.25) + labs(
title = "Número de Suicidios por Generación y Género en Colombia",
x = "Generación",
y = "NĆŗmero de Suicidios",
fill = "GƩnero"
)
fig_1<-ggplot(resum_col_3,aes(x=generacion,y=num_suic,
fill=sexo))+ geom_bar(stat="identity")
fig_1+facet_grid(rows =vars(sexo)) + labs(
title = "Número de Suicidios por Generación y Género en Colombia",
x = "Generación",
y = "NĆŗmero de Suicidios",
fill = "GƩnero"
)
Un tipo de grÔfico que permite relacionar dos variables categóricas
se obtiene con la función PlotXTabs del paquete
CGPfunctions:
library(CGPfunctions)
if (exists("master_col") && is.data.frame(master_col)) {
PlotXTabs(master_col, "sexo", "generacion")
} else {
print("El objeto 'master_col' no existe o no es un data.frame")
}
require(CGPfunctions)
#PlotXTabs(master_col, sexo, generacion)
#PlotXTabs(master_col, x = "sexo", fill = "generacion")
PlotXTabs(master_col, "sexo", "generacion")
`
resum_col_5<-master_col %>%group_by(anio,sexo) %>%
summarise(suic_x100k=mean(suic_x100k),
pib_anio=mean(pib_anio),
pib_anio_mill=mean(pib_anio_mill),
num_suic=sum(num_suic),
poblacion=sum(poblacion))
## `summarise()` has grouped output by 'anio'. You can override using the
## `.groups` argument.
resum_col_5 %<>%select(anio,sexo,suic_x100k,pib_anio,
pib_anio_mill,num_suic,poblacion)
resum_col_5
require(GGally)
## Cargando paquete requerido: GGally
## Registered S3 method overwritten by 'GGally':
## method from
## +.gg ggplot2
resum_col_5 %>%ggpairs(mapping=aes(col=sexo,alpha=0.4))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
fig_1<-ggplot(resum_col_5,aes(x=anio,y=poblacion,
col=sexo,fill=sexo))+
geom_point()+
geom_smooth(method ="lm")
fig_2<-ggplot(resum_col_5,aes(x=anio,y=pib_anio_mill,
col=sexo,fill=sexo))+
geom_point()+
geom_smooth(method ="lm")
require(ggpubr)
ggarrange(fig_1,fig_2,labels=c("A","B"),ncol=1,nrow=2)
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
ggplot(resum_col_5, aes(x = anio, y = pib_anio_mill, col = sexo, fill = sexo)) +
geom_boxplot(color = "blue") +
labs(
title = "Distribución del PIB por Año y Sexo en Colombia",
x = "AƱo",
y = "PIB (en millones)",
fill = "GƩnero",
col = "GƩnero"
)
# Agrupar los datos por aƱo y sexo, y calcular los valores mĆnimos y mĆ”ximos de cada grupo
min1 <- resum_col_5 %>% group_by(anio, sexo) %>% summarise(min_suic_x100k = min(suic_x100k))
## `summarise()` has grouped output by 'anio'. You can override using the
## `.groups` argument.
BD_min1 <- filter(resum_col_5, suic_x100k %in% min1$min_suic_x100k)
max1 <- resum_col_5 %>% group_by(anio, sexo) %>% summarise(max_suic_x100k = max(suic_x100k))
## `summarise()` has grouped output by 'anio'. You can override using the
## `.groups` argument.
BD_max1 <- filter(resum_col_5, suic_x100k %in% max1$max_suic_x100k)
# Crear una tabla con los valores mĆnimos y mĆ”ximos
Tabla1 <- data.frame(
Anio = BD_min1$anio,
Sexo = BD_min1$sexo,
Minimo_suic_x100k = BD_min1$suic_x100k,
Maximo_suic_x100k = BD_max1$suic_x100k,
row.names = NULL
)
# Requerir knitr y mostrar la tabla
require(knitr)
kable(Tabla1[1:9,])
| Anio | Sexo | Minimo_suic_x100k | Maximo_suic_x100k |
|---|---|---|---|
| 1985 | female | 1.676667 | 1.676667 |
| 1985 | male | 8.750000 | 8.750000 |
| 1986 | female | 1.381667 | 1.381667 |
| 1986 | male | 6.970000 | 6.970000 |
| 1987 | female | 1.220000 | 1.220000 |
| 1987 | male | 6.308333 | 6.308333 |
| 1988 | female | 1.271667 | 1.271667 |
| 1988 | male | 7.168333 | 7.168333 |
| 1989 | female | 1.175000 | 1.175000 |
Los datos faltantes es uno de los temas que se ignoran en la mayorĆa de los textos introductorios. Probablemente, parte de la razón por la que esto es asĆ es que todavĆa abundan muchos mitos sobre el anĆ”lisis con datos faltantes. AdemĆ”s, algunas de las investigaciones sobre tĆ©cnicas de vanguardia es aĆŗn relativamente nueva. Una razón mĆ”s legĆtima para su ausencia en los textos introductorios es que la mayorĆa de las metodologĆas son bastante complicadas, desde el punto de vista matemĆ”tico. Sin embargo, la increĆble ubicuidad de los problemas relacionados con los datos faltantes en el anĆ”lisis de datos de la vida real requiere que se aborde el tema.
Hay tratamientos para los datos que faltan, pero no hay tratamientos para los datos malos o incorrectos. El tratamiento estĆ”ndar para el problema de los datos que faltan es reemplazar los datos que faltan por valores no ausentes. Este proceso se denomina imputación. En la mayorĆa de los casos, el objetivo de la imputación no es recrear el conjunto de datos faltantes completo, sino permitir que se realicen estimaciones o inferencias.
Por ello, la eficacia de las diferentes tĆ©cnicas de imputación no puede evaluarse por su capacidad de recrear los datos con la mayor exactitud posible a partir de un conjunto de datos faltantes simulado, sino que deben juzgarse por su capacidad de apoyar las mismas inferencias estadĆsticas que se obtendrĆan del anĆ”lisis de los datos completos que se extraen del anĆ”lisis.
De este modo, rellenar los datos que faltan es sólo un paso hacia el verdadero objetivo: el anÔlisis. El conjunto de datos imputados rara vez se considera el objetivo final de la imputación. En la prÔctica, hay muchas formas diferentes de tratar los datos que faltan, algunas son buenas y otras no tanto. Algunas estÔn bien en determinadas circunstancias, pero no en otras. Algunas implican la eliminación de datos faltantes, mientras que otras implican la imputación. Vamos a mencionar brevemente algunos de los métodos mÔs comunes. Sin embargo, el objetivo final de esta sección, es iniciarle en lo que a menudo se describe como el estÔndar de oro de las técnicas de imputación: la imputación múltiple.
El paquete VIM nos permite visualizar los patrones de
datos faltantes. Un tƩrmino clave relacionado con los datos faltantes es
el mecanismo de datos faltantes, que describe el
proceso que determina la probabilidad de que cada punto de datos sea
faltante. Existen tres categorĆas principales de mecanismos de datos
faltantes:
Para mĆ”s información, consulta el artĆculo āExploring Incomplete Data Using Visualization Techniquesā. El artĆculo describe tĆ©cnicas como grĆ”ficos de agregación, diagramas de caja marginales, grĆ”ficos de mosaico, diagramas de dispersión con codificación de valores faltantes y Parallel boxplots para explorar datos incompletos.
Tomando el conjunto de datos airquality, un conjunto
de datos de mediciones diarias de la calidad del aire en Nueva
York de mayo a septiembre de 1973, que tiene valores
NA dentro de sus variables. Las filas del conjunto de datos
representan 154 dĆas consecutivos. Cualquier
eliminación de estas filas afectarÔ a la continuidad del tiempo, lo que
puede afectar a cualquier anƔlisis de series temporales que se
realice. Veamos con mƔs detalle el conjunto de datos de la
calidad del aire.
Iniciamos visualizando los datos faltantes. Eliminaremos
algunos puntos de datos del conjunto de datos para este ejemplo. En
lo que respecta a las variables categóricas, la sustitución de las
mismas no suele ser aconsejable. Algunas prƔcticas comunes incluyen
la sustitución de las variables categóricas que faltan por la moda de
las observadas, sin embargo, es cuestionable si es una buena
opción. Aunque en este caso no faltan puntos de datos de las
variables categóricas, las eliminamos de nuestro conjunto de datos
(podemos volver a aƱadirlas mƔs tarde si es necesario) y echamos un
vistazo a los datos utilizando summary().
library(VIM)
## Cargando paquete requerido: colorspace
## Cargando paquete requerido: grid
## VIM is ready to use.
## Suggestions and bug-reports can be submitted at: https://github.com/statistikat/VIM/issues
##
## Adjuntando el paquete: 'VIM'
## The following object is masked from 'package:datasets':
##
## sleep
data("airquality")
knitr::kable(head(airquality),
caption = "Primeras 6 filas del conjunto de datos 'airquality'",
col.names = c("Ozono", "Radiación Solar", "Viento", "Temperatura", "Mes", "DĆa"),
align = 'c')
| Ozono | Radiación Solar | Viento | Temperatura | Mes | DĆa |
|---|---|---|---|---|---|
| 41 | 190 | 7.4 | 67 | 5 | 1 |
| 36 | 118 | 8.0 | 72 | 5 | 2 |
| 12 | 149 | 12.6 | 74 | 5 | 3 |
| 18 | 313 | 11.5 | 62 | 5 | 4 |
| NA | NA | 14.3 | 56 | 5 | 5 |
| 28 | NA | 14.9 | 66 | 5 | 6 |
aggr(airquality, numbers = TRUE, col = c("navyblue", "red"),
cex.axis = 0.7, gap = 3, ylab = c("Proportion of missingness", "Missingness pattern"))
library(mice)
##
## Adjuntando el paquete: 'mice'
## The following object is masked from 'package:stats':
##
## filter
## The following objects are masked from 'package:base':
##
## cbind, rbind
md.pattern(airquality)
## Wind Temp Month Day Solar.R Ozone
## 111 1 1 1 1 1 1 0
## 35 1 1 1 1 1 0 1
## 5 1 1 1 1 0 1 1
## 2 1 1 1 1 0 0 2
## 0 0 0 0 7 37 44
library(naniar)
gg_miss_var(airquality, show_pct = TRUE)
library(Amelia)
missmap(airquality, col = c("red", "blue"), legend = TRUE)
library(VIM)
data("airquality")
marginplot(airquality[, c("Ozone", "Solar.R")],
col = c("darkgray", "red", "blue"),
cex.numbers = 1.3, pch = 19,
cex.lab = 1.5, cex.main = 1.5)
El anterior grÔfico muestra la relación entre las variables
Ozone y Solar.R en el conjunto de datos
airquality, destacando que la mayorĆa de los puntos se
concentran en valores bajos de Ozone, con una amplia
variabilidad en Solar.R. Los boxplots en los ejes
indican la distribución de cada variable, revelando la presencia de
varios valores atĆpicos (marcados en rojo) en ambas variables,
especialmente en Ozone, donde se observa un grupo
significativo de outliers en valores bajos. AdemƔs, se
identifican valores faltantes en Solar.R (2) y
Ozone (37), lo que podrĆa influir en la interpretación de
la relación entre estas dos variables.
Para abordar problemas de datos faltantes en estadĆstica, existen diversas metodologĆas que pueden ser aplicadas dependiendo del tipo de datos y del mecanismo de falta. A continuación se mencionan algunas de las principales tĆ©cnicas:
El mĆ©todo mĆ”s utilizado por los cientĆficos de datos para tratar los
datos que faltan es simplemente omitir los casos con datos que
faltan, analizando Ćŗnicamente el resto del conjunto de datos.
Este método se conoce como eliminación por lista o anÔlisis de casos
completos. En R, la función na.omit() elimina todos los
casos con uno o mƔs valores faltantes en un conjunto de datos.
Ejemplo en R:
A continuación, se muestra un ejemplo simple utilizando la función
na.omit() para eliminar los casos que contienen valores
faltantes (NAs):
data("airquality")
airquality_omit <- na.omit(airquality)
head(airquality_omit)
ggplotsuppressWarnings({
library(ggplot2)
library(tidyverse)
library(hrbrthemes)
library(gridExtra)
library(missForest)
})
##
## Adjuntando el paquete: 'gridExtra'
## The following object is masked from 'package:dplyr':
##
## combine
##
## Adjuntando el paquete: 'missForest'
## The following object is masked from 'package:VIM':
##
## nrmse
ggp1 <- ggplot(data.frame(value=airquality$Ozone), aes(x=value)) +
geom_histogram(fill="#FD0000", color="#E52521", alpha=0.9) +
ggtitle("Original data") +
xlab("Ozone") + ylab("Frequency") +
theme_ipsum() +
theme(plot.title = element_text(size=15))
ggp2 <- ggplot(data.frame(value=airquality_omit$Ozone), aes(x=value)) +
geom_histogram(fill="#43B047", color="#049DCB", alpha=0.9) +
ggtitle("Listwise Deletion") +
xlab("Ozone") + ylab("Frequency") +
theme_ipsum() +
theme(plot.title = element_text(size=15))
grid.arrange(ggp1, ggp2, ncol = 2)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
mice.pmm (Predictive Mean Matching): Es el mƩtodo mƔs utilizado para variables numƩricas. Busca valores observados similares a las predicciones del modelo y los usa para imputar los valores faltantes.
logreg (Logistic Regression): Utiliza la regresión logĆstica para imputar valores en variables categóricas binarias (sĆ/no).
polyreg (Polynomial Regression): Emplea la regresión polinómica para imputar variables categóricas con múltiples niveles.
norm (Normal Linear Regression): Usa regresión lineal asumiendo una distribución normal de la variable para imputar valores numéricos.
mean (Media): Imputa el valor faltante utilizando la media de la variable. Es un mƩtodo simple pero no siempre recomendado, ya que puede subestimar la variabilidad de los datos.
cart (Classification and Regression Trees): Usa Ôrboles de decisión para imputar valores faltantes. Este método puede ser útil para datos complejos con relaciones no lineales.
El método de imputación method='pmm' (Predictive
Mean Matching) reemplaza los valores faltantes con valores observados
que son similares en términos de predicción, manteniendo la coherencia y
la distribución original de los datos. Este enfoque es especialmente
Ćŗtil para evitar imputaciones irreales y preservar las caracterĆsticas
estadĆsticas del conjunto de datos.
Algunos cientĆficos de datos o estadĆsticos pueden buscar una
solución rÔpida sustituyendo los datos que faltan por la media.
La media se utiliza a menudo para imputar datos no
categóricos. Por ejemplo, en el conjunto de datos de calidad
del aire, supongamos que queremos imputar la media de sus valores
faltantes. En este caso, utilizamos el paquete R
mice. Cambiando el argumento method = mean, se
especifica la imputación de la media, el argumento m = 1
cambia las iteraciones a 1, lo que significa no (iteración).
El fundamento teórico de utilizar la media para imputar los datos
faltantes es que la media es una buena estimación para
seleccionar aleatoriamente una observación de una distribución
normal. Ahora, intentaremos imputar la media para las variables
Ozono y Solar.R del conjunto de datos
airquality. En primer lugar, vamos a cargar los paquetes
mice y mipackages (instale el paquete de
CRAN primero usando install.packages("mice") y
install.packages("mi")). Para mÔs información sobre el
paquete utilizado. Podemos recuperar el conjunto de datos completo
utilizando la función complete().
suppressWarnings({
library(mice)
library(foreign)
})
mice().imp <- mice(airquality, m=5, maxit=50, method ='pmm', seed=500, printFlag = FALSE)
El parÔmetro m se refiere al número de conjuntos
de datos imputados que se generarƔn. Es decir, m
representa la cantidad de veces que se imputarƔn los datos para crear
mĆŗltiples conjuntos de datos imputados. Cada conjunto de datos
imputados contendrĆ” valores imputados diferentes para los datos
faltantes, lo que permite capturar la incertidumbre en el proceso de
imputación o falta de certeza sobre los valores reales de los
datos faltantes.
El parƔmetro maxit en el paquete mice de
R controla cuƔntas veces se repetirƔ el proceso de
imputación en cada conjunto de datos para alcanzar una convergencia o un
lĆmite mĆ”ximo de iteraciones. Durante cada iteración, el
algoritmo intenta mejorar la imputación de los valores faltantes. Si el
nĆŗmero de iteraciones alcanza el valor especificado en
maxit y el proceso no converge, la imputación se detendrÔ y
se generarĆ” un mensaje de advertencia. Ajustar adecuadamente el
valor de maxit puede ayudar a controlar el tiempo de
ejecución y garantizar una convergencia adecuada del algoritmo de
imputación.
El valor por defecto es m=5. method='pmm'se
refiere al método de imputación. En este caso, utilizamos el
método de imputación por emparejamiento predictivo medio
(PMM). Se pueden utilizar otros métodos de imputación, escriba
methods(mice) para obtener una lista de los mƩtodos de
imputación disponibles.
suppressWarnings(methods(mice))
## [1] mice.impute.2l.bin mice.impute.2l.lmer
## [3] mice.impute.2l.norm mice.impute.2l.pan
## [5] mice.impute.2lonly.mean mice.impute.2lonly.norm
## [7] mice.impute.2lonly.pmm mice.impute.cart
## [9] mice.impute.jomoImpute mice.impute.lasso.logreg
## [11] mice.impute.lasso.norm mice.impute.lasso.select.logreg
## [13] mice.impute.lasso.select.norm mice.impute.lda
## [15] mice.impute.logreg mice.impute.logreg.boot
## [17] mice.impute.mean mice.impute.midastouch
## [19] mice.impute.mnar.logreg mice.impute.mnar.norm
## [21] mice.impute.mpmm mice.impute.norm
## [23] mice.impute.norm.boot mice.impute.norm.nob
## [25] mice.impute.norm.predict mice.impute.panImpute
## [27] mice.impute.passive mice.impute.pmm
## [29] mice.impute.polr mice.impute.polyreg
## [31] mice.impute.quadratic mice.impute.rf
## [33] mice.impute.ri mice.impute.sample
## [35] mice.mids mice.theme
## see '?methods' for accessing help and source code
imp_df <- complete(imp)
head(imp_df)
La siguiente imagen ilustra el funcionamiento del método de imputación múltiple por ecuaciones encadenadas.
set.seed(123)
data <- data.frame(
A = c(0.93, 0.24, 0.95, 0.23, 0.90, 0.15, 0.47, 0.89),
B = c(1.40, 0.46, 1.24, 0.57, 1.28, 0.42, 0.54, 1.23),
C = c(1.53, 0.76, 1.46, 1.28, 1.28, 1.53, 0.63, 1.45)
)
# Step 2: Introduce missing data in A
data_missing <- data
data_missing[c(2, 5, 7), "A"] <- NA
# Step 3: Original correlation
plot1 <- ggplot(data, aes(x = B, y = A)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE, color = "blue") +
ggtitle("Original \nR^2 = 0.9345") +
theme_minimal() +
theme(plot.title = element_text(size = 10, hjust = 0.5),
plot.margin = margin(20, 20, 20, 20))
# Step 4: Impute random data
data_random <- data_missing
data_random[is.na(data_random$A), "A"] <- runif(sum(is.na(data_missing$A)), min = min(data$A), max = max(data$A))
plot2 <- ggplot(data_random, aes(x = B, y = A)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE, color = "blue") +
ggtitle("Random Imput\nR^2 = 0.4106") +
theme_minimal() +
theme(plot.title = element_text(size = 10, hjust = 0.5),
plot.margin = margin(20, 20, 20, 20))
# Step 5: Impute Random Forest
data_rf <- suppressWarnings(missForest(data_missing)$ximp)
plot3 <- ggplot(data_rf, aes(x = B, y = A)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE, color = "blue") +
ggtitle("Rand. F Imput\nR^2 = 0.5311") +
theme_minimal() +
theme(plot.title = element_text(size = 10, hjust = 0.5),
plot.margin = margin(20, 20, 20, 20))
# Step 6: Impute B using Random Forest and re-calculate correlation
data_rf2 <- suppressWarnings(missForest(data_rf)$ximp)
plot4 <- ggplot(data_rf2, aes(x = B, y = A)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE, color = "blue") +
ggtitle("Ran. F (A & B Imputed)\nR^2 = 0.8771") +
theme_minimal() +
theme(plot.title = element_text(size = 10, hjust = 0.5),
plot.margin = margin(20, 20, 20, 20))
# Arrange plots in a grid with increased spacing
grid.arrange(plot1, plot2, plot3, plot4, ncol = 4, widths = c(0.5, 0.5, 0.5, 0.5))
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
## `geom_smooth()` using formula = 'y ~ x'
AdemƔs, la siguiente figura ilustra el funcionamiento del
método de imputación por emparejamiento predictivo medio usado
en el ejemplo para airquality.
Observación: El método PMM utiliza
aleatoriedad en la imputación para introducir variabilidad en las
estimaciones de los valores faltantes. Esto ayuda a evitar la
sobreestimación o subestimación sistemÔtica de los valores imputados y a
capturar la incertidumbre asociada con la imputación de datos faltantes.
La introducción de aleatoriedad también puede ayudar a mejorar la
robustez y generalización del modelo de imputación, especialmente en
situaciones donde los datos faltantes siguen patrones complejos o no
aleatorios.
Tracemos un histograma y un diagrama de
dispersión del aspecto del conjunto de datos de la calidad del
aire tras la imputación mediante el método PMM. Los
siguientes son los histogramas asociados:
ggp1 <- ggplot(data.frame(value=airquality$Ozone), aes(x=value)) +
geom_histogram(fill="#FBD000", color="#E52521", alpha=0.9) +
ggtitle("Original data") +
xlab('Ozone') + ylab('Frequency') +
theme_ipsum() +
theme(plot.title = element_text(size=15))
ggp2 <- ggplot(data.frame(value=imp_df$Ozone), aes(x=value)) +
geom_histogram(fill="#43B047", color="#049CD8", alpha=0.9) +
ggtitle("PMM imputation") +
xlab('Ozone') + ylab('Frequency') +
theme_ipsum() +
theme(plot.title = element_text(size=15))
grid.arrange(ggp1, ggp2, ncol = 2)
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Ozone con todas las demƔs variables.library(lattice)
xyplot(imp, Ozone ~ Wind + Temp + Solar.R, pch = 18, cex = 1)
densityplot(imp)
mice, para mostrar
grÔficos de seguimiento de la media y desviación estÔndar de todas las
variables implicadas en las imputaciones.plot(imp);
m conjuntos de datos
imputados, a saber m = 5 por defecto. Como se puede ver en
el grƔfico de trazado anterior sobre imp, no hay
tendencias claras y las variables se superponen de una iteración a la
siguiente. Dicho de otro modo, la varianza dentro de
una cadena (hay m cadenas) deberĆa ser
aproximadamente igual a la varianza entre las cadenas. Esto
indica que se ha logrado la convergencia. Si no se
logra la convergencia, se puede aumentar el nĆŗmero de
iteraciones que mice emplea especificando explĆcitamente el
parÔmetro maxit a la función
mice.La función mice entrega distintos métodos de
imputación los cuales puede estudiar y aplicar, de acuerdo a lo que
requieran sus datos. Puede aplicar la imputación por
regresión en R con la configuración del método
method = "norm.predict" en la función mice.
Puede aplicar la imputación de regresión estocÔstica en
R con la función mice utilizando el método
method = "norm.nob".
El paquete mice tambiƩn incluye un procedimiento de
imputación de regresión estocÔstica Bayesiana. Puede
aplicar este procedimiento de imputación con la función
mice y utilizar como mƩtodo method = "norm".
Para estos mƩtodos debe seleccionar primero dos columnas del conjunto de
datos de interƩs para ajustar constantes del modelo.
data <- airquality[, c("Solar.R", "Wind")]
imp.regress <- mice(airquality, method="norm.predict", m=1, maxit=1)
##
## iter imp variable
## 1 1 Ozone Solar.R
imp.regress$imp$Wind
mice, el mƔs importante es el
de lm/glm. Sin embargo, si se recuerda, el modelo lineal
generalizado es extremadamente flexible, y puede utilizarse para
expresar una amplia gama de anÔlisis diferentes. Por extensión,
podrĆamos utilizar la imputación mĆŗltiple no sólo para regresión
lineal, sino para la regresión logĆstica, la regresión
de Poisson, las pruebas t, el ANOVA, ANCOVA, etc. Cada una de
estas temƔticas pueden ser abordadas como proyectos finales en este
curso.Imputación MĆŗltiple (Multiple Imputation - MI): Esta tĆ©cnica es ampliamente utilizada cuando los datos faltantes se consideran como āMissing At Randomā (MAR). MI genera varios conjuntos de datos completos imputando valores plausibles para los datos faltantes. Luego, se analizan cada uno de estos conjuntos y los resultados se combinan para obtener estimaciones finales que tienen en cuenta la incertidumbre asociada a los valores imputados Fuente.
MÔxima Verosimilitud de Información Completa (FIML - Full Information Maximum Likelihood): FIML es un método que permite estimar parÔmetros directamente a partir de un modelo de verosimilitud completa sin necesidad de imputar valores faltantes. Funciona bien bajo las condiciones MAR y es preferido en modelos de ecuaciones estructurales y anÔlisis de regresión Fuente.
Algoritmo de Maximización de la Expectativa (EM - Expectation-Maximization): El algoritmo EM es utilizado para estimar parÔmetros en presencia de datos faltantes asumiendo una estructura de modelo subyacente. Alterna entre estimar las probabilidades de los datos faltantes y maximizar la función de verosimilitud, ofreciendo una solución iterativa a la imputación de datos Fuente.
MĆ©todos de Eliminación: Los mĆ©todos de eliminación, como la eliminación lista (listwise deletion) o eliminación por pares (pairwise deletion), son mĆ”s simples pero pueden introducir sesgos si los datos no son faltantes completamente al azar (MCAR). Estos mĆ©todos eliminan las observaciones con valores faltantes, lo que puede reducir la potencia estadĆstica y sesgar los resultados Fuente.
Precaución: - La imputación puede introducir sesgos significativos. - Se recomienda evaluar si la variable sigue siendo Ćŗtil. - Si la variable es crĆtica, se puede usar tĆ©cnicas de modelado predictivo para estimar los valores faltantes.
Primero verificas la normalidad de la variable imputada (o del conjunto completo, dependiendo de tu anƔlisis).
Esto lo puedes hacer con:
R
es comenzar con algunos estadĆsticos descriptivos, y en
particular con el mĆnimo y el mĆ”ximo. En
R, esto se puede hacer fÔcilmente con la función
summary(). El conjunto de datos a utilizar es
mpg asociado a consumo de combustible de 1999 a 2008 de 38
modelos populares de coches (ver ggplot2::mpg).dat <- ggplot2::mpg
head(dat)
summary(dat$hwy)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 12.00 18.00 24.00 23.44 27.00 44.00
min(dat$hwy)
## [1] 12
R (con el nĆŗmero de bins correspondiente a la
raĆz cuadrada del nĆŗmero de observaciones para tener mĆ”s
bins que la opción por defecto), o usando
ggplot2. Considere la columna
hwy: highway mileage (miles per gallon).hist(dat$hwy,
xlab = "hwy",
main = "Histograma de hwy",
breaks = sqrt(nrow(dat)))
ggplot(dat) +
aes(x = hwy) +
geom_histogram(bins = 30L, fill = "#0c4c8a") +
theme_minimal()
hwy.boxplot(dat$hwy,
ylab = "hwy"
)
ggplot(dat) +
aes(x = "", y = hwy) +
geom_boxplot(fill = "#0c4c8a") +
theme_minimal()
Un diagrama de caja ayuda a visualizar una variable cuantitativa
mostrando cinco resĆŗmenes de localización comunes (mĆnimo, mediana,
primer y tercer cuartil y mÔximo) y cualquier observación que se haya
clasificado como presunto valor atĆpico utilizando el criterio
del rango intercuartĆlico (IQR).
Las observaciones consideradas como posibles valores atĆpicos
segĆŗn el criterio IQR se muestran como
puntos en el diagrama de caja. SegĆŗn este criterio, hay 2
valores atĆpicos potenciales (vĆ©anse los 2 puntos por encima de
la lĆnea vertical, en la parte superior del
boxplot).
Recuerde que no por el hecho de que una observación sea
considerada como un valor atĆpico potencial por el criterio
IQR debe eliminarla. Eliminar o mantener un valor
atĆpico depende de: (i) el contexto de su anĆ”lisis, (ii) si las pruebas
que va a realizar en el conjunto de datos son robustas a los valores
atĆpicos o no, y (iii) a quĆ© distancia estĆ” el valor atĆpico de otras
observaciones.
TambiƩn es posible extraer los valores de los posibles
valores atĆpicos basĆ”ndose en el criterio IQR
gracias a la función boxplot.stats()$out:
boxplot.stats(dat$hwy)$out
## [1] 44 44 41
out <- boxplot.stats(dat$hwy)$out
out_ind <- which(dat$hwy %in% c(out))
out_ind
## [1] 213 222 223
dat[out_ind, ]
boxplot(dat$hwy,
ylab = "hwy",
main = "Boxplot of highway miles per gallon"
)
mtext(paste("Outliers: ", paste(out, collapse = ", ")))
\[ I_{out} = [q_{0.025} ; q_{0.975}]^C \]
quantile():lower_bound <- quantile(dat$hwy, 0.025)
lower_bound
## 2.5%
## 14
upper_bound <- quantile(dat$hwy, 0.975)
upper_bound
## 97.5%
## 35.175
which():outlier_ind <- which(dat$hwy < lower_bound | dat$hwy > upper_bound)
outlier_ind
## [1] 55 60 66 70 106 107 127 197 213 222 223
dat[outlier_ind, "hwy"]
dat[outlier_ind, ]
lower_bound <- quantile(dat$hwy, 0.01)
upper_bound <- quantile(dat$hwy, 0.99)
outlier_ind <- which(dat$hwy < lower_bound | dat$hwy > upper_bound)
dat[outlier_ind, ]
\[ I = \left[\bar{X} - 3 \cdot MAD, \, \bar{X} + 3 \cdot MAD\right], \]
donde MAD es la desviación absoluta de la mediana y se define como la mediana de las desviaciones absolutas de la mediana \(\bar{X}\) de los datos:
\[ MAD = k \times \text{median}\left(|X_i - \bar{X}|\right) \]
median() y mad(). En
este caso, k representa el factor de escala, por
defecto, R lo considera como 1.lower_bound <- median(dat$hwy) - 3 * mad(dat$hwy, constant = 1)
lower_bound
## [1] 9
upper_bound <- median(dat$hwy) + 3 * mad(dat$hwy, constant = 1)
upper_bound
## [1] 39
which(). SegĆŗn el filtro de Hampel, hay 3
valores atĆpicos para la variable hwy.outlier_ind <- which(dat$hwy < lower_bound | dat$hwy > upper_bound)
outlier_ind
## [1] 213 222 223
dat[outlier_ind, ]
Ventajas: - Robustez: Es menos sensible a los outliers que la desviación estĆ”ndar, ya que la mediana y la MAD son medidas robustas, lo que significa que no se ven muy afectadas por valores atĆpicos. - Aplicabilidad: Funciona bien en distribuciones no normales y en presencia de otros outliers.
Desventajas: - No paramĆ©trico: Aunque es robusto, puede no ser tan eficaz si los datos tienen una distribución bien definida (por ejemplo, normal) donde las tĆ©cnicas paramĆ©tricas podrĆan ser mĆ”s precisas. - Configuración: Requiere ajustar adecuadamente el umbral (generalmente 3 o 4 veces la MAD), lo cual puede ser arbitrario y depender del contexto.
Ventajas: - Simplicidad: Es fÔcil de entender y aplicar, ya que simplemente se basa en la posición de los valores en la distribución. - Flexibilidad: Permite detectar outliers de manera flexible al elegir diferentes percentiles según la necesidad (por ejemplo, percentil 1 o 99 para una detección mÔs estricta).
Desventajas: - No considera la variabilidad: Este mĆ©todo no toma en cuenta la dispersión de los datos de la misma manera que el filtro de Hampel. Esto significa que si los datos tienen una distribución muy dispersa, los percentiles podrĆan no ser tan efectivos para capturar outliers verdaderos. - Dependencia de la distribución: Puede ser menos robusto en distribuciones asimĆ©tricas o con colas largas, donde los percentiles pueden no reflejar adecuadamente los valores extremos.
La prueba de Grubbs permite detectar si el valor mĆ”s alto o mĆ”s bajo de un conjunto de datos es un valor atĆpico (ver Grubbs). La prueba de Grubbs detecta un valor atĆpico cada vez (valor mĆ”s alto o mĆ”s bajo), por lo que las hipótesis nula y alternativa son las siguientes:
Como para cualquier prueba estadĆstica, si el valor p es inferior al umbral de significación elegido (generalmente \(\alpha = 0.05\)), se rechaza la hipótesis nula y se concluye que el valor mĆ”s bajo/mĆ”s alto es un valor atĆpico. Por el contrario, si el valor p es mayor o igual que el nivel de significación, no se rechaza la hipótesis nula y concluiremos que, basĆ”ndonos en los datos, no rechazamos la hipótesis de que el valor mĆ”s bajo/mĆ”s alto no es un valor atĆpico. Tenga en cuenta que la prueba de Grubbs no es apropiada para un tamaƱo de muestra n \(\leq\) 6.
Para realizar la prueba de Grubbs en
R, utilizamos la función grubbs.test() del
paquete outliers. La función recibe los siguientes
argumentos:
grubbs.test(x, type = 10, opposite = FALSE, two.sided = FALSE).
En este caso, type:
library(outliers)
test <- grubbs.test(dat$hwy)
test
##
## Grubbs test for one outlier
##
## data: dat$hwy
## G = 3.45274, U = 0.94862, p-value = 0.05555
## alternative hypothesis: highest value 44 is an outlier
opposite = TRUE en la función
grubbs.test():test <- grubbs.test(dat$hwy, opposite = TRUE)
test
##
## Grubbs test for one outlier
##
## data: dat$hwy
## G = 1.92122, U = 0.98409, p-value = 1
## alternative hypothesis: lowest value 12 is an outlier
R indica que la prueba se realiza ahora
sobre el valor mÔs bajo (véase la hipótesis alternativa: el valor mÔs
bajo 12 es un valor atĆpico). El valor \(p\) es 1. Al nivel de significación del 5%,
no rechazamos la hipótesis de que el valor mÔs bajo 12 no es un
valor atĆpico.Grubbs
en este nuevo conjunto de datos. Reemplacemos el 34th por un
valor de 212.dat[34, "hwy"] <- 212
test <- grubbs.test(dat$hwy)
test
##
## Grubbs test for one outlier
##
## data: dat$hwy
## G = 13.72240, U = 0.18836, p-value < 2.2e-16
## alternative hypothesis: highest value 212 is an outlier
Al igual que la prueba de Grubbs, la prueba de
Dixon se utiliza para comprobar si un Ćŗnico valor
bajo o alto es un valor atĆpico. Por lo tanto, si se sospecha
que hay mĆ”s de un valor atĆpico, la prueba tiene que realizarse en estos
valores atĆpicos sospechosos individualmente. Tenga en cuenta que la
prueba de Dixon es mÔs útil para muestras de pequeño tamaño
(normalmente \(n \leq 25\)).
Para realizar la prueba de Dixon en R,
utilizamos la función dixon.test() del paquete
outliers. Sin embargo, restringimos nuestro conjunto de
datos a las 20 primeras observaciones, ya que la prueba de
Dixon sólo se puede realizar en muestras de pequeño tamaño
(R arrojarÔ un error y sólo acepta conjuntos de datos de 3
a 30 observaciones).
subdat <- dat[1:20, ]
test <- dixon.test(subdat$hwy)
test
##
## Dixon test for outliers
##
## data: subdat$hwy
## Q = 0.57143, p-value = 0.006508
## alternative hypothesis: lowest value 15 is an outlier
Para comprobar el valor mƔs alto, basta con aƱadir el argumento
opuesto = TRUE a la función dixon.test().
test <- dixon.test(subdat$hwy, opposite = TRUE)
test
##
## Dixon test for outliers
##
## data: subdat$hwy
## Q = 0.25, p-value = 0.8582
## alternative hypothesis: highest value 31 is an outlier
out <- boxplot.stats(subdat$hwy)$out
boxplot(subdat$hwy, ylab = "hwy")
mtext(paste("Outliers: ", paste(out, collapse = ", ")))
boxplot, vemos que tambiĆ©n podrĆamos
aplicar la prueba de Dixon sobre el valor 20 ademƔs del
valor 15 realizado anteriormente. Esto puede hacerse encontrando el
nĆŗmero de fila del valor mĆnimo, excluyendo este nĆŗmero de fila del
conjunto de datos y aplicando finalmente la prueba de Dixon
a este nuevo conjunto de datos.remove_ind <- which.min(subdat$hwy)
subsubdat <- subdat[-remove_ind, ]
tail(subsubdat)
test <- dixon.test(subsubdat$hwy)
test
##
## Dixon test for outliers
##
## data: subsubdat$hwy
## Q = 0.44444, p-value = 0.1297
## alternative hypothesis: lowest value 20 is an outlier
Los resultados muestran que el segundo valor mĆ”s bajo, 20, no es un valor atĆpico (valor p= 0.13).
Rosner para detectar valores atĆpicos
tiene las siguientes ventajas. Se utiliza para detectar varios
valores atĆpicos a la vez (a diferencia de la prueba de
Grubbs y Dixon, que debe realizarse de forma
iterativa para detectar mĆŗltiples valores atĆpicos), y estĆ” diseƱado
para evitar el problema del enmascaramiento, en el que un valor
atĆpico cercano a otro atĆpico puede pasar desapercibido. A
diferencia de la prueba de Dixon, hay que tener en cuenta
que la prueba de Rosner es mƔs apropiada cuando el
tamaƱo de la muestra es grande (\(n
\geq 20\)). Por tanto, volvemos a utilizar el conjunto de datos
inicial dat, que incluye 234 observaciones.Rosner utilizamos la función
rosnerTest() del paquete EnvStats. Esta
función requiere al menos 2 argumentos: los datos y el número de
presuntos valores atĆpicos k (con
k = 3 como nĆŗmero de presuntos valores atĆpicos por
defecto). Para este ejemplo, establecemos que el nĆŗmero
de presuntos valores atĆpicos sea igual a 3, tal y como sugiere
el nĆŗmero de posibles valores atĆpicos esbozado en el
boxplot al principio del artĆculo.library(EnvStats)
##
## Adjuntando el paquete: 'EnvStats'
## The following objects are masked from 'package:stats':
##
## predict, predict.lm
test <- rosnerTest(dat$hwy, k = 3)
Los resultados interesantes se ofrecen en la tabla all.stats
test$all.stats
Rosner, vemos que sólo hay
un valor atĆpico (vĆ©ase la columna de valores atĆpicos), y que es la
observación 34 (véase Obs.Num) con un valor de
212 (vĆ©ase Value).Una vez identificados los valores atĆpicos y habiendo decidido enmendar la situación segĆŗn la naturaleza del problema, puede considerar uno de los siguientes enfoques.
x <- dat$hwy
qnt <- quantile(x, probs=c(.25, .75), na.rm = T) #Quartiles
caps <- quantile(x, probs=c(.05, .95), na.rm = T) #5th,95th Percentiles
H <- 1.5 * IQR(x, na.rm = T)
x[x < (qnt[1] - H)] <- caps[1]
x[x > (qnt[2] + H)] <- caps[2]
head(x)
## [1] 29 29 31 30 26 26
NA) y luego pueden predecirse
considerÔndolos como una variable de respuesta. Ya hemos hablado de cómo
predecir los valores faltantes en la sección anterior.Realice un anÔlisis exploratorio de datos (EDA) utilizando el
siguiente conjunto de datos disponible en este
enlace. El conjunto de datos incluye varias variables mƩdicas
predictoras (independientes) y una variable objetivo (dependiente), que
es el resultado de interƩs. Entre las variables independientes se
encuentran el nĆŗmero de embarazos de la paciente, el Ćndice de masa
corporal (BMI, calculado como \(\frac{\text{peso en kg}}{(\text{altura en
m})^2}\)), el nivel de insulina, la edad, entre otras. En este
ejercicio, sustituya todos los valores ausentes o nulos por
NAN y realice el anƔlisis correspondiente de los datos
faltantes.
df.loc[df["Glucose"] == 0.0, "Glucose"] = np.NAN
df.loc[df["BloodPressure"] == 0.0, "BloodPressure"] = np.NAN
df.loc[df["SkinThickness"] == 0.0, "SkinThickness"] = np.NAN
df.loc[df["Insulin"] == 0.0, "Insulin"] = np.NAN
df.loc[df["BMI"] == 0.0, "BMI"] = np.NAN
Aplique imputación de datos usando las siguientes opciones para
method:
method="pmm"method="norm.predict"method="norm.nob"method="norm"Identifique datos atĆpicos para cada variable en el dataset usando las tĆ©cnicas estudiadas en clase. AdemĆ”s, realice imputación de los datos atĆpicos con base en lo desarrollado en el Ćtem anterior.
Realice un anƔlisis exploratorio de datos (EDA) utilizando el
siguiente conjunto de datos disponible en este
enlace.
El conjunto de datos contiene información sobre pacientes y gastos
mĆ©dicos anuales, con variables como la edad, el sexo, el Ćndice de masa
corporal (BMI, calculado como \( \frac{\text{peso en kg}}{(\text{altura
en m})^2} \)), número de hijos, hÔbito de fumar, región y costo anual
del seguro mƩdico.
NA) en las siguientes
condiciones:
bmi > 40charges == 0 (si existen)mice u
otros mƩtodos:
method = "pmm"method = "norm.predict"method = "norm.nob"method = "norm"
El anĆ”lisis exploratorio de datos (Ver video) (EDA por sus siglas en inglĆ©s) implica el uso de grĆ”ficos y visualizaciones para explorar y analizar un conjunto de datos. El objetivo es explorar, investigar y aprender, no confirmar hipótesis estadĆsticas.
Revisión de los tipos de datos y conversión si es necesario.
Revisión de valores faltantes.
Resumen estadĆstico inicial.
Detección de outliers.
El anƔlisis exploratorio de datos es una potente herramienta para explorar un conjunto de datos. Incluso cuando su objetivo es efectuar anƔlisis planificados, el EDA puede utilizarse para limpiar datos, para anƔlisis de subgrupos o simplemente para comprender mejor los datos. Un paso inicial importante en cualquier anƔlisis de datos es representar los datos grƔficamente.
No grĆ”fico: Calcula estadĆsticas descriptivas de las variables
GrĆ”fico: Calcula estadĆsticas de forma grĆ”fica
Univariado: Analiza una sola variable a la vez
Multivariado: Analiza dos o mƔs variables
A su vez, cada uno de esas dividisiones puede subdividirse según los tipos de datos con los que trabajemos: categóricos o numéricos.
tabla <- data.frame(
"Naturaleza de la variable" = c("Cualitativa", "", "Cuantitativa", ""),
"Escala de Medidas" = c("Nominal", "Ordinal", "Intervalo", "Razon"),
"Frecuencias" = c("Si", "Si", "Agrupadas", ""),
"Medidas de Localizacion" = c("Moda", "Moda", "Media, Mediana y Moda", ""),
"Medidas de Dispersion" = c("No", "No", "Si", "Si"),
"Medidas de Distribucion" = c("No", "No", "Si", "Si"),
"Graficos" = c("Sectores, Barras", "Sectores, Barras (sin orden)", "Histograma, Tallo y hojas, Cajas y Bigotes, Dispersion.", "")
)
# Create the table with kableExtra
library(knitr)
library(kableExtra)
tabla %>%
kable("html", align = "c", col.names = c(
"Naturaleza de la variable",
"Escala de Medidas",
"Frecuencias",
"Medidas de Localizacion",
"Medidas de Dispersion",
"Medidas de Distribucion",
"Graficos"
)) %>%
kable_styling(full_width = F, position = "center", bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
row_spec(0, bold = TRUE, background = "#D9E2F1", color = "black") %>%
row_spec(1:2, background = "white", color = "black") %>%
row_spec(3:4, background = "#E7E7E7", color = "black")
| Naturaleza de la variable | Escala de Medidas | Frecuencias | Medidas de Localizacion | Medidas de Dispersion | Medidas de Distribucion | Graficos |
|---|---|---|---|---|---|---|
| Cualitativa | Nominal | Si | Moda | No | No | Sectores, Barras |
| Ordinal | Si | Moda | No | No | Sectores, Barras (sin orden) | |
| Cuantitativa | Intervalo | Agrupadas | Media, Mediana y Moda | Si | Si | Histograma, Tallo y hojas, Cajas y Bigotes, Dispersion. |
| Razon | Si | Si |
tabla <- data.frame(
"Tipo de Tabla" = c(
"De Frecuencia (Variable Cualitativa)",
"De Frecuencia (Variable Cuantitativa)",
"De Asociacion (Dos Variables Cualitativas)",
"De Asociacion (Una Variable Cualitativa y una Cuantitativa Discreta)",
"De Asociacion (Una Variable Cualitativa y una Cuantitativa Continua)",
"De Asociacion (Dos Variables Cuantitativas)"
),
"Tipo de Grafico" = c(
"- Barras simples\n- Pastel",
"- Histograma",
"- Barras compuestas\n- Barras superpuestas",
"- Barras:\n * Compuestas\n * Superpuestas",
"- Poligono de Frecuencia\n- Box plot (diagrama de cajas y bigotes)",
"- Diagrama de Puntos"
),
stringsAsFactors = FALSE
)
# Creating the table using kableExtra
tabla %>%
kable("html", escape = FALSE, align = "l", col.names = c("Tipo de Tabla", "Tipo de Grafico")) %>%
kable_styling(full_width = F, position = "center", bootstrap_options = c("striped", "hover", "condensed", "responsive")) %>%
row_spec(0, bold = TRUE, background = "#D9E2F1", color = "black") %>%
row_spec(c(3, 4), background = "#F2F2F2", color = "black") %>%
row_spec(c(2, 5), background = "white", color = "black") %>%
row_spec(c(1, 6), background = "#E7E7E7", color = "black") %>%
column_spec(1, width = "4cm") %>%
column_spec(2, width = "6cm")
| Tipo de Tabla | Tipo de Grafico |
|---|---|
| De Frecuencia (Variable Cualitativa) |
|
| De Frecuencia (Variable Cuantitativa) |
|
| De Asociacion (Dos Variables Cualitativas) |
|
| De Asociacion (Una Variable Cualitativa y una Cuantitativa Discreta) |
|
| De Asociacion (Una Variable Cualitativa y una Cuantitativa Continua) |
|
| De Asociacion (Dos Variables Cuantitativas) |
|
Exploración de la Intersección entre la PsicologĆa y la Ciencia de Datos: Comportamiento Humano en Entornos Digitales
En un centro de investigación psicológica enfocado en el comportamiento humano en entornos digitales como redes sociales y plataformas de juegos en lĆnea. Recopilamos datos que incluyen variables demogrĆ”ficas, patrones de uso de redes sociales, datos de juegos en lĆnea y mediciones psicológicas. Utilizamos herramientas de ciencia de datos y anĆ”lisis estadĆstico para identificar patrones significativos que ayuden a comprender cómo diferentes factores influyen en el comportamiento en lĆnea y el bienestar psicológico. Este enfoque integrado entre la psicologĆa y la ciencia de datos nos permite desarrollar intervenciones efectivas para mejorar la calidad de vida en lĆnea y promover la salud mental de los usuarios.
A continuación se construirÔ la primera base de datos a partir de las variables. Para esto, como se observa en los siguientes comandos, se parte por la construcción de 11 variables de 20 casos cada una:
#Creación de las variables: todas tienen la misma cantidad de casos.
Paciente <- c("Mario", "Luis", "Pedro", "Maria", "Sandra", "Erika", "Laura","Luz","Olga")
Edad <- c(18, 20, 20, 17, 19, 22, 22, 22,31)
Sexo <- c("Masculino", "Femenino", "Masculino", "Femenino", "Masculino", "Femenino", "Masculino", "Femenino","Femenino")
Educacion <- c("Universidad", "Secundaria", "Universidad", "Posgrado", "Universidad", "Universidad", "Universidad", "Posgrado","Posgrado")
Ocupacion <- c("Estudiante", "Profesional", "Estudiante", "Profesional", "Estudiante", "Profesional", "Estudiante", "Profesional","Posgrado")
Red_Social_Principal <- c("Instagram", "Facebook", "Instagram", "Twitter", "TikTok", "Instagram", "Facebook", "Instagram","TikTok")
Tiempo_en_Redes_Sociales <- c(2.5, 3.0, 2.0, 2.5, 3.5, 2.2, 2.8, 3.0,2.0)
Horas_Semanales_de_Juego <- c(15, 20, 12, 10, 18, 15, 20, 15,41)
Autoestima <- c(8.2, 6.9, 7.8, 7.0, 8.5, 7.3, 8.0, 7.6,9)
Ansiedad_Social <- c(42, 50, 38, 45, 35, 48, 40, 42,45)
Satisfaccion_con_la_Vida <- c(7.5, 6.9, 8.0, 7.2, 7.8, 6.5, 7.0, 7.3,8)
Estres<- c(2,2,1,3,4,2,1,4,4)
A partir de las variables ya creadas se puede construir una base de datos.
df=data.frame(Paciente, Edad,Sexo,Edad,Educacion, Ocupacion, Red_Social_Principal,Horas_Semanales_de_Juego,Ansiedad_Social,Satisfaccion_con_la_Vida,Estres)
df
La representación de datos se refiere al proceso de presentar la información de manera visual o tabular para facilitar su comprensión, anĆ”lisis y comunicación. Esta representación puede tomar diversas formas, incluyendo grĆ”ficos, tablas, diagramas, mapas y resĆŗmenes estadĆsticos. El objetivo principal de la representación de datos es convertir datos crudos en información comprensible y significativa.
Aquà hay una descripción de algunas formas comunes de representación de datos:
GrĆ”ficos: Los grĆ”ficos son representaciones visuales de datos que utilizan diferentes tipos de elementos visuales, como lĆneas, barras, puntos y Ć”reas, para mostrar la relación entre variables o la distribución de datos. Algunos tipos comunes de grĆ”ficos incluyen grĆ”ficos de barras, grĆ”ficos de lĆneas, grĆ”ficos circulares, histogramas y diagramas de dispersión.
Tablas: Las tablas son representaciones tabulares de datos que organizan la información en filas y columnas. Las tablas son Ćŗtiles para mostrar datos detallados o para comparar valores entre diferentes categorĆas o grupos. Pueden incluir valores numĆ©ricos, texto descriptivo y otras caracterĆsticas.
Para visualizar los datos en formato dataframe puede usar el comando View() o tambiƩn head() para visualizar las primeras filas en consola.
head(df)
ggplot2 es un sistema para crear grÔficos de forma declarativa, basado en la GramÔtica de los GrÔficos. Se deben proporcionar los datos, indicar a ggplot2 cómo asignar las variables a la estética y qué tipo de grÔficas utilizar. La función geom_bar() se utiliza para producir grÔficos de Ôrea 1d: grÔficos de barras para x categóricas, e histogramas para y continuas
library(ggplot2)
ggplot(data=df, aes(x=Paciente, y=Edad)) + geom_bar(stat="identity")+labs(title = "Distribución de Edad por Paciente")
El diagrama puede ser dibujado en forma horizontal usando la función coord_flip()
ggplot(data=df, aes(x=Paciente, y=Edad)) + geom_bar(stat="identity")+labs(title = "Distribución de Edad por Paciente")+ coord_flip()
Podemos cambiar el ancho, asà como también el color de las barras y bordes. Nótese que se puede hacer una copia de la grÔfica en una variable, en este ejemplo en p para que luego pueda ser usada para presentar el grafico o realizar mÔs transformaciones
ggplot(data=df, aes(x=Paciente, y=Edad)) + geom_bar(stat="identity",width=0.5)+labs(title = "Distribución de Edad por Paciente")
ggplot(data=df, aes(x=Paciente, y=Edad)) + geom_bar(stat="identity",width=0.5,color="blue", fill="green3")+labs(title = "Distribución de Edad por Paciente")
ggplot(data=df, aes(x=Paciente, y=Edad)) + geom_bar(stat="identity",width=0.8, fill="steelblue")+labs(title = "Distribución de Edad por Paciente")
#creando tabla de resumen
Tabla_1 <- df %>%
dplyr::group_by(Red_Social_Principal) %>%
dplyr::summarise(Total = n()) %>%
dplyr::mutate(Porcentaje = round(Total/sum(Total)*100, 1)) %>%
dplyr::arrange(Red_Social_Principal)
"Grafico"
## [1] "Grafico"
G1<-ggplot(Tabla_1, aes(x =Red_Social_Principal, y=Total) ) +
geom_bar(width = 0.7,stat="identity",
position = position_dodge(), fill="cyan4") +
ylim(c(0,5))+
#xlim(c(0,300)) +
#ggtitle("Un tĆtulo") +
labs(x="Red Social", y= "Frecuencias \n (Porcentajes)") +
geom_text(aes(label=paste0(Total," ", "", "(", Porcentaje, "%", ")")),
vjust=-0.9,
color="black",
hjust=0.5,
# define text position and size
position = position_dodge(0.9),
angle=0,
size=4.5) +
theme(axis.text.x = element_text(angle = 0, vjust = 1, hjust=1)) +
theme_bw(base_size = 16) +
#coord_flip() +
facet_wrap(~"Distribución de Tipo de Red Social")
G1
Un grƔfico de barras agrupado muestra un valor numƩrico para un conjunto de entidades divididas en grupos y subgrupos.
El conjunto de datos para el presente ejemplo proporciona 3 columnas: el valor numĆ©rico (value), y 2 variables categóricas. En el llamada aes(), x es (categ), y el subgrupo (categ) se da al argumento fill. En la función geom_bar(), debe especificarse position=ādodgeā para que las barras estĆ©n una al lado de la otra.
head(df)
ggplot(df, aes(fill = Sexo, y = Edad, x = Red_Social_Principal, label = Edad)) +
geom_bar(position = "dodge", stat = "identity") +
labs(title = "Distribución de la Red Social según la Edad y Sexo",
x = "Red Social Principal",
y = "Edad",
fill = "Sexo")
ggplot(df, aes(fill = Sexo, x = Red_Social_Principal)) +
geom_bar(position = "stack") +
geom_text(stat = 'count', aes(label = ..count..), position = position_stack(vjust = 0.5), size = 3, color = "black") +
labs(title = "Distribución de la Red Social según el Sexo",
x = "Red Social Principal",
y = "Frecuencia") +
scale_fill_manual(values = c("blue2", "pink2"), name = "Sexo", labels = c("Hombre", "Mujer")) +
theme(legend.position = "right") # Ubicación de la leyenda
Los histogramas son útiles para representar la distribución de variables continuas como Edad, Tiempo en Redes Sociales y Horas Semanales de Juego. Cada barra del histograma muestra la frecuencia de los datos..
ggplot(data = df, aes(x = Edad)) +
geom_histogram(binwidth = 1, fill = "skyblue", color = "black", alpha = 0.8) +
labs(title = "Histograma de Edades",
x = "Edad",
y = "Frecuencia")
ggplot2 no ofrece ningĆŗn geom especĆfica para construir diagramas circulares (piecharts). El truco es el siguiente: El marco de datos de entrada tiene 2 columnas: los nombres de los grupos (group here) y su valor (value here), se construye un grĆ”fico de barras apilado con una sola barra utilizando la función geom_bar(), luego se hace circular con coord_polar()
library(magrittr)
library(dplyr)
#Tabla resumen
Tabla_2 <- df %>%
group_by(Sexo) %>% # Variable a ser transformada
count() %>%
ungroup() %>%
mutate(Porcentaje = `n` / sum(`n`)) %>%
arrange(Porcentaje) %>%
mutate(etiquetas = scales::percent(Porcentaje))
#Grafico #2
require(scales)
## Cargando paquete requerido: scales
##
## Adjuntando el paquete: 'scales'
## The following object is masked from 'package:purrr':
##
## discard
## The following object is masked from 'package:readr':
##
## col_factor
ggplot(Tabla_2, aes(x = "", y = Porcentaje, fill = Sexo)) +
geom_col(color = "black") +
geom_label(aes(label = etiquetas),
position = position_stack(vjust = 0.5),
show.legend = FALSE) +
guides(fill = guide_legend(title = "Distribución de Pacientes según el Sexo")) + scale_color_gradient() +
coord_polar(theta = "y") + ggtitle ("")
#theme_void()
La libreria questionr de R contiene la función freq la cual genera y formatea tablas de frecuencia simples a partir de una variable o una tabla, con porcentajes y opciones de formato. El resultado es un objeto de la clase data.frame.
library(questionr)
Tabla_Sexo <- questionr::freq(Sexo, cum = TRUE, sort = "dec", total = TRUE)
knitr::kable(Tabla_Sexo)
| n | % | val% | %cum | val%cum | |
|---|---|---|---|---|---|
| Femenino | 5 | 55.6 | 55.6 | 55.6 | 55.6 |
| Masculino | 4 | 44.4 | 44.4 | 100.0 | 100.0 |
| Total | 9 | 100.0 | 100.0 | 100.0 | 100.0 |
La tabla puede ordenarse opcionalmente en frecuencia descendente, y funciona bien con kable. Si deseamos ver la estructura de la tabla generada por freq() utilizamos la función str()
str(Tabla_Sexo)
## Classes 'freqtab' and 'data.frame': 3 obs. of 5 variables:
## $ n : num 5 4 9
## $ % : num 55.6 44.4 100
## $ val% : num 55.6 44.4 100
## $ %cum : num 55.6 100 100
## $ val%cum: num 55.6 100 100
Para realizar una tabla de frecuencias agrupada utilizaremos en este ejemplo la Regla de Sturges, en la que el nĆŗmero de clases es obtenido por medio de: \(c=1+ln(N)/ln(2)\) donde \(N\) representa el nĆŗmero total de datos. Consideremos el Ejemplo 23 de los apuntes, en el que se representan las edades de un conjunto de estudiantes.
Ejemplo: Se tienen las siguientes edades de algunos estudiantes
edades <- c(22, 19, 16, 13, 18, 15, 20, 14, 15, 16,
15, 16, 20, 13, 15, 18, 15, 13, 18, 15)
knitr::kable(head(edades))
| x |
|---|
| 22 |
| 19 |
| 16 |
| 13 |
| 18 |
| 15 |
Encontremos el nĆŗmero de clases usando la regla de Sturges
n_sturges = 1 + log(length(edades))/log(2)
n_sturgesc = ceiling(n_sturges)
n_sturgesf = floor(n_sturges)
n_clases = 0
if (n_sturgesc%%2 == 0) {
n_clases = n_sturgesf
} else {
n_clases = n_sturgesc
}
R = max(edades) - min(edades)
w = ceiling(R/n_clases)
Calculemos ahora nuestra tabla de frecuencias con nĆŗmero de clases n_clases. Primero creamos una lista de fronteras de clases bins y luego agrupamos los datos basados en estas
bins <- seq(min(edades), max(edades) + w, by = w)
bins
## [1] 13 15 17 19 21 23
Edades <- cut(edades, bins)
Freq_table <- transform(table(Edades), Rel_Freq=prop.table(Freq), Cum_Freq=cumsum(Freq))
knitr::kable(Freq_table)
| Edades | Freq | Rel_Freq | Cum_Freq |
|---|---|---|---|
| (13,15] | 7 | 0.4117647 | 7 |
| (15,17] | 3 | 0.1764706 | 10 |
| (17,19] | 4 | 0.2352941 | 14 |
| (19,21] | 2 | 0.1176471 | 16 |
| (21,23] | 1 | 0.0588235 | 17 |
str(Freq_table)
## 'data.frame': 5 obs. of 4 variables:
## $ Edades : Factor w/ 5 levels "(13,15]","(15,17]",..: 1 2 3 4 5
## $ Freq : int 7 3 4 2 1
## $ Rel_Freq: num 0.4118 0.1765 0.2353 0.1176 0.0588
## $ Cum_Freq: int 7 10 14 16 17
Podemos tambiƩn crear un histograma para la tabla de frecuencias agrupada
df2 <- data.frame(x = Freq_table$Edades, y = Freq_table$Freq)
knitr::kable(df2)
| x | y |
|---|---|
| (13,15] | 7 |
| (15,17] | 3 |
| (17,19] | 4 |
| (19,21] | 2 |
| (21,23] | 1 |
ggplot(data=df2, aes(x=x, y=y)) +
geom_bar(stat="identity", color="blue", fill="green") +
xlab("Rango de Edades") +
ylab("Frecuencia")
Una función multiuso muy Ćŗtil en R es summary(X), donde X puede ser uno de cualquier nĆŗmero de objetos, incluyendo conjuntos de datos, variables y modelos lineales, por nombrar algunos. Cuando se utiliza, el comando proporciona datos de resumen relacionados con el objeto individual que se introdujo en Ć©l. AsĆ, la función de resumen tiene diferentes resultados dependiendo del tipo de objeto que tome como argumento. AdemĆ”s de ser ampliamente aplicable, este mĆ©todo es valioso porque a menudo proporciona exactamente lo que se necesita en tĆ©rminos de estadĆsticas de resumen.
Usando la función summary() podemos obtener estadĆsticos de interes y valores de posición:
summary(df$Horas_Semanales_de_Juego)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 10.00 15.00 15.00 18.44 20.00 41.00
Del anterior resultado se puede observar que la hora mĆnima en el juego fue de 10, el 25% se ubicó en 15 horas indicando que dedicaron 15 o menor o igual a 15, al igual que el 50%, en promedio dedicaron 18,4 horas de juego, el 75% dedicó menos o igual que 20 horas y la hora que mĆ”s dedicaron fue de 41.
Por otro lado, se puede notar la función summary() no nos entrega todos los estadĆsticos de interĆ©s, para solucionar esto podemos hacer uso de la librerĆa, pastecs y la función stat.desc(), como se muestra a continuación.
library(pastecs)
##
## Adjuntando el paquete: 'pastecs'
## The following object is masked from 'package:magrittr':
##
## extract
## The following objects are masked from 'package:dplyr':
##
## first, last
## The following object is masked from 'package:tidyr':
##
## extract
stat.desc(df)
Los grĆ”ficos de caja (box plots), tambiĆ©n conocidos como diagramas de cajas y bigotes, son una representación grĆ”fica que permite resumir las caracterĆsticas principales de los datos (posición, dispersión, asimetrĆa, ā¦) e identificar la presencia de valores atĆpicos. En esta sección revisaremos cómo hacer box plots en R base y en ggplot2.
Utilizando boxplot() R base
boxplot(df$Edad, horizontal=TRUE, col='steelblue')
Usando geom_boxplot() de la librerĆa ggplot2
library(tidyverse)
library(hrbrthemes)
library(viridis)
## Cargando paquete requerido: viridisLite
##
## Adjuntando el paquete: 'viridis'
## The following object is masked from 'package:scales':
##
## viridis_pal
df %>%
ggplot(aes(x = "", y = Edad)) +
geom_boxplot(color = "black", fill = "yellow2", alpha = 0.5) +
theme_ipsum() +
theme(legend.position = "none", plot.title = element_text(size = 11)) +
ggtitle("Distribución de las Edades") +
coord_flip()
ggplot(df, aes(x = Sexo, y = Edad, fill = Sexo)) +
geom_boxplot() +
labs(title = "Diagrama de Edades segĆŗn el Sexo",
x = "Sexo", y = "Edades") +
scale_fill_manual(values = c("lightblue", "pink")) +
theme_minimal()
El coeficiente de variación (CV) es una medida estadĆstica que se utiliza para evaluar la variabilidad relativa de una muestra o población en relación con su media. Se calcula como la desviación estĆ”ndar de los datos dividida por la media, y se expresa como un porcentaje multiplicado por 100 para facilitar su interpretación.
El CV es útil cuando se comparan distribuciones de datos con diferentes escalas o unidades, ya que normaliza la variabilidad en relación con la magnitud de los datos. Esto permite realizar comparaciones mÔs significativas entre diferentes conjuntos de datos.
\[CV = \left( \frac{\text{Desviación EstÔndar}}{\text{Media}} \right) \times 100\]
Ahora vamos a hallar el coeficiente de variación de la variable Edad.
media <- mean(df$Edad)
desviacion <- sd(df$Edad)
coef_variacion <- (desviacion / media) * 100
cat("El coeficiente de variación es:", coef_variacion, "%\n")
## El coeficiente de variación es: 19.25285 %
El coeficiente de asimetrĆa de Pearson es que es una medida estandarizada de la asimetrĆa de una distribución de datos. Se calcula como el tercer momento estandarizado de la distribución, es decir, la diferencia promedio al cubo entre los datos y la media, dividida por la desviación estĆ”ndar al cubo. Si el coeficiente de asimetrĆa de Pearson es cero, la distribución es simĆ©trica. Si es positivo, la cola de la distribución estĆ” en el lado derecho, y si es negativo, la cola estĆ” en el lado izquierdo. Esto proporciona información sobre la forma y dirección de la asimetrĆa en la distribución de datos.
\[\text{Coeficiente de AsimetrĆa de Pearson} = \frac{E[(X - \mu)^3]}{\sigma^3}\]
Como el coefiente de asmetrĆa de Pearson es mayo que cero, indica que la edad presenta distribución asimetrica hacia la derecha.
La curtosis es una medida estadĆstica que describe la forma de la distribución de los datos en relación con una distribución normal estĆ”ndar. La curtosis es una medida de la āpicudezā de la distribución, es decir, cuĆ”n puntiaguda o aplanada es en comparación con una distribución normal.
\[\text{Curtosis} = \frac{1}{n} \sum_{i=1}^{n} \left(\frac{x_i - \bar{x}}{s}\right)^4 - 3\]
Platicúrtica: Una distribución platicúrtica es aquella que tiene un exceso de curtosis negativo en comparación con la distribución normal estÔndar (cuyo exceso de curtosis es 0). Esto significa que la distribución tiene colas mÔs ligeras y es mÔs aplanada en comparación con la distribución normal. En una distribución platicúrtica, los valores se concentran mÔs cerca de la media y hay menos valores extremos en comparación con una distribución normal.
MesocĆŗrtica: Una distribución mesocĆŗrtica es aquella que tiene un exceso de curtosis igual a 0, es decir, su forma es similar a la de una distribución normal estĆ”ndar. Esto significa que la distribución tiene una cantidad ānormalā de picos y colas, y su forma se asemeja a una campana simĆ©trica.
Leptocúrtica: Una distribución leptocúrtica es aquella que tiene un exceso de curtosis positivo en comparación con la distribución normal estÔndar. Esto significa que la distribución tiene colas mÔs pesadas y es mÔs puntiaguda en comparación con la distribución normal. En una distribución leptocúrtica, los valores tienden a agruparse mÔs cerca de la media y hay mÔs valores extremos en comparación con una distribución normal.
curtosis <- kurtosis(df$Edad)
cat("La curtosis de la muestra es:", curtosis, "\n")
## La curtosis de la muestra es: 0.8255815
El paquete ggmap permite visualizar datos espaciales
y estadĆsticas espaciales en formato de mapa utilizando el enfoque por
capas de ggplot2. Este paquete tambiƩn incluye mapas base
que dan contexto a sus visualizaciones, incluyendo Google Maps, Open
Street Map, Stamen Maps y CloudMade. AdemƔs, se proporcionan funciones
para acceder a varios servicios de Google, como Geocoding,
Distance Matrix, and Directions.
El paquete ggmap se basa en ggplot2, lo
que significa que adoptarĆ” un enfoque por capas y constarĆ” de los mismos
cinco componentes que se encuentran en ggplot2. Estos
incluyen un conjunto de datos por defecto con mapeos estƩticos donde x
es longitud, y es latitud, y el sistema de coordenadas se fija en
Mercator. Otros componentes incluyen una o mƔs capas
definidas con un objeto geométrico y una transformación para cada mapa
estético, el sistema de coordenadas y la especificación de las facetas.
Debido a que ggmap estĆ” construido sobre
ggplot2 tiene acceso a toda la gama de ggplot2
ya aprendidas.
En esta sección cubriremos los siguientes temas:
shapefileggmap. En
general, sólo hay que descargar el mapa rasterizado (basemap) y luego
trazar los datos operativos en el mapa base. El primer paso se consigue
con la función get_map(), que puede utilizarse para crear
un mapa base desde Google, Stamen, Open Street Map o CloudMade.
AprenderÔs cómo hacerlo en este paso. En un próximo paso aprenderÔs a
aƱadir y estilizar los datos operativos de varias maneras.ggmap yendo al panel de paquetes en
RStudio y haciendo clic en la casilla junto al nombre del paquete.
TambiƩn puede cargarlo desde la consola escribiendo:library(ggmap)
## ā¹ Google's Terms of Service: <https://mapsplatform.google.com>
## Stadia Maps' Terms of Service: <https://stadiamaps.com/terms-of-service/>
## OpenStreetMap's Tile Usage Policy: <https://operations.osmfoundation.org/policies/tiles/>
## ā¹ Please cite ggmap if you use it! Use `citation("ggmap")` for details.
##
## Adjuntando el paquete: 'ggmap'
##
##
## The following object is masked from 'package:magrittr':
##
## inset
myLocation y defĆnela como
California, ubicación que usaremos mÔs adelante:myLocation <- "California"
Llama a la función get_map() y pasa la variable de
localización junto con un nivel de Zoom de 6. NecesitarÔs primero crear
una API key en Google Cloud Platform.
Si descargas la versión de desarrollo de ggmap desde
GitHub, hay una función que guarda tu clave de la
API de Google y la utiliza en las siguientes llamadas a
la API a travƩs de ggmap. Para instalarla desde la consola
escribe:
Utilice las siguientes instrucciones para descargar el mapa.
Cargue primero su API key y luego use la función
get_googlemap para obtener el mapa de interƩs. Para que las
siguientes instrucciones funcionen, debe realizar los siguientes pasos
en Google Cloud Platform:
get_googlemap() ver ggmap.
Por favor, no use la API key
mykey = "AIzaSyABAXSsDR-f9qby3LK2o6bh0BypFw1Krm4" pues estĆ”
serĆ” cambiada por otra, en este caso es usada a manera de ejemplo. Cree
su propia API Google Cloud Platformmykey = "AIzaSyABAXSsDR-f9qby3LK2o6bh0BypFWlKrm4"
register_google(key = mykey)
get_googlemap("waco, texas") %>% ggmap()
## ā¹ <https://maps.googleapis.com/maps/api/staticmap?center=waco,%20texas&zoom=10&size=640x640&scale=2&maptype=terrain&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
## ā¹ <https://maps.googleapis.com/maps/api/geocode/json?address=waco,+texas&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
get_googlemap:get_googlemap("Colombia",
maptype = "hybrid",
scale = 2,
zoom = 5) %>% ggmap()
## ā¹ <https://maps.googleapis.com/maps/api/staticmap?center=Colombia&zoom=5&size=640x640&scale=2&maptype=hybrid&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
## ā¹ <https://maps.googleapis.com/maps/api/geocode/json?address=Colombia&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
get_googlemap("Universidad del Norte",
maptype = "roadmap",
scale = 2,
zoom = 12) %>% ggmap()
## ā¹ <https://maps.googleapis.com/maps/api/staticmap?center=Universidad%20del%20Norte&zoom=12&size=640x640&scale=2&maptype=roadmap&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
## ā¹ <https://maps.googleapis.com/maps/api/geocode/json?address=Universidad+del+Norte&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
get_googlemap("Universidad del Norte",
maptype = "terrain",
scale = 2,
zoom = 16) %>% ggmap()
## ā¹ <https://maps.googleapis.com/maps/api/staticmap?center=Universidad%20del%20Norte&zoom=16&size=640x640&scale=2&maptype=terrain&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
## ā¹ <https://maps.googleapis.com/maps/api/geocode/json?address=Universidad+del+Norte&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
get_googlemap("Universidad del Norte",
maptype = "hybrid",
scale = 2,
zoom = 18) %>% ggmap()
## ā¹ <https://maps.googleapis.com/maps/api/staticmap?center=Universidad%20del%20Norte&zoom=18&size=640x640&scale=2&maptype=hybrid&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
## ā¹ <https://maps.googleapis.com/maps/api/geocode/json?address=Universidad+del+Norte&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
ggmap() devuelve un objeto ggplot, lo que
significa que actĆŗa como capa base en el ggplot2. Esto
permite la gama completa de capacidades de ggplot2, lo que
significa que puede trazar puntos en el mapa, aƱadir contornos,
mapas de calor 2D y mucho mƔs. Examinaremos algunas de estas
capacidades en esta sección.myLocation <- "California"
myMap <- get_map(location = myLocation, zoom = 6)
## ā¹ <https://maps.googleapis.com/maps/api/staticmap?center=California&zoom=6&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
## ā¹ <https://maps.googleapis.com/maps/api/geocode/json?address=California&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
library(readr)
dfWildfires <- read_csv("StudyArea_SmallFile.csv", col_names = TRUE)
## Rows: 7154 Columns: 21
## āā Column specification āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
## Delimiter: ","
## chr (10): ORGANIZATI, FIRENAME, FIRENUMBER, FIRECODE, CAUSE, SIZECLASS, STAR...
## dbl (11): FID, SPECCAUSE, STATCAUSE, FIRETYPE, YEAR_, STATE_FIPS, DLATITUDE,...
##
## ā¹ Use `spec()` to retrieve the full column specification for this data.
## ā¹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
library(dplyr)
df <- select(dfWildfires, STATE, YEAR_, TOTALACRES, DLATITUDE, DLONGITUDE)
knitr::kable(head(df))
| STATE | YEAR_ | TOTALACRES | DLATITUDE | DLONGITUDE |
|---|---|---|---|---|
| Arizona | 1988 | 1500 | 31.58333 | -111.5500 |
| Arizona | 1986 | 10390 | 32.50000 | -111.5167 |
| Montana | 1986 | 1400 | 47.50000 | -111.4333 |
| Arizona | 2002 | 1035 | 31.70000 | -111.4830 |
| Arizona | 2000 | 5700 | 31.51600 | -111.5170 |
| Arizona | 2000 | 2750 | 31.64900 | -111.4910 |
df <- filter(df, TOTALACRES >= 1000 & STATE == "California")
ggmap(myMap) + geom_point(data=df, aes(x = DLONGITUDE, y = DLATITUDE))
dplyr mutate() para
agrupar los incendios por dƩcada:df <- mutate(df,
DECADE = ifelse(YEAR_ %in% 1980:1989, "1980-1989",
ifelse(YEAR_ %in% 1990:1999, "1990-1999",
ifelse(YEAR_ %in% 2000:2009, "2000-2009",
ifelse(YEAR_ %in% 2010:2016, "2010-2016", "-99")))))
ggplotly. Para esto utilizamos la función
ggplot_build para convertir el plot de ggmap a
un objeto ggplot.last_plot <- ggmap(myMap) +
geom_point(data = df, aes(x = DLONGITUDE, y = DLATITUDE, colour = DECADE, size = TOTALACRES))
library(plotly)
##
## Adjuntando el paquete: 'plotly'
## The following object is masked from 'package:ggmap':
##
## wind
## The following object is masked from 'package:igraph':
##
## groups
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
gg <- ggplot_build(last_plot)$plot
plotly_plot <- ggplotly(gg)
plotly_plot
myMap <- get_map(location = "Santa Clarita, California", zoom = 10)
## ā¹ <https://maps.googleapis.com/maps/api/staticmap?center=Santa%20Clarita,%20California&zoom=10&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
## ā¹ <https://maps.googleapis.com/maps/api/geocode/json?address=Santa+Clarita,+California&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
ggmap(myMap) +
geom_point(data = df,
aes(x = DLONGITUDE, y = DLATITUDE, colour = DECADE, size = TOTALACRES))
geom_density2d() se utiliza para
crear los contornos mientras que la función
stat_density2d() crea el mapa de calor. Puedes experimentar
con los colores usando las propiedades
scale_fill_gradient(low, high).fill = ..level.. se utiliza para
rellenar los contornos en el grƔfico basado en los niveles de
densidad de los puntos de datos.myMap <- get_map(location = "California", zoom = 6)
## ā¹ <https://maps.googleapis.com/maps/api/staticmap?center=California&zoom=6&size=640x640&scale=2&maptype=terrain&language=en-EN&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
## ā¹ <https://maps.googleapis.com/maps/api/geocode/json?address=California&key=xxx-f9qby3LK2o6bh0BypFWlKrm4>
ggmap(myMap, extent = "device") +
geom_density2d(data = df, aes(x = DLONGITUDE, y = DLATITUDE), size = 0.3) +
stat_density2d(data = df,
aes(x = DLONGITUDE, y = DLATITUDE, fill = ..level..,
alpha = ..level..),
size = 0.01, bins = 16, geom = "polygon") +
scale_fill_gradient(low = "green", high = "red") +
scale_alpha(range = c(0.3, 0.9), guide = FALSE)
ggmap(myMap, extent = "device") +
stat_density2d(data = df,
aes(x = DLONGITUDE, y = DLATITUDE, fill = ..level..,
alpha = ..level..),
size = 0.01, bins = 16, geom = "polygon") +
scale_fill_gradient(low = "green", high = "red") +
scale_alpha(range = c(0, 0.3), guide = FALSE)
facet_wrap() es una función útil en ggplot2
para crear múltiples mapas mostrados juntos en un diseño de
cuadrĆcula, cada uno representando un subconjunto de los
datos.df <- filter(df, YEAR_ %in% c(2010, 2011, 2012, 2013, 2014, 2015, 2016))
gg <- ggmap(myMap, extent = "device") +
stat_density2d(data = df, aes(x = DLONGITUDE, y = DLATITUDE, fill = ..level.., alpha = ..level..),
size = 0.01, bins = 16, geom = "polygon") +
scale_fill_gradient(low = "green", high = "red") +
scale_alpha(range = c(0, 0.3), guide = FALSE) +
facet_wrap(~YEAR_, ncol = 4) +
theme(plot.title = element_text(size = 20)) +
theme(plot.margin = margin(1, 1, 1, 1, "cm"))
print(gg)
ggmap para crear atractivas visualizaciones de datos en
formato de mapa.violent_crimes.csv de la
carpeta Datos, el cual abarca una gran variedad de delitos,
y realice visualizaciones geogrƔficas para cada tipo de crimen.
Los precios de los combustibles tienen un impacto significativo en la economĆa y el bienestar social, dado que influyen en el costo de transporte, la inflación, y en la competitividad de diversos sectores productivos. La fluctuación de los precios del combustible afecta tanto a los consumidores individuales como a las empresas, generando un efecto dominó en los precios de bienes y servicios. Es importante entender cómo estos precios varĆan a lo largo del tiempo y el espacio, y cómo factores económicos y polĆticos pueden influir en estas variaciones. Este anĆ”lisis permitirĆ” obtener una visión detallada de la estructura de precios de los combustibles en diferentes regiones de Colombia, y cómo estas variaciones pueden estar relacionadas con otras variables macroeconómicas.
Precios_de_Combustibles_MinEnergia.csv que se encuentra en
la carpeta DATOS de GitHub. Incluyendo grƔficos que
presenten los datos sobre un mapa de Colombia, para visualizar la
distribución geogrÔfica de los precios de los combustibles en el
paĆs.library(readr)
p_comb <- read_csv("Precios_de_Combustibles_MinEnergia.csv")
## Rows: 418853 Columns: 12
## āā Column specification āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
## Delimiter: ","
## chr (10): mes, CodigoDepartamento, NombreDepartamento, CodigoMunicipio, muni...
## dbl (2): periodo, precio
##
## ā¹ Use `spec()` to retrieve the full column specification for this data.
## ā¹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
La presente base de datos contiene información acerca de los precios de diversos combustibles durante los aƱos 2017, 2018, 2019 y 2020p. TambiĆ©n contiene información acerca de municipios, departamentos, meses del aƱo, entre otras variables.ā
head(p_comb) #Primeras filas de la base de datos
dim(p_comb) #Dimensiones de los datos: 418.853 registros y 12 variables:
## [1] 418853 12
nombres1<- names(p_comb); nombres1 #Nombres de las columnas
## [1] "periodo" "mes" "CodigoDepartamento"
## [4] "NombreDepartamento" "CodigoMunicipio" "municipio"
## [7] "nombrecomercial" "bandera" "direccion"
## [10] "producto" "precio" "estado"
p_comb %<>% clean_names;names(p_comb) # se limpian los nombres para evitar inconvenientes
## [1] "periodo" "mes" "codigo_departamento"
## [4] "nombre_departamento" "codigo_municipio" "municipio"
## [7] "nombrecomercial" "bandera" "direccion"
## [10] "producto" "precio" "estado"
p_comb %>% glimpse #Tipos de variables
## Rows: 418,853
## Columns: 12
## $ periodo <dbl> 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 20ā¦
## $ mes <chr> "Enero", "Enero", "Enero", "Enero", "Enero", "Enerā¦
## $ codigo_departamento <chr> "13", "9", "9", "9", "3", "9", "11", "9", "13", "8ā¦
## $ nombre_departamento <chr> "HUILA", "CESAR", "CESAR", "CESAR", "BOGOTA D.C.",ā¦
## $ codigo_municipio <chr> "645", "439", "436", "435", "182", "437", "519", "ā¦
## $ municipio <chr> "GARZON", "BECERRIL", "AGUACHICA", "VALLEDUPAR", "ā¦
## $ nombrecomercial <chr> "ESTACION DE SERVICIO ZULUAGA", "ESTACION DE SERVIā¦
## $ bandera <chr> "BIOMAX", "SAVE", "PETROMIL", "TERPEL", "PETROBRASā¦
## $ direccion <chr> "CALLE 4 No. 2-15", "Cra 5 No. 6-36", "KILOMETRO 6ā¦
## $ producto <chr> "BIODIESEL EXTRA", "BIODIESEL EXTRA", "GASOLINA COā¦
## $ precio <dbl> 8055, 6500, 6100, 6520, 7685, 6550, 7929, 7000, 82ā¦
## $ estado <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", ā¦
p_comb %<>% mutate_if(is.character, as.factor);p_comb %>% glimpse #Convertir en factor las variables que son character
## Rows: 418,853
## Columns: 12
## $ periodo <dbl> 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 20ā¦
## $ mes <fct> Enero, Enero, Enero, Enero, Enero, Enero, Enero, Eā¦
## $ codigo_departamento <fct> 13, 9, 9, 9, 3, 9, 11, 9, 13, 8, 8, 1, 23, 22, 1, ā¦
## $ nombre_departamento <fct> "HUILA", "CESAR", "CESAR", "CESAR", "BOGOTA D.C.",ā¦
## $ codigo_municipio <fct> 645, 439, 436, 435, 182, 437, 519, 440, 659, 420, ā¦
## $ municipio <fct> "GARZON", "BECERRIL", "AGUACHICA", "VALLEDUPAR", "ā¦
## $ nombrecomercial <fct> ESTACION DE SERVICIO ZULUAGA, ESTACION DE SERVICIOā¦
## $ bandera <fct> BIOMAX, SAVE, PETROMIL, TERPEL, PETROBRAS, SAVE, Oā¦
## $ direccion <fct> "CALLE 4 No. 2-15", "Cra 5 No. 6-36", "KILOMETRO 6ā¦
## $ producto <fct> BIODIESEL EXTRA, BIODIESEL EXTRA, GASOLINA CORRIENā¦
## $ precio <dbl> 8055, 6500, 6100, 6520, 7685, 6550, 7929, 7000, 82ā¦
## $ estado <fct> A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,ā¦
Al explorar las primeras filas del conjunto de datos
p_comb mediante la función head(p_comb), se
observó una muestra inicial del contenido. Este conjunto de datos cuenta
con 418,853 registros y 12 variables, como se indicó utilizando la
función dim(p_comb). Los nombres de las columnas fueron
revisados inicialmente con names(p_comb) y luego se
procedió a limpiar estos nombres utilizando clean_names, lo
cual facilita el manejo y la manipulación de las variables sin
inconvenientes en pasos posteriores. Al aplicar glimpse, se
examinaron los tipos de variables presentes en el dataset. AdemƔs, se
decidió convertir todas las variables de tipo character en
factores usando mutate_if(is.character, as.factor), para
optimizar el anĆ”lisis posterior y garantizar que las categorĆas sean
tratadas adecuadamente en los modelos y grƔficos.
unique(p_comb$mes)
## [1] Enero Febrero Marzo Mayo Julio Agosto
## [7] Octubre Noviembre Diciembre Abril Junio Septiembre
## 12 Levels: Abril Agosto Diciembre Enero Febrero Julio Junio Marzo ... Septiembre
"Ordenar meses:"
## [1] "Ordenar meses:"
p_comb$mes %<>% factor(levels=c("Enero","Febrero","Marzo","Abril","Mayo", "Junio", "Julio",
"Agosto", "Septiembre","Octubre", "Noviembre", "Diciembre"))
unique(p_comb$nombre_departamento)
## [1] HUILA
## [2] CESAR
## [3] BOGOTA D.C.
## [4] CUNDINAMARCA
## [5] CAUCA
## [6] ANTIOQUIA
## [7] TOLIMA
## [8] SUCRE
## [9] LA GUAJIRA
## [10] VALLE DEL CAUCA
## [11] META
## [12] RISARALDA
## [13] ATLANTICO
## [14] SANTANDER
## [15] CALDAS
## [16] NARIĆO
## [17] NORTE DE SANTANDER
## [18] PUTUMAYO
## [19] MAGDALENA
## [20] CAQUETA
## [21] BOYACA
## [22] CASANARE
## [23] QUINDIO
## [24] BOLIVAR
## [25] CORDOBA
## [26] CHOCO
## [27] ARAUCA
## [28] GUAVIARE
## [29] VICHADA
## [30] AMAZONAS
## [31] ARCHIPIELAGO DE SAN ANDRES, SANTA CATALINA Y PROVIDENCIA
## [32] VAUPES
## [33] GUAINIA
## [34] NARI?O
## 34 Levels: AMAZONAS ANTIOQUIA ... VICHADA
"Corregir Ć:"
## [1] "Corregir Ć:"
p_comb$nombre_departamento %<>% str_replace_all("\\?","Ć");unique(p_comb$nombre_departamento)
## [1] "HUILA"
## [2] "CESAR"
## [3] "BOGOTA D.C."
## [4] "CUNDINAMARCA"
## [5] "CAUCA"
## [6] "ANTIOQUIA"
## [7] "TOLIMA"
## [8] "SUCRE"
## [9] "LA GUAJIRA"
## [10] "VALLE DEL CAUCA"
## [11] "META"
## [12] "RISARALDA"
## [13] "ATLANTICO"
## [14] "SANTANDER"
## [15] "CALDAS"
## [16] "NARIĆO"
## [17] "NORTE DE SANTANDER"
## [18] "PUTUMAYO"
## [19] "MAGDALENA"
## [20] "CAQUETA"
## [21] "BOYACA"
## [22] "CASANARE"
## [23] "QUINDIO"
## [24] "BOLIVAR"
## [25] "CORDOBA"
## [26] "CHOCO"
## [27] "ARAUCA"
## [28] "GUAVIARE"
## [29] "VICHADA"
## [30] "AMAZONAS"
## [31] "ARCHIPIELAGO DE SAN ANDRES, SANTA CATALINA Y PROVIDENCIA"
## [32] "VAUPES"
## [33] "GUAINIA"
"Reducir San Andres: Nombre muy largo"
## [1] "Reducir San Andres: Nombre muy largo"
p_comb$nombre_departamento[p_comb$nombre_departamento
== "ARCHIPIELAGO DE SAN ANDRES, SANTA CATALINA Y PROVIDENCIA"] <- "SAN ANDRES ISLAS"
p_comb$municipio %<>% str_replace_all("\\?","Ć")
unique(p_comb$producto)
## [1] BIODIESEL EXTRA GASOLINA CORRIENTE OXIGENADA
## [3] GASOLINA EXTRA OXIGENADA GASOLINA CORRIENTE
## [5] GASOLINA EXTRA KEROSENE
## [7] ACEM - DIESEL ECOLOGICO GASOLINA CORRIENTE - IMPORTADO
## [9] BIODIESEL CORRIENTE ACPM - DIESEL
## [11] BIOACEM AL 9%
## 11 Levels: ACEM - DIESEL ECOLOGICO ACPM - DIESEL ... KEROSENE
unique(p_comb$estado)
## [1] A 1
## Levels: 1 A
Se procedió a verificar y corregir las categorĆas de las variables de
tipo factor en el conjunto de datos p_comb. Inicialmente,
se revisaron las categorĆas de la variable mes usando
unique(p_comb$mes). Para asegurar un orden lógico en los
meses del aƱo, se reorganizaron los niveles de esta variable con la
función factor, especificando el orden correcto de los
meses de enero a diciembre.
Luego, al examinar la variable nombre_departamento, se
detectó la presencia de un carĆ”cter incorrecto en lugar de la letra āĆā.
Este error se corrigió utilizando la función
str_replace_all, reemplazando el sĆmbolo ā?ā por āĆā.
Adicionalmente, se simplificó el nombre del departamento āARCHIPIELAGO
DE SAN ANDRES, SANTA CATALINA Y PROVIDENCIAā a āSAN ANDRES ISLASā para
facilitar su manejo en anƔlisis posteriores.
Posteriormente, se revisaron las categorĆas de la variable
municipio, aplicando una corrección similar para el
carĆ”cter āĆā en caso de ser necesario. TambiĆ©n se exploraron las
categorĆas de las variables producto y estado
para asegurar que no hubiera inconsistencias o errores en los datos.
#Verifiar los NA:
nas<-!complete.cases(p_comb) #Determinar filas con al menos un NA
Nas_tabla<-p_comb[nas,] #Tabla registros con NA, 3 datos faltantes en nombre comercial
"El nombre comercial es una variable categórica que posrĆa ser reemplazada consultando con el dueƱo de la data"
## [1] "El nombre comercial es una variable categórica que posrĆa ser reemplazada consultando con el dueƱo de la data"
require(Amelia)
missmap(p_comb) #GrƔfica que identiica los NA
Se realizó una verificación de valores faltantes (NA) en
el conjunto de datos p_comb. Primero, se identificaron las
filas que contenĆan al menos un valor NA utilizando la
función complete.cases, y estas filas se almacenaron en la
tabla Nas_tabla. Se observó que habĆa 3 registros con datos
faltantes en la variable nombre_comercial. Dado que
nombre_comercial es una variable categórica, se sugirió que
podrĆa ser reemplazada, en caso necesario, consultando con el dueƱo de
la base de datos para obtener la información correcta.
Para visualizar de manera mÔs clara la distribución de los valores
faltantes en todo el conjunto de datos, se utilizó la función
missmap del paquete Amelia, que genera un mapa
que identifica visualmente la presencia de NA en el
dataset.
#Observar datos atĆpicos:
summary(p_comb)
## periodo mes codigo_departamento nombre_departamento
## Min. :2017 Junio : 38293 1 : 43434 Length:418853
## 1st Qu.:2017 Julio : 37827 3 : 34921 Class :character
## Median :2018 Agosto : 37742 11 : 34788 Mode :character
## Mean :2018 Mayo : 37574 24 : 32621
## 3rd Qu.:2019 Noviembre: 36982 17 : 31249
## Max. :2019 Octubre : 36890 21 : 17773
## (Other) :193545 (Other):224067
## codigo_municipio municipio nombrecomercial
## 182 : 34938 Length:418853 ESTACION DE SERVICIO SAN ANTONIO: 480
## 1035 : 12893 Class :character ESTACION DE SERVICIO EL BOSQUE : 405
## 34 : 9718 Mode :character ESTACION DE SERVICIO EL JARDIN : 362
## 159 : 7931 ESTACION DE SERVICIO EL LAGO : 325
## 183 : 5102 ESTACION DE SERVICIO EL EDEN : 321
## 876 : 4481 (Other) :416957
## (Other):343790 NA's : 3
## bandera direccion
## TERPEL :154766 VEREDA CODEMACO : 384
## BIOMAX : 63828 CALLE DEL COMERCIO : 305
## MOBIL : 62008 BARRIO EL PINDO DE TUMACO : 228
## TEXACO : 42473 CALLE PRINCIPAL : 215
## PETROMIL: 25365 KM 1 VIA NEIVA : 204
## ZEUSS : 13860 VIA AL MAR KILOMETRO 92 EN TUBARA: 204
## (Other) : 56553 (Other) :417313
## producto precio estado
## BIODIESEL EXTRA :168764 Min. : 100 1: 12468
## GASOLINA CORRIENTE OXIGENADA:165615 1st Qu.: 8000 A:406385
## GASOLINA EXTRA OXIGENADA : 55548 Median : 8755
## BIOACEM AL 9% : 16413 Mean : 8853
## GASOLINA CORRIENTE : 5332 3rd Qu.: 9450
## ACPM - DIESEL : 3218 Max. :70910
## (Other) : 3963
boxplot(p_comb$precio) #Existe un dato atĆpico en los precios, valor muy alto
Para identificar la presencia de datos atĆpicos en el conjunto de
datos p_comb, se realizó un resumen estadĆstico utilizando
la función summary(p_comb). Este resumen proporcionó una
visión general de las estadĆsticas descriptivas para cada variable,
incluyendo valores mĆnimos, mĆ”ximos, medianas, y cuartiles.
Posteriormente, se utilizó un diagrama de caja (boxplot)
para la variable precio, lo que permitió visualizar la
distribución de los precios y detectar posibles valores atĆpicos. El
grĆ”fico reveló la presencia de un valor atĆpico significativamente alto
en la variable precio, lo cual sugiere que podrĆa haber un
error en los datos o que este valor requiere una revisión mÔs detallada
para entender su origen.
require(dplyr)
atip<-filter(p_comb, precio>50000);atip #Detección de datos atĆpico en precios
p_comb$precio[p_comb$precio>50000]<-NA #AtĆpico como NA
#Reemplazar NA con la mediana de los precios
p_comb$precio <- replace_na(p_comb$precio,median(p_comb$precio,na.rm = T))
boxplot(p_comb$precio) #Mejora la distribución de los datos
Para abordar el dato atĆpico detectado en la variable
precio, se utilizó la función filter del
paquete dplyr para identificar los registros donde el
precio era superior a 50,000. Estos registros se almacenaron en la
variable atip para su revisión. Dado que el valor era
considerablemente mayor que el rango tĆpico de precios, se decidió
tratarlo como un valor atĆpico y se reemplazó con NA
utilizando
p_comb$precio[p_comb$precio>50000]<-NA.
Para evitar la pƩrdida de datos y mejorar la calidad del anƔlisis,
los valores NA resultantes se reemplazaron por la mediana
de los precios, calculada sin incluir los NA, mediante la
función replace_na. Finalmente, se generó un nuevo diagrama
de caja (boxplot) de la variable precio,
observÔndose una mejora en la distribución de los datos tras el
tratamiento del valor atĆpico.
Para analizar la evolución de los precios de los combustibles a lo
largo de los meses y años, se creó una nueva variable
periodo en el conjunto de datos p_comb,
convirtiƩndola en un factor para facilitar su uso en el anƔlisis.
Posteriormente, se generó un resumen agrupando los datos por
mes y periodo, calculando el precio promedio
mensual (p_prom) para cada combinación.
Este resumen fue visualizado utilizando un grĆ”fico de lĆneas mediante
ggplot2. En el grƔfico, los meses se colocaron en el eje X
y el precio promedio en el eje Y. Se utilizó una lĆnea discontinua de
color azul cadete para representar la tendencia del precio promedio a lo
largo de los meses. AdemĆ”s, se aplicó la tĆ©cnica de āfacetadoā
(facet_grid) para crear subgrƔficos por cada periodo, lo
que permite observar cómo varĆa el precio promedio a lo largo del tiempo
en diferentes aƱos. El grĆ”fico se tituló āPrecio promedio de
combustibles mensualā, y se ajustó la rotación del texto en el eje X
para mejorar la legibilidad.
library(ggplot2)
p_comb_filtered <- p_comb %>%
filter(precio < 20000) # Ajusta el umbral segĆŗn sea necesario
ggplot(p_comb_filtered, aes(x = producto, y = precio, fill = producto)) +
geom_boxplot(outlier.shape = NA) + # Elimina la visualización de valores atĆpicos extremos
labs(title = "Distribucion de Precios por Tipo de Combustible",
x = "Producto",
y = "Precio") +
theme_minimal() +
theme(legend.position = "none",
axis.text.x = element_text(angle = 45, hjust = 1), # Gira el texto para mejorar la legibilidad
plot.title = element_text(hjust = 0.5)) + # Centra el tĆtulo
scale_y_continuous(labels = scales::comma) # AƱade separadores de miles al eje y
#Precios a travƩs de los meses y aƱos
p_comb$periodo<-as.factor(p_comb$periodo)
resum1 <- p_comb %>% group_by(mes,periodo) %>%
summarise(p_prom=mean(precio))
## `summarise()` has grouped output by 'mes'. You can override using the `.groups`
## argument.
ggplot(resum1, aes(x = mes, y = p_prom, group=1)) +
geom_line(linetype="dashed",size=1.2, col="cadetblue") +
ggtitle("Precio promedio de combustibles mensual")+
xlab("AƱo")+
ylab("precio")+
facet_grid(cols = vars(periodo))+
theme(axis.text.x = element_text(angle = 90, hjust = 1))
La tendencia de los precios de los combustibles a lo largo del tiempo ha sido creciente. Los tres grƔficos revelan un incremento constante.
Para analizar los precios promedios por departamento a lo largo de
los meses, se creó un resumen del conjunto de datos p_comb
agrupando por producto, periodo, y
mes. En este resumen, se calculó el precio promedio
(p_prom2) para cada combinación de estas variables.
Este anÔlisis se visualizó utilizando un grÔfico de puntos con
ggplot2, donde se representa el mes en el eje X y el precio
promedio en el eje Y. Los puntos estÔn coloreados según el tipo de
producto, permitiendo diferenciar entre los distintos combustibles. Se
utilizó la tĆ©cnica de āfacetadoā (facet_grid) para crear
subgrƔficos por cada periodo, lo que permite observar las variaciones de
precios a lo largo del tiempo para cada producto. El grÔfico se tituló
āPrecio promedio a travĆ©s de los meses, por departamentosā, y se ajustó
la rotación del texto en el eje X para mejorar la legibilidad.
#Precios promedios por departamento
resum2 <- p_comb %>% group_by(producto,periodo,mes) %>%
summarise(p_prom2=mean(precio))
## `summarise()` has grouped output by 'producto', 'periodo'. You can override
## using the `.groups` argument.
ggplot(resum2, aes(x=mes,y=p_prom2, color=producto))+
geom_point(cex=3)+
facet_grid(cols = vars(periodo))+
theme(axis.text.x = element_text(angle = 90, hjust = 1))+
xlab("mes")+
ylab("precio promedio")+
ggtitle("Precio promedio a travƩs de los meses, por departamentos")
La gasolina extra oxigenada se ha mantenido a lo argo del tiempo con el mayor precio, seguida de la gasolina extra. El ACEM- Diesel económico es el combustible mÔs económico. Sin embargo, todos presentan una tendencia creciente en sus precios a lo largo del tiempo.
#Distribución, precio del combustible según tipo:
ggplot(resum2, aes(x=p_prom2, fill=producto))+
geom_density(alpha=0.4)+
xlab("Precio promedio")+
ylab("Densidad")+
ggtitle("Distribución precio promedio combustibles según tipo")
Las distribuciones reflejan los rangos de precios variados que presenta cada tipo de combustible.
ggplot(resum2, aes(x=p_prom2, fill=periodo))+
geom_density(alpha=0.4)+
xlab("Precio promedio")+
ylab("Densidad")+
ggtitle("Distribución precio promedio combustibles según periodo")
Al mirar la distribución según los años, se puede apreciar nuevamente la tencdencia creciente.
Analizando el comportamiento de la gasolina corriente
g_corriente<-subset(p_comb,subset=(producto=="GASOLINA CORRIENTE")) #Filtrar base por gasolina corriente
resum3 <- g_corriente %>% group_by(periodo,mes) %>%
summarise(p_prom3=mean(precio))
## `summarise()` has grouped output by 'periodo'. You can override using the
## `.groups` argument.
#Precio de la gasolina corriente en el tiempo
ggplot(resum3, aes(x = mes, y = p_prom3, color=periodo)) +
geom_point(stat="identity")+
ggtitle("Precio promedio mensual gasolina corriente")+
xlab("AƱo")+
ylab("precio")+
theme(axis.text.x = element_text(angle = 90, hjust = 1))
require(plotly)
plot_ly(resum3, x = ~mes, y = ~p_prom3, type = "scatter", mode = "markers",
color = ~periodo, colors = "Set1") %>%
layout(title = "Precio promedio mensual gasolina corriente",
xaxis = list(title = "mes"),
yaxis = list(title = "Precio promedio"))
Al observar el comportamiento del precio promedio de la gasolina corriente, se puede observar un crecimiento alto en los meses de abril a mayo del año 2017, y el mes de julio dle mismo año, alcanzó el precio promedio mÔs elevado
Precio promedio de la casolina corriente segĆŗn departamento
resum4 <- g_corriente %>% group_by(periodo,nombre_departamento) %>%
summarise(p_prom4=mean(precio))
## `summarise()` has grouped output by 'periodo'. You can override using the
## `.groups` argument.
ggplot(resum4, aes(x=reorder(nombre_departamento,-p_prom4),y=p_prom4))+
geom_bar(stat="identity", position=position_dodge(),fill="darkolivegreen")+
theme(axis.text.x = element_text(angle = 90, hjust = 1))+
ggtitle("Precio promedio gasolina corriente segĆŗn departamento")+
xlab("Departamentos")+
ylab("precio promedio")
La presente grĆ”fica relaciona el precio promedio de la gasolina corriente segĆŗn el departamento. GuainĆa dispone del precio promedio mĆ”s elevado, mientras Cesar y La Guajira los mĆ”s bajos. Esta informaciónn se puede apreciar mejor a travĆ©s de los mapas que se crearĆ”n a continuación
# Cargar las librerĆas necesarias
library(sf)
## Linking to GEOS 3.13.0, GDAL 3.10.1, PROJ 9.5.1; sf_use_s2() is TRUE
library(dplyr)
library(leaflet)
# Leer el archivo shapefile
mapa_col <- st_read("COLOMBIA/COLOMBIA.shp", quiet = FALSE)
## Reading layer `COLOMBIA' from data source
## `C:\Users\Keyla Alba\OneDrive - Universidad del Norte\UNINORTE\2025-I\DATAVIZ\DataViz_R\COLOMBIA\COLOMBIA.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 33 features and 11 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -81.73575 ymin: -4.227907 xmax: -66.84735 ymax: 13.39453
## Geodetic CRS: WGS 84
# Corregir geometrĆas no vĆ”lidas
mapa_col <- st_make_valid(mapa_col)
En este anÔlisis, se comienza revisando la información de los
departamentos contenida en el objeto mapa_col@data, que
representa la estructura de datos asociada a un archivo shapefile
(shp). Esta estructura incluye nombres de departamentos,
identificadores geogrĆ”ficos, y otras caracterĆsticas relevantes para la
visualización geoespacial.
Primero, se extrajeron los nombres de los departamentos tanto desde
la base de datos p_comb como desde el archivo shapefile,
almacenƔndolos en las variables dpto_1 y
dpto_2, respectivamente. Esto permite comparar y trabajar
con los nombres en ambos conjuntos de datos.
Para asegurar la coherencia y facilitar comparaciones posteriores,
los nombres de los departamentos en ambas variables se convirtieron a
letras minúsculas utilizando la función tolower. Este paso
es crucial para evitar problemas de coincidencia debido a diferencias de
mayĆŗsculas y minĆŗsculas entre los dos conjuntos de datos.
# Extraer los nombres de los departamentos desde la base de datos y el shapefile
dpto_1 <- tolower(p_comb$nombre_departamento) # Base de datos
dpto_2 <- tolower(mapa_col$DPTO_CNMBR) # Archivo shapefile
# Verificar quƩ nombres de la base de datos no estƔn en el shapefile
M1 <- which(is.na(match(dpto_1, dpto_2)))
# Corregir nombres que no coinciden entre la base de datos y el shapefile
dpto_2 <- str_replace_all(dpto_2, "\\?", "Ʊ")
dpto_2 <- str_replace_all(dpto_2, "archipielago de san andres", "san andres islas")
# Agregar los nombres corregidos como códigos para unir las bases de datos
p_comb$codigo <- dpto_1
mapa_col$codigo <- dpto_2
Para asegurar la consistencia entre los nombres de los departamentos
en la base de datos p_comb y el archivo shapefile
mapa_col, se utilizó la función match para
comparar ambos conjuntos de nombres. Concretamente, se identificaron
aquellos nombres en dpto_1 (extraĆdos de la base de datos
p_comb) que no tenĆan una correspondencia en
dpto_2 (extraĆdos del archivo shapefile). Los Ćndices de
estos nombres no coincidentes se almacenaron en M1, y los
nombres correspondientes se listaron utilizando
dpto_1[M1].
Una vez identificados los nombres que no coincidĆan, se procedió a
realizar las correcciones necesarias en dpto_2 para alinear
ambas bases de datos. Esto incluyó reemplazar los caracteres ā?ā por āƱā
y modificar el nombre del departamento āarchipielago de san andresā a
āsan andres islasā utilizando la función str_replace_all.
Estas modificaciones aseguran que los nombres de los departamentos en
ambas bases de datos sean consistentes y puedan ser utilizados sin
problemas en anƔlisis posteriores.
# Unir la base de datos y el archivo shapefile a través del código agregado
datos_unidos <- merge(st_drop_geometry(mapa_col), p_comb, by = "codigo", all = TRUE, sort = FALSE)
DespuƩs de realizar las correcciones necesarias en los nombres de los
departamentos, se verificó que todos los nombres coincidieran entre las
dos bases de datos (p_comb y mapa_col)
comprobando que no quedaran valores NA en la comparación
(dpto_1[M1]). Una vez confirmado que todos los nombres eran
consistentes, se procedió a agregar estos nombres como códigos en ambas
bases de datos, asignƔndolos a una nueva columna denominada
codigo tanto en p_comb como en
mapa_col@data.
Este código sirvió como clave común para unir la base de datos
p_comb con el archivo shapefile mapa_col. La
unión se realizó utilizando la función merge, que combinó
ambas bases de datos en un nuevo objeto llamado
datos_unidos. Esta unión se realizó utilizando la columna
codigo como clave, asegurando que los datos espaciales del
shapefile y la información adicional de la base de datos estuvieran
correctamente alineados.
# Filtrar los datos para el aƱo 2019 y para el producto "GASOLINA CORRIENTE"
datos_unidos2 <- datos_unidos %>%
filter(periodo == "2019", producto == "GASOLINA CORRIENTE") %>%
group_by(codigo, DPTO_NANO_, DPTO_NAREA, DPTO_CSMBL, DPTO_NANO, PAIS_PAIS_, SHAPE_Leng, SHAPE_Area) %>%
summarize(precio_prom_dpto = mean(precio)) %>%
ungroup() # Desagrupar los datos para evitar problemas con leaflet
## `summarise()` has grouped output by 'codigo', 'DPTO_NANO_', 'DPTO_NAREA',
## 'DPTO_CSMBL', 'DPTO_NANO', 'PAIS_PAIS_', 'SHAPE_Leng'. You can override using
## the `.groups` argument.
Para visualizar el promedio de los precios de la gasolina corriente
en Colombia durante el año 2019, se realizó una serie de filtrados y
agrupaciones en la base de datos datos_unidos. Primero, se
filtró el conjunto de datos para incluir únicamente los registros
correspondientes al aƱo 2019, utilizando la variable
periodo. Luego, se aplicó un segundo filtro para
seleccionar Ćŗnicamente los registros relacionados con el producto
āGASOLINA CORRIENTEā.
Una vez filtrados los datos, se procedió a agrupar la base por varias
variables geoespaciales y administrativas, incluyendo
codigo, DPTO_NANO_, DPTO_NAREA,
DPTO_CSMBL, entre otras. Para cada grupo, se calculó el
promedio del precio de la gasolina (precio_prom_dpto)
utilizando la función summarize. Este proceso permitió
colapsar la base de datos, generando un resumen que refleja el precio
promedio de la gasolina corriente por departamento para todo el aƱo
2019, lo que facilita su posterior representación en un mapa.
# Construcción del mapa
boxplot(datos_unidos2$precio_prom_dpto)
max(datos_unidos2$precio_prom_dpto) # Ćtil para definir los 'breaks'
## [1] 12413.64
# Crear un factor para asignar colores basado en el precio promedio
datos_unidos2$precio_prom_dpto <- as.numeric(datos_unidos2$precio_prom_dpto)
precio_fac <- cut(datos_unidos2$precio_prom_dpto,
breaks = c(0, 8000, 9000, 10000, 11000, 13000),
labels = c("a. 0-8.000", "b. 8.000-9.000", "c. 9.000-10.000", "d. 10.000-11.000", "e. 12.000-13.000"))
Para construir un mapa que represente los precios promedios de la
gasolina corriente por departamento en Colombia durante 2019, se inició
con un anƔlisis exploratorio de la variable
precio_prom_dpto en datos_unidos2. Se generó
un diagrama de caja (boxplot) para visualizar la
distribución de los precios promedio, lo cual ayudó a identificar la
variabilidad y los posibles valores atĆpicos. AdemĆ”s, se utilizó la
función max para identificar el valor mÔximo, que resultó
Ćŗtil para definir los puntos de corte (breaks) en la
categorización de los precios.
Posteriormente, se creó una variable precio_fac
utilizando la función cut para clasificar los precios
promedio en cinco categorĆas, basadas en los puntos de corte
establecidos (0, 8000, 9000, 10000, 11000, 13000). Estas categorĆas
facilitarÔn la asignación de colores en el mapa.
Para cada categorĆa de precios, se asignaron colores especĆficos: verde para los precios mĆ”s bajos, seguido de amarillo, naranja, rojo, marrón, y negro para los precios mĆ”s altos. Esta codificación por colores permitirĆ” una representación visual clara de las variaciones en los precios de la gasolina corriente a lo largo de los departamentos en Colombia.
# Asignar colores manualmente
colores <- c("a. 0-8.000" = "green",
"b. 8.000-9.000" = "yellow",
"c. 9.000-10.000" = "orange",
"d. 10.000-11.000" = "red",
"e. 12.000-13.000" = "brown")
Para visualizar los precios promedios de la gasolina corriente por
departamento en Colombia durante 2019 en un mapa interactivo, se llevó a
cabo una serie de pasos utilizando leaflet. Primero, se
ajustaron los nombres de los departamentos (DPTO_CNMBR) a
un formato adecuado para su visualización, utilizando la función
iconv para convertir la codificación de caracteres de
āUTF-8ā a āISO_8859-1ā.
Luego, se creó una leyenda personalizada utilizando la función
colorFactor, asignando colores especĆficos a intervalos de
precios predefinidos (0-8,000, 8,000-9,000, 9,000-10,000, 10,000-11,000,
y 12,000-13,000). Estos colores permiten una fÔcil interpretación visual
de los datos en el mapa.
Finalmente, se generó el mapa utilizando leaflet. Se
aƱadieron las capas del mapa base (addTiles) y los
polĆgonos que representan los departamentos (addPolygons),
donde se aplicaron los colores definidos previamente en función del
precio promedio. AdemÔs, se incluyó una leyenda en la esquina superior
derecha (addLegend), que facilita la interpretación de los
colores en relación con los rangos de precios, con el tĆtulo āPrecio
promedio de gasolina corriente en 2019ā. Este mapa interactivo permite a
los usuarios explorar visualmente las variaciones de precios por
departamento en Colombia.
# Verificar y asignar colores
if (length(precio_fac) == nrow(datos_unidos2)) {
datos_unidos2$color <- colores[precio_fac]
cat("Colores asignados correctamente.\n")
} else {
stop("Error: La longitud de precio_fac no coincide con el nĆŗmero de filas en datos_unidos2.")
}
## Colores asignados correctamente.
# Fusionar la geometrĆa de `mapa_col` con `datos_unidos2`
datos_unidos_final <- merge(mapa_col, datos_unidos2, by = "codigo")
# Graficar el mapa con la leyenda
leaflet(datos_unidos_final) %>%
addTiles() %>%
addPolygons(popup = ~DPTO_CNMBR, color = "black", fillColor = ~color, weight = 1.1) %>%
addLegend(position = "topright",
pal = colorFactor(palette = c("green", "yellow", "orange", "red", "brown"),
domain = levels(precio_fac)),
values = levels(precio_fac),
title = "Precio promedio de gasolina corriente en 2019")
De esta manera es posible observar mediante rangos e información espacial, aquellos departamentos que presentaron precios mÔs elevados y mÔs bajos en cuanto al precio de la gasolina corriente durante el año 2019
Cuando se trata de grandes conjuntos de datos
que potencialmente exceden la memoria de su mƔquina, es bueno tener otra
posibilidad, como su propio servidor con una base de datos
SQL/PostgreSQL, donde se puede consultar los datos. Por
ejemplo, un conjunto de datos financieros de 5 GB caben en una
memoria RAM bƔsica, pero los datos consumen muchos recursos.
Una solución es utilizar una base de datos basada en SQL,
donde puedo consultar los datos en trozos mƔs pequeƱos, dejando recursos
para el cƔlculo.
Aunque MySQL es la mƔs utilizada,
PostgreSQL tiene la ventaja de ser de código abierto y
gratuita para todos los usos. Sin embargo, todavĆa tenemos que conseguir
un servidor. Una forma posible de hacerlo es alquilar un servidor de
Amazon, sin embargo, existe la opción de utilizar
Render tal como se explicó en la sección de
Python.
Ahora es el momento de conectarse a la base de datos con
R. Este enfoque utiliza el paquete
RPostgreSQL. Los siguientes paquetes y herramientas deben
ser instalados para poder hacer uso de la API y realizar la
conexión con éxito:
RPostgresqlDBIEl paquete RPostgreSQL y DBI se puede
instalar desde CRAN o Github.
Para conectar, necesitamos introducir los siguientes comandos en
R, nos conectaremos a la base de datos creada anteriormente
en Docker en la sección de Python.
RPostgres es una interfaz compatible con DBI
para la base de datos Postgres. Es una reescritura
desde cero usando C++ y
Rcpp.
Este paquete actĆŗa tanto como el controlador de la base
de datos como la interfaz DBI. El código y la
información adicional estÔn disponibles en su repositorio
GitHub aquĆ: RPostgres. Debe modificar la
información que recibe la función dbConnect() por aquella
que obtuvo al crear su base de datos en Docker.
library(DBI)
library(RPostgreSQL)
#con <- dbConnect(RPostgres::Postgres(),
# dbname = "undatascience-db",
# host = "localhost",
# port = 5432,
# user = "undatascience-user",
# password = "undatascience-password")
dbListTables():#dbListTables(conn = con)
Considere la base de datos indicada en el enlace y con respecto a tasas de suicidios mundiales entre 1985 y 2016. La url es:
https://www.kaggle.com/russellyates88/suicide-rates-overview-1985-to-2016
La base de datos Suicide Rates Overview 1985 to 2016 se refiere a lo siguiente:
Existen varias seƱales correlacionadas con el aumento de las tasas de suicidios mundial, este conjunto de datos fue creado para encontrar este tipo de seƱales y contiene 27,820 observaciones que aportan información tanto socioeconómica como demogrĆ”fica de cada paĆs.
Las variables se codificaron como:
table del paquete
base y utilĆcela para explorar la base de datos.Considere la base de datos master y realice lo siguiente
(utilice los operadores pipe de continuidad y
compuesto):
Edite y explore reglas para verificar que la base de datos no contenga posibles registros erróneos.
Filtre los datos de Colombia y los de EEUU,
generando dos bases de datos, llamadas master_col y
master_eu.
Realice un anĆ”lisis de la evolución de los suicidios por cada 100.000 habitantes, del PIB per cĆ”pita y del IDH, a lo largo de los aƱos en ambos paĆses.
Realice un anĆ”lisis de la evolución de los suicidios por cada 100.000 habitantes, del PIB per cĆ”pita y del IDH, a lo largo de los aƱos en ambos paĆses por gĆ©nero.
Realice un anĆ”lisis de la evolución de los suicidios por cada 100.000 habitantes, del PIB per cĆ”pita y del IDH, a lo largo de los aƱos en ambos paĆses por grupo de edad.
Accidentalidad_en_Barranquilla.csv. Deben entregar
un script .R con los códigos usados.Este anÔlisis puede consistir en:
1.1 Contextualizar tanto la base de datos como las variables
describiendo en quƩ consiste cada una de ellas. La base de
datos estĆ” en el sitio web:
https://www.datos.gov.co/Transporte/Accidentalidad-en-Barranquilla/yb9r-2dsi
1.2. Analizar las caracterĆsticas de la base de datos. Estas pueden incluir: nĆŗmero de filas, nĆŗmero de columnas, nombres de las variables, tipos de variables, entre otros.
1.3. Analizar cada una de las variables según su tipo: numéricas y categóricas.
1.4. Filtrar la base de datos para entender mejor su estructura. Aplique filtros en al menos cinco oportunidades.
1.5. Utilice la función table para explorar la
base de datos.
1.6. Identifique los valores NA (Not Available) en la base de datos.
1.7. Analice la presencia de posibles valores atĆpicos.
Aplique imputación de datos usando las siguientes opciones para
method:
method="pmm"method="norm.predict"method="norm.nob"method="norm"Identifique datos atĆpicos para cada variable en el dataset usando las tĆ©cnicas estudiadas en clase. AdemĆ”s, realice imputación de los datos atĆpicos con base en lo desarrollado en el Ćtem anterior.
plotly y shinyplotlyCualquier grƔfico hecho con el paquete R de
plotly es impulsado por la biblioteca
JavaScript plotly.js.
La función plot_ly() proporciona una interfaz ādirectaā a
plotly.js con algunas abstracciones adicionales para ayudar
a reducir la escritura.
Estas abstracciones, inspiradas en la gramƔtica de los grƔficos y
ggplot2, hacen que sea mucho mƔs rƔpido iterar de un
grƔfico a otro,
facilitando el descubrimiento de caracterĆsticas interesantes en los
datos (Wilkinson 2005; Wickham 2009).
Para demostrarlo, usaremos plot_ly() para explorar el
conjunto de datos diamonds de ggplot2 y
aprenderemos un poco cómo funciona plotly.
library(plotly)
library(ggplot2)
data(diamonds, package = "ggplot2")
knitr::kable(head(diamonds))
| carat | cut | color | clarity | depth | table | price | x | y | z |
|---|---|---|---|---|---|---|---|---|---|
| 0.23 | Ideal | E | SI2 | 61.5 | 55 | 326 | 3.95 | 3.98 | 2.43 |
| 0.21 | Premium | E | SI1 | 59.8 | 61 | 326 | 3.89 | 3.84 | 2.31 |
| 0.23 | Good | E | VS1 | 56.9 | 65 | 327 | 4.05 | 4.07 | 2.31 |
| 0.29 | Premium | I | VS2 | 62.4 | 58 | 334 | 4.20 | 4.23 | 2.63 |
| 0.31 | Good | J | SI2 | 63.3 | 58 | 335 | 4.34 | 4.35 | 2.75 |
| 0.24 | Very Good | J | VVS2 | 62.8 | 57 | 336 | 3.94 | 3.96 | 2.48 |
library(knitr)
# Crear la tabla con kable
data <- data.frame(
Variable = c("price", "carat", "cut", "color", "clarity", "x", "y", "z", "depth", "table"),
Description = c(
"price in US dollars",
"weight of the diamond",
"quality of the cut",
"diamond color",
"measurement of how clear the diamond is",
"length in mm",
"width in mm",
"depth in mm",
"total depth percentage",
"width of top of diamond relative to widest point"
),
Values = c(
"$326-$18,823",
"0.2-5.01",
"Fair, Good, Very Good, Premium, Ideal",
"J (worst) to D (best)",
"I1 (worst), SI2, SI1, VS2, VS1, VVS2, VVS1, IF (best)",
"0-10.74",
"0-58.9",
"0-31.8",
"43-79",
"43-95"
)
)
# Mostrar la tabla en formato HTML
kable(data, format = "html", align = "l")
| Variable | Description | Values |
|---|---|---|
| price | price in US dollars | $326-$18,823 |
| carat | weight of the diamond | 0.2-5.01 |
| cut | quality of the cut | Fair, Good, Very Good, Premium, Ideal |
| color | diamond color | J (worst) to D (best) |
| clarity | measurement of how clear the diamond is | I1 (worst), SI2, SI1, VS2, VS1, VVS2, VVS1, IF (best) |
| x | length in mm | 0-10.74 |
| y | width in mm | 0-58.9 |
| z | depth in mm | 0-31.8 |
| depth | total depth percentage | 43-79 |
| table | width of top of diamond relative to widest point | 43-95 |
cut,
clarity, etc.) a propiedades visuales (p.Ā ej.,
x, y, color, etc.) dentro de
plot_ly(), como se ve en la figura,plot_ly(diamonds, x = ~cut)
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
plot_ly(diamonds, x = ~cut, y = ~clarity)
## No trace type specified:
## Based on info supplied, a 'histogram2d' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram2d
plot_ly(diamonds, x = ~cut, color = ~clarity, colors = "Accent")
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
ggplotlyLa función ggplotly() del paquete
plotly tiene la capacidad de traducir ggplot2
a plotly.
Esta funcionalidad puede ser realmente útil para añadir rÔpidamente
interactividad a su flujo de trabajo ggplot2
existente.
AdemƔs, incluso si conoce bien plot_ly() y
ggplotly(), puede seguir siendo deseable para crear
visualizaciones
que no son necesariamente fƔciles de lograr sin ella. Para demostrarlo,
vamos a explorar la relación entre price
y otras variables del conocido conjunto de datos
diamonds.
El binning hexagonal (es decir, geom_hex())
es una forma Ćŗtil de visualizar una densidad 2D,
como la relación entre price y carat, como se
muestra en la siguiente figura, donde podemos ver que existe una
fuerte
relación lineal positiva entre el logaritmo de los quilates y el
precio.
También muestra que, para muchos, el quilate sólo se redondea a un
nĆŗmero determinado, indicado por las bandas azul claro.
Hacer este grÔfico interactivo facilita la decodificación de los colores
hexagonales en los recuentos que representan.
library(ggplot2)
library(plotly)
library(hexbin)
p <- ggplot(diamonds, aes(x = log(carat), y = log(price))) +
geom_hex(bins = 100)
ggplotly(p)
ggplotly() sobre
plot_ly() para aprovechar la interfaz consistente y
expresiva de ggplot2cut) con geom_freqpoly(),geomgeom_boxplot(), geom_histogram(),
geom_density(), etc.) y es una caracterĆstica clave de
ggplot2.Las funciones add_bars() y
add_histogram() envuelven los tipos de trazado de barras e
histogramas de plotly.js.
La principal diferencia entre ellas es que las trazas de barras
requieren alturas de barras (tanto x como
y),
mientras que las trazas de histograma requieren sólo una variable, y
plotly.js maneja el binning en el navegador.
Y quizƔs de forma confusa, ambas funciones pueden utilizarse para
visualizar la distribución de una variable numérica o discreta.
Asà que, esencialmente, la única diferencia entre ellas es dónde se
produce el binning.
La siguiente figura compara el algoritmo de agrupación por
defecto en plotly.js con algunos algoritmos
diferentes
disponibles en R a través de la función
hist().
Aunque plotly.js tiene la capacidad de personalizar los
bins del histograma a travƩs de
xbins/ybins,
R tiene diversas facilidades para estimar el número óptimo
de bins en un histograma que podemos aprovechar
fƔcilmente.
La función hist() por sà sola nos permite referenciar 3
algoritmos famosos por su nombre (Sturges 1926;
Freedman-Diaconsis 1981
y Scott 1979), pero tambiƩn hay paquetes (por ejemplo, el
paquete histogram) que amplĆan esta interfaz
para incorporar mĆ”s metodologĆa (Mildenberger, Rozenholc y Zasada.
2009).
La función price_hist() que aparece a continuación envuelve
la función hist() para obtener los resultados del
binning,
y mapear esos bins a una versión graficada del histograma
usando add_bars().
p1 <- plot_ly(diamonds, x = ~price) %>%
add_histogram(name = "plotly.js")
price_hist <- function(method = "FD") {
h <- hist(diamonds$price, breaks = method, plot = FALSE)
plot_ly(x = h$mids, y = h$counts) %>% add_bars(name = method)
}
subplot(
p1, price_hist(), price_hist("Sturges"), price_hist("Scott"),
nrows = 4, shareX = TRUE
)
add_histogram() envĆa todos los valores
observados al navegador y deja que plotly.js realice el
binning.Se necesita mƔs esfuerzo humano para realizar el binning en
R, pero hacerlo tiene la ventaja de enviar menos
datos,
y requiere menos trabajo de cƔlculo del navegador web.
En este caso, sólo tenemos unos 50,000 registros,
por lo que no hay mucha diferencia en los tiempos de carga o el tamaƱo
de la pƔgina.
Sin embargo, con 1 millón de registros, el tiempo de
carga de la pƔgina es mƔs del doble y el tamaƱo de la pƔgina casi se
duplica.
library(dplyr)
p1 <- plot_ly(diamonds, x = ~cut) %>%
add_histogram()
p2 <- diamonds %>%
count(cut) %>%
plot_ly(x = ~cut, y = ~n) %>%
add_bars()
subplot(p1, p2) %>% hide_legend()
Obsérvese cómo la función one_plot() define lo que debe
mostrarse en cada panel,
y luego se emplea una estrategia de dividir-aplicar-recombinar (es
decir, split(), lapply(),
subplot())
para generar la visualización del enrejado.
one_plot <- function(d) {
plot_ly(d, x = ~price) %>%
add_annotations(
~unique(clarity), x = 0.5, y = 1,
xref = "paper", yref = "paper", showarrow = FALSE
)
}
diamonds %>%
split(.$clarity) %>%
lapply(one_plot) %>%
subplot(nrows = 2, shareX = TRUE, titleX = FALSE) %>%
hide_legend()
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
## No trace type specified:
## Based on info supplied, a 'histogram' trace seems appropriate.
## Read more about this trace type -> https://plotly.com/r/reference/#histogram
La visualización de las distribuciones discretas
mĆŗltiples es difĆcil.
Esta sutil complejidad se debe a que tanto los recuentos como las
proporciones
son importantes para comprender las distribuciones discretas
multivariadas.
La figura siguiente presenta los recuentos de diamantes,
divididos por su talla y su claridad, mediante un grƔfico de barras
agrupadas.
plot_ly(diamonds, x = ~cut, color = ~clarity) %>%
add_histogram()
Hay varios marcos diferentes para crear aplicaciones web a travƩs
de R, pero centraremos nuestra atención en la vinculación
de grƔficos plotly con shiny, un paquete de
R para crear aplicaciones web reactivas completamente en
R. El modelo de programación reactiva de Shiny
permite a los programadores de R aprovechar sus
conocimientos existentes de R y crear aplicaciones web
basadas en datos sin ninguna experiencia previa en programación web.
Shiny en sà mismo es en gran medida agnóstico al motor
utilizado para renderizar las vistas de datos (es decir, puede
incorporar cualquier tipo de salida de R), pero el propio
shiny también agrega algún soporte especial para
interactuar con grƔficos e imƔgenes estƔticas de R (Chang
2017).
Al vincular los grÔficos en una aplicación web, hay
compensaciones a considerar cuando se utilizan grƔficos estƔticos de
R en lugar de grƔficos basados en la web. Resulta que esas
compensaciones se complementan muy bien con las fortalezas y debilidades
relativas de la vinculación de vistas con plotly, lo que
hace que su combinación sea un potente conjunto de herramientas para
vincular vistas en la web desde R. El propio
Shiny proporciona una forma de acceder a eventos con
grƔficos estƔticos hechos con cualquiera de los siguientes paquetes de
R: graphics, ggplot2 y
lattice. Estos paquetes son muy maduros, con todas las
funciones bien probadas, y soportan una gama increĆblemente amplia de
grƔficos, pero como deben ser regenerados en el servidor, son
fundamentalmente limitados desde una perspectiva de grƔficos
interactivos.
Comparativamente, plotly no tiene la misma gama e
historia, pero proporciona mƔs opciones y control sobre la
interactividad. MƔs concretamente, debido a que plotly estƔ
intrĆnsecamente basado en la web, permite un mayor control sobre cómo se
actualizan los grƔficos en respuesta a la entrada del usuario (por
ejemplo, cambiar el color de unos pocos puntos en lugar de redibujar
toda la imagen). Esta sección aborda el cómo utilizar grÔficos
plotly dentro de shiny, cómo conseguir que
esos grÔficos se comuniquen con otros tipos de vistas de datos, y cómo
hacerlo todo de forma eficiente.
El patrón mÔs común de plotly+shiny utiliza una
entrada de shiny para controlar una salida de
plotly. Mostraremos un ejemplo sencillo de uso de la
función selectizeInput() de shiny para crear
un desplegable que controla un grƔfico de plotly. Este
ejemplo, asà como cualquier otra aplicación shiny, tiene
dos partes principales:
ui, define cómo se
muestran los widgets de entrada y salida en la pƔgina. La
función fluidPage() ofrece una forma agradable y rÔpida de
obtener un diseƱo responsivo basado en una cuadrĆcula, pero tambiĆ©n vale
la pena seƱalar que la UI es completamente personalizable,
y paquetes como shinydashboard facilitan el aprovechamiento
de marcos de diseño mÔs sofisticados (Chang y Borges Ribeiro).La función del servidor, server, define un mapeo
de los valores de entrada a los widgets de salida. MƔs
concretamente, el servidor shiny es una
R function() entre los valores ingresados por el cliente y
las salidas generadas en el servidor web.
Cada widget de entrada, incluido el selectizeInput(),
estĆ” vinculado a un valor de entrada al que se puede acceder en el
servidor dentro de una expresión reactiva. Las expresiones reactivas de
Shiny construyen un grƔfico de dependencia entre las
salidas (tambiƩn conocidas como puntos finales reactivos) y las entradas
(tambiƩn conocidas como fuentes reactivas). La verdadera potencia de las
expresiones reactivas reside en su capacidad para encadenar y almacenar
en caché los cÔlculos, pero nos centraremos primero en la generación de
salidas. Para generar una salida, tienes que elegir una función adecuada
para renderizar el resultado de una expresión reactiva.
plotlyConsideraremos el siguiente dataset txhousing para este
ejemplo. La siguiente rutina utiliza la función
renderPlotly() para renderizar una expresión reactiva que
genera un grÔfico plotly. Esta expresión depende del valor
de entrada input$cities (es decir, el valor de entrada
ligado al widget de entrada con un inputId de
"cities") y almacena la salida como output$p.
Esto indica a shiny que inserte el grƔfico reactivo en el
contenedor plotlyOutput(outputId = "p") definido en la
interfaz de usuario.
str(txhousing)
## tibble [8,602 Ć 9] (S3: tbl_df/tbl/data.frame)
## $ city : chr [1:8602] "Abilene" "Abilene" "Abilene" "Abilene" ...
## $ year : int [1:8602] 2000 2000 2000 2000 2000 2000 2000 2000 2000 2000 ...
## $ month : int [1:8602] 1 2 3 4 5 6 7 8 9 10 ...
## $ sales : num [1:8602] 72 98 130 98 141 156 152 131 104 101 ...
## $ volume : num [1:8602] 5380000 6505000 9285000 9730000 10590000 ...
## $ median : num [1:8602] 71400 58700 58100 68600 67300 66900 73500 75000 64500 59300 ...
## $ listings : num [1:8602] 701 746 784 785 794 780 742 765 771 764 ...
## $ inventory: num [1:8602] 6.3 6.6 6.8 6.9 6.8 6.6 6.2 6.4 6.5 6.6 ...
## $ date : num [1:8602] 2000 2000 2000 2000 2000 ...
library(shiny)
library(plotly)
ui <- fluidPage(
selectizeInput(
inputId = "cities",
label = "Select a city",
choices = unique(txhousing$city),
selected = "Abilene",
multiple = TRUE
),
plotlyOutput(outputId = "p")
)
server <- function(input, output) {
output$p <- renderPlotly({
plot_ly(txhousing, x = ~date, y = ~median) %>%
filter(city %in% input$cities) %>%
group_by(city) %>%
add_lines()
})
}
shinyApp(ui, server)
Si, en lugar de un grÔfico plotly, una expresión
reactiva genera un grƔfico R estƔtico, simplemente utilice
renderPlot() (en lugar de renderPlotly()) para
renderizarlo y plotOutput() (en lugar de
plotlyOutput()) para posicionarlo. Otros widgets de salida
shiny utilizan esta convención de nombres:
renderDataTable()/datatableOutput(),renderPrint()/verbatimTextOutput(),renderText()/textOutput(),renderImage() etc.Los paquetes que se basan en el estƔndar htmlwidgets
(por ejemplo, plotly y leaflet) tambiƩn siguen
esta convención (renderPlotly()/plotlyOutput()
y renderLeaflet()/leafletOutput()).
Shiny también viene con otros widgets de entrada útiles,
que pueden estilizarse fƔcilmente con CSS o
SASS, e incluso integrarse con widgets personalizados:
selectInput()/selectizeInput() para menĆŗs
desplegables.numericInput() para ingresar un solo número.sliderInput() para seleccionar un rango numérico.textInput() para ingresar texto.dateInput() para seleccionar una fecha.library(shiny)
cities <- unique(txhousing$city)
ui <- fluidPage(
selectizeInput(
inputId = "cities",
label = NULL,
choices = c("Please choose a city" = "", cities),
multiple = TRUE
),
plotlyOutput(outputId = "p")
)
server <- function(input, output, session) {
output$p <- renderPlotly({
req(input$cities)
if (identical(input$cities, "")) return(NULL)
p <- ggplot(data = filter(txhousing, city %in% input$cities)) +
geom_line(aes(date, median, group = city))
height <- session$clientData$output_p_height
width <- session$clientData$output_p_width
ggplotly(p, height = height, width = width)
})
}
shinyApp(ui, server)
dateRangeInput() para seleccionar un rango de
fechas.fileInput() para subir archivos.checkboxInput()/checkboxGroupInput()/radioButtons()
para elegir opciones.Nuestro enfoque ahora serÔ enlazar múltiples grÔficos en
shiny mediante la manipulación directa, centrÔndonos en el
uso de plotly y grƔficos estƔticos de R.
La función renderPlotly() renderiza objetos compatibles
con plotly_build(), incluyendo plot_ly(),
ggplotly() y grƔficos de ggplot2. TambiƩn
puede renderizar NULL como un div HTML vacĆo,
útil cuando no tiene sentido mostrar un grÔfico inmediatamente. Por
ejemplo, puedes mostrar un marcador de posición mientras se seleccionan
ciudades con selectizeInput(), y luego renderizar un
grƔfico interactivo con ggplotly().
AdemƔs, la salida de plotly puede depender del tamaƱo
del contenedor que la alberga. Por defecto, el grƔfico cambia de tamaƱo
Ćŗnicamente desde el lado del cliente, pero es posible re-ejecutar la
expresión reactiva al cambiar la ventana del navegador, mejorando el
comportamiento del grƔfico con ggplotly(). Esto debe usarse
con precaución con grandes conjuntos de datos o largos tiempos de
cÔlculo. La función req() garantiza que los datos estén
disponibles antes de realizar cƔlculos o acciones.
plotly permite acceder a eventos para informar a otras
vistas sobre interacciones. La función event_data() utiliza
un argumento fuente (ID) para determinar qué vista actúa
como fuente del evento. Cuando este ID coincide con el
grƔfico de plot_ly()/ggplotly(),
event_data() se asigna a esa vista especĆfica.
Por ejemplo, al hacer clic en una celda de un mapa de calor de
correlación, se genera un grÔfico de dispersión para analizar variables
en detalle. Los eventos vinculados a un evento plotly_click
contienen categorĆas relevantes como x, y y
z. Es crucial que el argumento fuente de
event_data() coincida con la fuente de
plot_ly() para evitar errores.
knitr::kable(head(mtcars))
| mpg | cyl | disp | hp | drat | wt | qsec | vs | am | gear | carb | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Mazda RX4 | 21.0 | 6 | 160 | 110 | 3.90 | 2.620 | 16.46 | 0 | 1 | 4 | 4 |
| Mazda RX4 Wag | 21.0 | 6 | 160 | 110 | 3.90 | 2.875 | 17.02 | 0 | 1 | 4 | 4 |
| Datsun 710 | 22.8 | 4 | 108 | 93 | 3.85 | 2.320 | 18.61 | 1 | 1 | 4 | 1 |
| Hornet 4 Drive | 21.4 | 6 | 258 | 110 | 3.08 | 3.215 | 19.44 | 1 | 0 | 3 | 1 |
| Hornet Sportabout | 18.7 | 8 | 360 | 175 | 3.15 | 3.440 | 17.02 | 0 | 0 | 3 | 2 |
| Valiant | 18.1 | 6 | 225 | 105 | 2.76 | 3.460 | 20.22 | 1 | 0 | 3 | 1 |
Este dataset de ejemplo (mtcars) puede usarse para
ilustrar cómo ajustar modelos lineales con la función lm
tras interactuar con grƔficos reactivos.
library(shiny)
# cache computation of the correlation matrix
correlation <- round(cor(mtcars), 3)
ui <- fluidPage(
plotlyOutput("heat"),
plotlyOutput("scatterplot")
)
server <- function(input, output, session) {
output$heat <- renderPlotly({
plot_ly(source = "heat_plot") %>%
add_heatmap(
x = names(mtcars),
y = names(mtcars),
z = correlation
)
})
output$scatterplot <- renderPlotly({
# if there is no click data, render nothing!
clickData <- event_data("plotly_click", source = "heat_plot")
if (is.null(clickData)) return(NULL)
# Obtain the clicked x/y variables and fit linear model
vars <- c(clickData[["x"]], clickData[["y"]])
d <- setNames(mtcars[vars], c("x", "y"))
yhat <- fitted(lm(y ~ x, data = d))
# scatterplot with fitted line
plot_ly(d, x = ~x) %>%
add_markers(y = ~y) %>%
add_lines(y = ~yhat) %>%
layout(
xaxis = list(title = clickData[["x"]]),
yaxis = list(title = clickData[["y"]]),
showlegend = FALSE
)
})
}
shinyApp(ui, server)
Una aplicación de tipo drill-down permite al usuario explorar datos en niveles mĆ”s especĆficos. Por ejemplo, un grĆ”fico circular puede mostrar ventas por categorĆa (Furniture, Office Supplies, Technology). Al hacer clic sobre una porción del grĆ”fico, se puede profundizar en las subcategorĆas de la categorĆa seleccionada.
La implementación requiere mantener el estado actual de la categorĆa
seleccionada utilizando un reactiveVal() y actualizar este
valor al hacer clic en una categorĆa o al pulsar un botón como āBackā.
La función reactive() se utiliza para envolver expresiones
normales creando asà una expresión reactiva cuyo resultado cambia
dinƔmicamente con el tiempo.
Este ejemplo utiliza el dataset sales para ilustrar cómo
implementar drill-down en aplicaciones Shiny con grƔficos
interactivos.
Un desglose bÔsico como este es útil por sà mismo, pero se vuelve aún
mÔs potente cuando se combina con múltiples vistas relacionadas de los
datos. Por ejemplo, se podrĆa ampliar la aplicación para mostrar las
ventas a lo largo del tiempo tanto por categorĆa como por subcategorĆa.
Manteniendo el estado con reactiveVal(), las diferentes
vistas pueden responder automĆ”ticamente segĆŗn la categorĆa seleccionada,
mostrando ventas generales cuando no hay categorĆa seleccionada y
detalles especĆficos cuando se elige una categorĆa particular.
library(shiny)
library(dplyr)
library(readr)
library(plotly)
library(purrr)
sales <- read_csv("https://plotly-r.com/data-raw/sales.csv")
## Rows: 9994 Columns: 5
## āā Column specification āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
## Delimiter: ","
## chr (3): id, category, sub_category
## dbl (1): sales
## dttm (1): order_date
##
## ā¹ Use `spec()` to retrieve the full column specification for this data.
## ā¹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
categories <- unique(sales$category)
ui <- fluidPage(
plotlyOutput("bar"),
uiOutput("back"),
plotlyOutput("time")
)
server <- function(input, output, session) {
current_category <- reactiveVal()
# report sales by category, unless a category is chosen
sales_data <- reactive({
if (!length(current_category())) {
return(count(sales, category, wt = sales))
}
sales %>%
filter(category %in% current_category()) %>%
count(sub_category, wt = sales)
})
# the pie chart
output$bar <- renderPlotly({
d <- setNames(sales_data(), c("x", "y"))
plot_ly(d) %>%
add_bars(x = ~x, y = ~y, color = ~x) %>%
layout(title = current_category() %||% "Total Sales")
})
# same as sales_data
sales_data_time <- reactive({
if (!length(current_category())) {
return(count(sales, category, order_date, wt = sales))
}
sales %>%
filter(category %in% current_category()) %>%
count(sub_category, order_date, wt = sales)
})
output$time <- renderPlotly({
d <- setNames(sales_data_time(), c("color", "x", "y"))
plot_ly(d) %>%
add_lines(x = ~x, y = ~y, color = ~color)
})
# update the current category when appropriate
observe({
cd <- event_data("plotly_click")$x
if (isTRUE(cd %in% categories)) current_category(cd)
})
# populate back button if category is chosen
output$back <- renderUI({
if (length(current_category()))
actionButton("clear", "Back", icon("chevron-left"))
})
# clear the chosen category on back button press
observeEvent(input$clear, current_category(NULL))
}
shinyApp(ui, server)
La plataforma shinyapps.io es ampliamente utilizada para
desplegar aplicaciones Shiny. Primero, debe crear una
cuenta en shinyapps.io, la cual
ofrece un plan gratuito limitado a 5 aplicaciones activas y hasta 25
horas activas mensuales. Si se requiere mayor uso, serĆ” necesario
cambiar a un plan pago.
RStudio y cree una nueva aplicación
Shiny.drill-down. De la misma
manera que cuando se abre un nuevo documento R Markdown, se
crea el código para una aplicación Shiny bÔsica. Ejecute la
aplicación haciendo clic en el botón Run App para ver el
resultado.Luego de esto hacemos click en ShinyApps.io para conectarnos a nuestra cuenta, creada anteriormente
ShinyApps.io. Con este fin, seguimos las
instrucciones que aparecen en la ventana; es decir, vamos a nuestra
cuenta, hacemos clic en nuestro nombre de usuario, luego presionamos el
botón Tokens, después hacemos clic en Show
para visualizar el token y finalmente clic en
Show secret/Copy to clipboard. Una vez realizado esto,
pegamos nuestro token en la ventana de publicación de nuestra app.
6. Luego le va a aparecer la siguiente ventana con la
opción de publicar su aplicación. Haga click en Publish
7. DespuƩs de varios
segundos (dependiendo del peso de su aplicación), la aplicación Shiny
deberĆa aparecer en su navegador de Internet. Para volver a desplegar la
aplicación puede hacerlo desde su aplicación haciendo click en el botón
resaltado en la imagen y deberĆ” visualizar su despliegue en la parte
inferior. Ahora puede realizar cambio y usar sólo ese botón cuando desde
hacer el despliegue de su aplicación.