Carga y Limpieza de Datos

# Instalación de librerías 
 
if (!require("ggplot2")) install.packages("ggplot2")
if (!require("scales")) install.packages("scales")
if (!require("e1071")) install.packages("e1071")
if (!require("knitr")) install.packages("knitr")


library(ggplot2)
library(scales)
library(e1071)
library(knitr)


# setwd("C:/Users/ronal/OneDrive/Desktop") # Ajusta según tu necesidad

# Carga de datos
datos <- read.csv("database (1).csv", header = TRUE, sep = ",", dec = ".")

# Extracción y limpieza
All.Costs <- na.omit(datos$All.Costs)
All.Costs <- All.Costs[All.Costs > 0]

Tabla de Distribución de Frecuencias

xmin <- min(All.Costs)
xmax <- max(All.Costs)
R <- xmax - xmin
K <- floor(1 + 3.3 * log10(length(All.Costs)))
A <- R / K

# Límites y Marcas de Clase
Li <- round(seq(from = xmin, to = xmax - A, by = A), 2)
Ls <- round(seq(from = xmin + A, to = xmax, by = A), 2)
MC <- round((Li + Ls) / 2)

# Frecuencias
ni <- numeric(K)
for (i in 1:(K-1)) {
  ni[i] <- sum(All.Costs >= Li[i] & All.Costs < Ls[i])
}
ni[K] <- sum(All.Costs >= Li[K] & All.Costs <= xmax)

hi <- ni / sum(ni) * 100
Ni_asc <- cumsum(ni)
Ni_desc <- rev(cumsum(rev(ni)))
Hi_asc <- cumsum(hi)
Hi_desc <- rev(cumsum(rev(hi)))

TDF <- data.frame(Li, Ls, MC, ni, hi_porc = round(hi, 2), Ni_asc, Ni_desc, 
                  Hi_asc_porc = round(Hi_asc, 2), Hi_desc_porc = round(Hi_desc, 2))


knitr::kable(TDF, caption = "Tabla No. 1: Tabla de Distribución de Frecuencias de costos totales")
Tabla No. 1: Tabla de Distribución de Frecuencias de costos totales
Li Ls MC ni hi_porc Ni_asc Ni_desc Hi_asc_porc Hi_desc_porc
1 70043844 35021923 2757 99.86 2757 2761 99.86 100.00
70043844 140087687 105065766 2 0.07 2759 4 99.93 0.14
140087687 210131530 175109609 1 0.04 2760 2 99.96 0.07
210131530 280175373 245153452 0 0.00 2760 1 99.96 0.04
280175373 350219216 315197295 0 0.00 2760 1 99.96 0.04
350219216 420263060 385241138 0 0.00 2760 1 99.96 0.04
420263060 490306903 455284981 0 0.00 2760 1 99.96 0.04
490306903 560350746 525328824 0 0.00 2760 1 99.96 0.04
560350746 630394589 595372667 0 0.00 2760 1 99.96 0.04
630394589 700438432 665416510 0 0.00 2760 1 99.96 0.04
700438432 770482275 735460353 0 0.00 2760 1 99.96 0.04
770482275 840526118 805504196 1 0.04 2761 1 100.00 0.04

Nueva tabla de Frecuencia

### Se seleccionó el primer intervalo de la variable costos totales para el análisis, debido a que en este se concentra la mayorcantidad de datos. Esta elección permite construir la tabla de frecuencia y  gráficas más claras y legibles, facilitando la interpretación de la distribución de los datos y evitando distorsiones visuales provocadas por intervalos con baja frecuencia. ###

umbral_90 <- quantile(All.Costs, 0.90)
datos_zoom <- All.Costs[All.Costs <= umbral_90]

n_z <- length(datos_zoom)
xmin_z <- min(datos_zoom)
xmax_z <- max(datos_zoom)
K_z <- floor(1 + 3.322 * log10(n_z))
A_z <- (xmax_z - xmin_z) / K_z

cortes_z <- seq(xmin_z, xmin_z + (K_z * A_z), by = A_z)
Li_z <- cortes_z[1:K_z]
Ls_z <- cortes_z[2:(K_z + 1)]
MC_z <- (Li_z + Ls_z) / 2

ni_z <- numeric(K_z)
for (i in 1:(K_z - 1)) {
  ni_z[i] <- sum(datos_zoom >= Li_z[i] & datos_zoom < Ls_z[i])
}
ni_z[K_z] <- sum(datos_zoom >= Li_z[K_z] & datos_zoom <= xmax_z)

hi_z <- (ni_z / n_z) * 100
Ni_asc_z <- cumsum(ni_z)
Ni_desc_z <- rev(cumsum(rev(ni_z)))
Hi_asc_z <- cumsum(hi_z)
Hi_desc_z <- rev(cumsum(rev(hi_z)))

TDF_final_zoom <- data.frame(
  Li = round(Li_z, 2), Ls = round(Ls_z, 2), MC = round(MC_z, 2),
  ni = ni_z, hi_porc = round(hi_z, 2), Ni_asc = Ni_asc_z, 
  Ni_desc = Ni_desc_z, Hi_asc_porc = round(Hi_asc_z, 2), Hi_desc_porc = round(Hi_desc_z, 2)
)

kable(TDF_final_zoom, caption = "Tabla No. 2: Tabla de Distribución de Frecuencias")
Tabla No. 2: Tabla de Distribución de Frecuencias
Li Ls MC ni hi_porc Ni_asc Ni_desc Hi_asc_porc Hi_desc_porc
1.00 43809.25 21905.12 1695 68.21 1695 2485 68.21 100.00
43809.25 87617.50 65713.38 286 11.51 1981 790 79.72 31.79
87617.50 131425.75 109521.62 120 4.83 2101 504 84.55 20.28
131425.75 175234.00 153329.88 103 4.14 2204 384 88.69 15.45
175234.00 219042.25 197138.12 68 2.74 2272 281 91.43 11.31
219042.25 262850.50 240946.38 50 2.01 2322 213 93.44 8.57
262850.50 306658.75 284754.62 44 1.77 2366 163 95.21 6.56
306658.75 350467.00 328562.88 23 0.93 2389 119 96.14 4.79
350467.00 394275.25 372371.12 31 1.25 2420 96 97.38 3.86
394275.25 438083.50 416179.38 24 0.97 2444 65 98.35 2.62
438083.50 481891.75 459987.62 15 0.60 2459 41 98.95 1.65
481891.75 525700.00 503795.88 26 1.05 2485 26 100.00 1.05

Graficas de Histogramas

Gráfica No. 1: Histograma de Frecuencia Absoluta

#Gráfica No. 1: Distribución global de costos totales 

options(scipen = 999) 

hist(datos_zoom, 
     main = "Gráfica No. 1: Distribución de Costos Totales",
     ylab = "Cantidad", 
     xlab = "Costos Totales", 
     col = "grey",           
     breaks = K_z,           
     ylim = c(0, n_z),       
     las = 2)

Gráfica No. 2: Histograma Local de Costos Totales

options(scipen = 999) 

hist(datos_zoom, 
     main = "Gráfica No. 2:Costos Totales",
     xlab = "Costos Totales", 
     ylab = "Cantidad", 
     col = "grey",          
     breaks = K_z,          
     las = 2)

Gráfica 3. Porcentaje global de Costos Totales

p_hi_global <- ggplot(TDF, aes(x = MC, y = hi_porc)) +
  geom_col(fill = "gray70", color = "black", alpha = 0.7, width = A) +
  scale_x_continuous(labels = comma) + 
  scale_y_continuous(labels = function(x) paste0(x, "%"), limits = c(0, 100)) + 
  labs(title = "Gráfica N. 3 : Porcentaje de Costos Totales", x = "Costos Totales", y = "Porcentaje (%)") +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", hjust = 0.5),
        panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        axis.line = element_line(color = "black"))

print(p_hi_global)

Gráfica 4. Distribucion de Costos Totales

limite_max <- max(TDF_final_zoom$Ls)
quiebres_x <- seq(0, limite_max + 100000, by = 100000)

p_hi_local <- ggplot(TDF_final_zoom, aes(x = MC, y = hi_porc)) +
  geom_col(fill = "steelblue", color = "black", alpha = 0.8, width = A_z) +
  
  scale_x_continuous(breaks = quiebres_x, 
                     labels = comma) + 
  
  scale_y_continuous(labels = function(x) paste0(x, "%"), 
                     expand = expansion(mult = c(0, 0.05))) +
  
  labs(title = "Gráfica N. 4: Distribución de los Costos Totales", 
       subtitle = "Frecuencia relativa agrupada por intervalos de clase",
       x = "Costos Totales (USD)", 
       y = "Porcentaje (%)") +
  
  # Estética profesional
  theme_classic() +
  theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 13),
        axis.text.x = element_text(angle = 45, hjust = 1, size = 9),
        panel.grid.major.y = element_line(color = "gray90")) # Añade líneas guía horizontales

print(p_hi_local)

Análisis de Ojivas descententes y ascendentes

Ojivas Ni

# Ojivas Ni
library(ggplot2)
library(scales)

eje_x <- c(TDF_final_zoom$Li[1], TDF_final_zoom$Ls)
ni_asc_alineado <- c(0, TDF_final_zoom$Ni_asc)
ni_desc_ajustado <- c(TDF_final_zoom$Ni_asc[nrow(TDF_final_zoom)], TDF_final_zoom$Ni_desc)

df_ni_final <- data.frame(x = eje_x, y_asc = ni_asc_alineado, y_desc = ni_desc_ajustado)

# 2. Construcción de la Gráfica
ggplot(df_ni_final) +
  # Ojiva Ascendente 
  geom_line(aes(x = x, y = y_asc, color = "Ascendente"), linewidth = 1.2) +
  geom_point(aes(x = x, y = y_asc, color = "Ascendente"), size = 3) +
  
  # Ojiva Descendente
  geom_line(aes(x = x, y = y_desc, color = "Descendente"), linewidth = 1.2, linetype = "solid") +
  geom_point(aes(x = x, y = y_desc, color = "Descendente"), size = 3) +
  

  scale_x_continuous(labels = comma) + 
  scale_y_continuous(labels = comma) +
  scale_color_manual(values = c("Ascendente" = "blue4", "Descendente" = "red3")) +
  
  labs(title = "Gráfica 5: Ojivas de Frecuencia Absoluta", 
       subtitle = "Comparativa de distribución acumulada ascendente y descendente",
       x = "Costos Totales", 
       y = "Cantidad Acumulada (Ni)",
       color = "Tipo de Ojiva") +
  theme_bw() +
  theme(legend.position = "bottom",
        plot.title = element_text(face = "bold", size = 14))

Ojivas Hi

library(ggplot2)
library(scales)

hi_asc_alineado <- c(0, TDF_final_zoom$Hi_asc_porc)
hi_desc_alineado <- c(100, TDF_final_zoom$Hi_desc_porc)

df_hi_final <- data.frame(x = eje_x, y_asc = hi_asc_alineado, y_desc = hi_desc_alineado)

# 2. Construcción de la Gráfica
ggplot(df_hi_final) +
  # Ojiva Ascendente
  geom_line(aes(x = x, y = y_asc, color = "Ascendente"), linewidth = 1.2) +
  geom_point(aes(x = x, y = y_asc, color = "Ascendente"), size = 3) +
  
  # Ojiva Descendente 
  geom_line(aes(x = x, y = y_desc, color = "Descendente"), linewidth = 1.2, linetype = "solid") +
  geom_point(aes(x = x, y = y_desc, color = "Descendente"), size = 3) +
  
  scale_x_continuous(labels = comma) +
  scale_y_continuous(labels = function(x) paste0(x, "%"), limits = c(0, 100)) +
  
  scale_color_manual(values = c("Ascendente" = "blue4", "Descendente" = "red3")) +
  
  labs(title = "Gráfica 6: Ojivas de Frecuencia Relativa", 
       subtitle = "Distribución porcentual acumulada",
       x = "Costos Totales", 
       y = "Porcentaje Acumulado (%)",
       color = "Tipo de Ojiva") +
  theme_bw() + 
  theme(legend.position = "bottom",
        plot.title = element_text(face = "bold", size = 14))

Diagrama de Caja (Boxplot)

boxplot(datos_zoom, horizontal = TRUE, col = "gray", 
        main = "Gráfica No. 7: Distribución de Costos Totales",
        xlab = "Costo Total", las = 1, pch = 19, outcol = "black")
axis(1, at = pretty(range(datos_zoom)), labels = format(pretty(range(datos_zoom)), big.mark = ","))

Indicadores Estadísticos

library(knitr)
library(moments)

datos_analisis <- datos_zoom[!is.na(datos_zoom) & datos_zoom > 0]
n <- length(datos_analisis)

min_real <- min(datos_analisis) 
max_real <- max(datos_analisis)
k <- ceiling(1 + 3.322 * log10(n))

cortes <- seq(min_real, max_real, length.out = k + 1)

intervalos_f <- cut(datos_analisis, 
                    breaks = cortes, 
                    include.lowest = TRUE, 
                    right = FALSE, 
                    dig.lab = 10)

tab_freq <- as.data.frame(table(intervalos_f))

pos_modal <- which.max(tab_freq$Freq)
moda_raw <- as.character(tab_freq$intervalos_f[pos_modal])

moda_limpia <- gsub("\\[|\\]|\\)", "", moda_raw)
moda_limpia <- gsub(",", " a ", moda_limpia)

media_val <- mean(datos_analisis)
mediana_val <- median(datos_analisis)
S_val <- sd(datos_analisis)
CV_val <- (S_val / media_val) * 100
As_val <- skewness(datos_analisis)
K_val <- kurtosis(datos_analisis)

# 6. Tabla Final
Tabla_Resultados <- data.frame(
  Variable = "Costos Totales",
  Mínimo = round(min_real, 2),
  Máximo = round(max_real, 2),
  Media = round(media_val, 2),
  Mediana = round(mediana_val, 2),
  Moda = moda_limpia, # Empezará en 524
  SD = round(S_val, 2),
  CV = paste0(round(CV_val, 2), "%"),
  As = round(As_val, 2),
  K = round(K_val, 2)
)

kable(Tabla_Resultados, format = "markdown", align = "c",
      caption = "Tabla 1: Indicadores Estadísticos (Moda anclada al mínimo real).")
Tabla 1: Indicadores Estadísticos (Moda anclada al mínimo real).
Variable Mínimo Máximo Media Mediana Moda SD CV As K
Costos Totales 1 525700 62015.52 18757 1 a 40439.38462 102072.4 164.59% 2.46 8.9

Tabla de indicadores estadisticos

Tabla_Resultados <- data.frame(
  Variable = "Costos Totales",
  Mínimo = round(min_real, 2),
  Máximo = round(max_real, 2),
  Media = round(media_val, 2),
  Mediana = round(mediana_val, 2),
  Moda = moda_limpia, 
  SD = round(S_val, 2),
  CV = paste0(round(CV_val, 2), "%"),
  As = round(As_val, 2),
  K = round(K_val, 2)
)

kable(Tabla_Resultados, format = "markdown", align = "c",
      caption = "Tabla 1: Indicadores Estadísticos")
Tabla 1: Indicadores Estadísticos
Variable Mínimo Máximo Media Mediana Moda SD CV As K
Costos Totales 1 525700 62015.52 18757 1 a 40439.38462 102072.4 164.59% 2.46 8.9
###IMPORTANTE##

## Se determinó que la moda se encuentra en el intervalo inicial (aproximadamente 1 a más de 40000) debido a la alta concentración de frecuencias en los valores inferiores de la muestra. Aunque el valor mínimo registrado es de $524, la metodología de intervalos de clase agrupa estos registros dentro de una primera categoría de mayor densidad.

Análisis de valores Atípicos

stats_outliers <- boxplot.stats(datos_zoom)$out
num_outliers <- length(stats_outliers)
cat("\nCantidad de valores atípicos detectados:", num_outliers, "\n")
## 
## Cantidad de valores atípicos detectados: 354

Conclusiones

“La variable ‘Costos Totales’ fluctúa entre 1 USD hasta 525700 USD, y sus valores se sitúan al rededor de 62015.52 USD, con una desviación estándar de 1027204 USD, siendo esta una variable muy heterogénea, cuyos valores se concentran en el intervalo inicial , lo que evidencia una asimetría positiva marcada por la presencia de valores atípicos (354 outliers).Por todo esto la variable presenta un comportamiento impredecible y volátil, lo cual resulta perjudicial.”