#1 Configuración y carga de datos#
# 1. Cargar librerías
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.0 ✔ readr 2.1.6
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.2 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl)
# 2. Cargar el archivo y almacenarlo en la variable 'Datos'
# Usamos la ruta que ya validamos en tu equipo
Datos <- read_excel("C:/Users/ASUS/OneDrive/Escritorio/ESTADÍSTICA/EXPO/Variables/plantas_dep.xls",
sheet = "Fernando_Depuracion")
# 3. Verificar que se almacenó correctamente
str(Datos)
## tibble [58,812 × 29] (S3: tbl_df/tbl/data.frame)
## $ objectid : num [1:58812] 127 129 131 132 133 137 138 139 140 145 ...
## $ code : chr [1:58812] "00127-ARG-P" "00129-ARG-G" "00131-ARG-P" "00132-ARG-P" ...
## $ plant_name : chr [1:58812] "Aconcagua solar farm" "Altiplano 200 Solar Power Plant" "Anchoris solar farm" "Antu Newen solar farm" ...
## $ country : chr [1:58812] "Argentina" "Argentina" "Argentina" "Argentina" ...
## $ operational_status : chr [1:58812] "announced" "operating" "construction" "cancelled - inferred 4 y" ...
## $ longitude : num [1:58812] -68.9 -66.9 -68.9 -70.3 -66.8 ...
## $ latitude : num [1:58812] -33 -24.1 -33.3 -37.4 -28.6 ...
## $ elevation : num [1:58812] 929 4000 937 865 858 ...
## $ area : num [1:58812] 250 4397290 645 241 30 ...
## $ size : chr [1:58812] "Pequeña" "Grande" "Pequeña" "Pequeña" ...
## $ initial_letter_size : chr [1:58812] "P" "G" "P" "P" ...
## $ slope : num [1:58812] 0.574 1.603 0.903 1.791 1.872 ...
## $ slope_type : chr [1:58812] "Plano o casi plano" "Plano o casi plano" "Plano o casi plano" "Plano o casi plano" ...
## $ curvature : num [1:58812] 0.000795 -0.002781 0.002781 -0.002384 -0.009137 ...
## $ curvature_type : chr [1:58812] "Superficies planas o intermedias" "Superficies planas o intermedias" "Superficies planas o intermedias" "Superficies planas o intermedias" ...
## $ aspect : num [1:58812] 55.1 188.7 108.4 239.3 56.2 ...
## $ aspect_type : chr [1:58812] "Northeast" "South" "East" "Southwest" ...
## $ dist_to_road : num [1:58812] 127 56015 336 34 314 ...
## $ ambient_temperature : num [1:58812] 12.6 6.8 13.1 11.4 18.8 ...
## $ ghi : num [1:58812] 6.11 8.01 6.12 6.22 6.74 ...
## $ humidity : num [1:58812] 53.7 53.7 53.7 53.7 51.5 ...
## $ wind_speed : num [1:58812] 3.78 7.02 3.87 6.56 7.19 ...
## $ wind_direction : num [1:58812] 55.1 55.1 55.1 55.1 114.8 ...
## $ dt_wind : chr [1:58812] "Northeast" "Northeast" "Northeast" "Northeast" ...
## $ solar_aptitude : num [1:58812] 0.746 0.8 0.595 0.657 0.743 ...
## $ solar_aptittude_class: chr [1:58812] "Alta" "Alta" "Media" "Alta" ...
## $ capacity : num [1:58812] 25 101 180 20 50.4 ...
## $ optimal_tilt : num [1:58812] 31 26 31 33 30 31 29 31 27 32 ...
## $ pv_potential : num [1:58812] 4.98 6.39 4.97 5 5.37 ...
#2 Distribución de Frecuencias# #A continuación se presenta la tabla de distribución de frecuencias#
library(gt)
library(tidyverse)
#CÁLCULOS
Variable <- na.omit(Datos$slope[Datos$slope >= 0])
N <- length(Variable)
min_dec <- min(Variable)
max_dec <- max(Variable)
k_dec <- 9
rango_dec <- max_dec - min_dec
amplitud_dec <- rango_dec / k_dec
cortes_dec <- seq(min_dec, max_dec, length.out = k_dec + 1)
cortes_dec[length(cortes_dec)] <- max_dec + 0.0001 # Ajuste para incluir el máximo
inter_dec <- cut(Variable, breaks = cortes_dec, include.lowest = TRUE, right = FALSE)
ni_dec <- as.vector(table(inter_dec))
if(length(ni_dec) < k_dec) {
ni_dec <- c(ni_dec, rep(0, k_dec - length(ni_dec)))
}
hi_dec <- (ni_dec / N) * 100
Ni_asc_dec <- cumsum(ni_dec)
Hi_asc_dec <- cumsum(hi_dec)
Ni_desc_dec <- rev(cumsum(rev(ni_dec)))
Hi_desc_dec <- rev(cumsum(rev(hi_dec)))
TDF_Decimal <- data.frame(
Li = cortes_dec[1:k_dec],
Ls = cortes_dec[2:(k_dec+1)],
MC = (cortes_dec[1:k_dec] + cortes_dec[2:(k_dec+1)]) / 2,
ni = ni_dec,
hi = hi_dec,
Ni_asc = Ni_asc_dec,
Ni_desc = Ni_desc_dec,
Hi_asc = Hi_asc_dec,
Hi_desc = Hi_desc_dec
)
TDF_Dec_Final <- data.frame(
Li = as.character(round(TDF_Decimal$Li, 2)),
Ls = as.character(round(TDF_Decimal$Ls, 2)),
MC = as.character(round(TDF_Decimal$MC, 2)),
ni = as.character(TDF_Decimal$ni),
hi = as.character(round(TDF_Decimal$hi, 2))
)
totales_dec <- c("TOTAL", "-", "-", sum(TDF_Decimal$ni), round(sum(TDF_Decimal$hi), 2))
TDF_Dec_Final <- rbind(TDF_Dec_Final, totales_dec)
TDF_Dec_Final %>% gt() %>%
tab_header(title = md("**Tabla N°1 de Distribución de Frecuencias de Temperatura Máxima del Volcán Antisana (°C)**")) %>%
cols_label(Li="Lím. Inf", Ls="Lím. Sup", MC="Marca Clase (Xi)", ni="ni", hi="hi (%)") %>%
cols_align(align = "center") %>%
gt::tab_options(heading.title.font.size = px(14), column_labels.background.color = "#F0F0F0")
| Tabla N°1 de Distribución de Frecuencias de Temperatura Máxima del Volcán Antisana (°C) | ||||
| Lím. Inf | Lím. Sup | Marca Clase (Xi) | ni | hi (%) |
|---|---|---|---|---|
| 0 | 3.85 | 1.93 | 53322 | 90.67 |
| 3.85 | 7.71 | 5.78 | 3942 | 6.7 |
| 7.71 | 11.56 | 9.63 | 1028 | 1.75 |
| 11.56 | 15.41 | 13.49 | 313 | 0.53 |
| 15.41 | 19.27 | 17.34 | 119 | 0.2 |
| 19.27 | 23.12 | 21.19 | 42 | 0.07 |
| 23.12 | 26.98 | 25.05 | 33 | 0.06 |
| 26.98 | 30.83 | 28.9 | 4 | 0.01 |
| 30.83 | 34.68 | 32.76 | 3 | 0.01 |
| TOTAL | - | - | 58806 | 100 |
#3 Análisis Gráfico
#Esta sección presenta la visualización de la distribución de los datos
#3.1 Histogramas de Frecuencia
Variable <- na.omit(Datos$slope[Datos$slope >= 0])
N <- length(Variable)
k_int <- 9
min_int <- min(Variable)
max_int <- max(Variable)
cortes_int <- seq(from = min_int, to = max_int, length.out = k_int + 1)
cortes_int[length(cortes_int)] <- max_int + 0.0001
inter_int <- cut(Variable, breaks = cortes_int, include.lowest = TRUE, right = FALSE)
ni_int <- as.vector(table(inter_int))
MC_int <- (cortes_int[1:k_int] + cortes_int[2:(k_int+1)]) / 2
par(mar = c(8, 5, 4, 2)) # Márgenes amplios
bp <- barplot(ni_int,
names.arg = round(MC_int, 2),
main = "",
xlab = "",
ylab = "",
col = "#B0C4DE",
space = 0,
las = 2,
cex.axis = 0.6,
cex.names = 0.6)
mtext("Frecuencia Absoluta", side = 2, line = 3.5, cex = 0.7)
mtext("Pendiente del Terreno (Grados)", side = 1, line = 4, cex = 0.7)
mtext("Gráfica N°1: Distribución de Plantas Solares por Pendiente del Terreno",
side = 3, line = 2, adj = 0.5, cex = 0.8, font = 2)
##4 Estratificación y Validación del Modelo
##1 Justificación del Modelo Global #Al observar el Histograma General (Gráfico Nº1), se detecta un comportamiento de asimetría positiva extrema. Dado que la muestra es unimodal y se concentra en valores bajos, se opta por un modelo único para garantizar el ajuste.Modelo Global: Pendiente (Slope) \(\ge\) 0° -> Modelo Log-Normal Estándar.
#5 Análisis del Modelo (Log-Normal Estándar)Para ajustar la Log-Normal a datos con alta densidad en el origen, aplicamos una traslación de seguridad para evitar indeterminaciones en 0°: \(Y = X + 0.001\)
Variable <- na.omit(Datos$slope[Datos$slope >= 0])
Var_Model <- Variable[Variable > 0]
# CÁLCULO DE PARÁMETROS DEL MODELO (Log-Normal)
meanlog_tot <- mean(log(Var_Model))
sdlog_tot <- sd(log(Var_Model))
N <- length(Variable)
k_int <- 9
min_int <- min(Variable)
max_int <- max(Variable)
cortes_int <- seq(from = min_int, to = max_int, length.out = k_int + 1)
cortes_int[length(cortes_int)] <- max_int + 0.0001
inter_int <- cut(Variable, breaks = cortes_int, include.lowest = TRUE, right = FALSE)
ni_int <- as.vector(table(inter_int))
MC_int <- (cortes_int[1:k_int] + cortes_int[2:(k_int+1)]) / 2
par(mar = c(8, 5, 4, 2))
bp <- barplot(ni_int,
names.arg = round(MC_int, 2),
main = "",
xlab = "",
ylab = "",
col = "#B0C4DE",
space = 0,
las = 2,
cex.axis = 0.6,
cex.names = 0.6,
ylim = c(0, max(ni_int) * 1.3))
x_teorico <- seq(min_int, max_int, length.out = 200)
ancho <- cortes_int[2] - cortes_int[1]
y_teorico <- dlnorm(x_teorico, meanlog_tot, sdlog_tot) * N * ancho
x_plot <- (x_teorico - min_int) / (max_int - min_int) * k_int
lines(x_plot, y_teorico, col = "#922B21", lwd = 3)
mtext("Frecuencia Absoluta", side = 2, line = 3.5, cex = 0.7)
mtext("Pendiente del Terreno (Grados)", side = 1, line = 4, cex = 0.7)
mtext("Gráfica N°2: Conjetura del Modelo (Log-Normal)",
side = 3, line = 2, adj = 0.5, cex = 0.8, font = 2)
legend("topright",
legend = c("Datos (Histograma)", "Modelo Log-Normal"),
fill = c("#B0C4DE", NA),
border = c("black", NA),
col = c(NA, "#922B21"),
lty = c(NA, 1),
lwd = c(NA, 3),
bty = "n",
cex = 0.7)
library(gt)
library(tidyverse)
Variable <- na.omit(Datos$slope[Datos$slope > 0])
meanlog_tot <- mean(log(Variable))
sdlog_tot <- sd(log(Variable))
k_int <- 9
min_val <- min(Variable); max_val <- max(Variable)
breaks_tot <- seq(min_val, max_val, length.out = k_int + 1)
breaks_tot[length(breaks_tot)] <- max_val + 0.0001
inter_tot <- cut(Variable, breaks = breaks_tot, include.lowest = TRUE, right = FALSE)
ni_tot <- as.vector(table(inter_tot))
probs_tot <- numeric(k_int)
for(i in 1:k_int) {
probs_tot[i] <- plnorm(breaks_tot[i+1], meanlog_tot, sdlog_tot) -
plnorm(breaks_tot[i], meanlog_tot, sdlog_tot)
}
probs_tot <- probs_tot / sum(probs_tot)
n_base <- 100
Fo_tot <- ni_tot * (n_base / sum(ni_tot))
Fe_tot <- probs_tot * n_base
chi2 <- sum((Fo_tot - Fe_tot)^2 / Fe_tot)
gl <- k_int - 1 - 2
crit2 <- qchisq(0.99, gl)
if(is.na(crit2) || crit2 < 0) crit2 <- 3.84
res2 <- if(chi2 < crit2) "APROBADO" else "RECHAZADO (Ajuste Visual)"
pear2 <- cor(Fo_tot, Fe_tot) * 100
data.frame(
Indicador = c("Prueba Chi-Cuadrado", "Correlación de Pearson"),
Valor = c(paste(round(chi2, 2), "<", round(crit2, 2)),
paste0(round(pear2, 2), "%")),
Conclusion = c(res2, "Nivel de Ajuste")
) %>%
gt() %>%
tab_header(
title = md("**Tabla N°3: Resultados de la Validación Estadística del Modelo (Pendiente)**")
) %>%
cols_align(align = "center") %>%
gt::tab_options(
heading.title.font.size = 14,
column_labels.background.color = "#F0F0F0"
)
| Tabla N°3: Resultados de la Validación Estadística del Modelo (Pendiente) | ||
| Indicador | Valor | Conclusion |
|---|---|---|
| Prueba Chi-Cuadrado | 0.47 < 16.81 | APROBADO |
| Correlación de Pearson | 100% | Nivel de Ajuste |
#8 Cálculo de Probabilidades y Toma de Decisiones
#Dado que hemos validado el comportamiento global de los datos mediante el modelo Log-Normal, utilizaremos los parámetros calculados (\(\mu_{log}\) y \(\sigma_{log}\)) para la toma de decisiones técnicas y operativas sobre la ubicación de las plantas solares.
#Pregunta 1 (Zona de Aptitud Óptima): ¿Cuál es la probabilidad de que un terreno elegido al azar presente una pendiente “ideal” para la construcción (entre 0° y 3°)?
#Pregunta 2 (Riesgo de Costo de Obra Civil): Si se planifica la expansión de 50 nuevas plantas, ¿cuántas se estima que se ubicarán en terrenos con pendientes superiores a 10°, donde los costos de nivelación son significativamente más altos?
# DATOS Y PARÁMETROS (Modelo Log-Normal Validado)
Variable <- na.omit(Datos$slope[Datos$slope > 0])
mean_gl <- mean(log(Variable))
sd_gl <- sd(log(Variable))
# CÁLCULOS DE PROBABILIDAD (Escenarios)
# Escenario 1: Zona Óptima (Ej: 0 a 10 grados de pendiente)
x1 <- 0
x2 <- 10
# Usamos plnorm porque el modelo es Log-Normal
prob_ventana <- plnorm(x2, meanlog = mean_gl, sdlog = sd_gl) -
plnorm(x1, meanlog = mean_gl, sdlog = sd_gl)
pct_ventana <- round(prob_ventana * 100, 2)
# Escenario 2: Límite Crítico (Ej: > 20 grados)
limite_pendiente <- 20
prob_critica <- 1 - plnorm(limite_pendiente, meanlog = mean_gl, sdlog = sd_gl)
pct_critica <- round(prob_critica * 100, 2)
col_ejes <- "#2E4053"
col_rojo <- "#C0392B"
col_azul_claro <- rgb(0.2, 0.6, 0.8, 0.5)
par(mar = c(6, 6, 4, 2))
curve(dlnorm(x, meanlog = mean_gl, sdlog = sd_gl),
from = 0,
to = max(Variable) * 1.2,
main = "",
xlab = "",
ylab = "",
col = col_ejes,
lwd = 2,
las = 1,
xaxt = "n",
yaxt = "n")
x_fill <- seq(x1, x2, length.out = 100)
# Calculamos sus alturas Y con dlnorm
y_fill <- dlnorm(x_fill, meanlog = mean_gl, sdlog = sd_gl)
# Dibujamos el polígono
polygon(c(x1, x_fill, x2), c(0, y_fill, 0), col = col_azul_claro, border = NA)
abline(v = limite_pendiente, col = col_rojo, lwd = 2, lty = 2)
axis(1, las = 1, cex.axis = 0.8)
axis(2, las = 1, cex.axis = 0.8)
mtext("Densidad de Probabilidad", side = 2, line = 4, cex = 0.9)
mtext("Pendiente del Terreno (Grados)", side = 1, line = 4, cex = 0.9)
mtext("Gráfica N°3: Proyección de Escenarios (Modelo Global)",
side = 3, line = 1.5, adj = 0.5, cex = 1.0, font = 2)
legend("topright",
legend = c("Distribución Log-Normal",
paste0("Zona Óptima (", x1, "-", x2, "°)"),
paste0("Zona Crítica (> ", limite_pendiente, "°)")),
col = c(col_ejes, col_azul_claro, col_rojo),
lwd = c(2, 10, 2),
pch = c(NA, 15, NA),
lty = c(1, 1, 2),
bty = "n",
cex = 0.8)
grid(nx = NULL, ny = NULL, col = "gray90", lty = "dotted")
library(gt)
library(tidyverse)
# CÁLCULOS PREVIOS NECESARIOS
n_proyectos <- N
cant_estimada <- round(prob_critica * n_proyectos)
Tabla_Probabilidades <- data.frame(
Escenario = c(
paste0("Zona de Aptitud Óptima (", x1, "° a ", x2, "°)"),
paste0("Riesgo de Obra Civil (> ", limite_pendiente, "°)")
),
Porcentaje = c(
paste0(pct_ventana, " %"),
paste0(pct_critica, " %")
),
Estimacion_Proyectos = c(
"-", # En la zona óptima no ponemos conteo de riesgo
paste0(cant_estimada, " plantas (de ", n_proyectos, ")")
)
)
Tabla_Probabilidades %>%
gt() %>%
tab_header(
title = md("**Tabla N°4: Análisis de Probabilidades y Toma de Decisiones (Slope)**")
) %>%
cols_label(
Escenario = "Escenario Analizado",
Porcentaje = "Probabilidad (%)",
Estimacion_Proyectos = "Estimación (N° Plantas)"
) %>%
cols_align(align = "center") %>%
gt::tab_options(
heading.title.font.size = 14,
column_labels.background.color = "#F0F0F0"
)
| Tabla N°4: Análisis de Probabilidades y Toma de Decisiones (Slope) | ||
| Escenario Analizado | Probabilidad (%) | Estimación (N° Plantas) |
|---|---|---|
| Zona de Aptitud Óptima (0° a 10°) | 97.67 % | - |
| Riesgo de Obra Civil (> 20°) | 0.63 % | 371 plantas (de 58806) |
#Respuestas Gerenciales (Análisis de Decisiones)
#Existe una probabilidad del r pct_ventana% de que la pendiente de los terrenos se mantenga en la zona de factibilidad óptima (0° a 3°), lo que garantiza costos mínimos de nivelación.Se estima que en un lote de 50 proyectos, aproximadamente r cant_estimada plantas (un r pct_riesgo%) superarán el límite de pendiente de 10°, por lo que se recomienda asignar un presupuesto de contingencia para movimientos de tierra y estabilización de taludes.
#Teorema del Límite CentralEl Teorema del Límite Central (TLC) establece que, dada una muestra suficientemente grande (\(n > 30\)), la distribución de las medias muestrales seguirá una distribución Normal, independientemente de la distribución original de la variable.En nuestro caso, con 58,806 registros, el cumplimiento del TLC es absoluto, lo que nos permite validar las inferencias realizadas. Los postulados de confianza empírica sugieren:\(P(\bar{x} - E < \mu < \bar{x} + E) \approx 68\%\)\(P(\bar{x} - 2E < \mu < \bar{x} + 2E) \approx 95\%\)\(P(\bar{x} - 3E < \mu < \bar{x} + 3E) \approx 99\%\)Donde el Margen de Error (E) se define como: # \[E = \frac{\sigma}{\sqrt{n}}\]
library(gt)
library(tidyverse)
Variable <- na.omit(Datos$slope[Datos$slope >= 0])
stats_global <- boxplot.stats(Variable)$stats
Variable_TLC <- Variable[Variable >= stats_global[1] & Variable <= stats_global[5]]
# Cálculos del Teorema del Límite Central
x_bar <- mean(Variable_TLC)
sigma_muestral <- sd(Variable_TLC)
n_tlc <- length(Variable_TLC)
error_est <- sigma_muestral / sqrt(n_tlc)
margen_error_95 <- 2 * error_est # Aproximación empírica del 95%
lim_inf_tlc <- x_bar - margen_error_95
lim_sup_tlc <- x_bar + margen_error_95
# TABLA FINAL
data.frame(
Parametro = "Pendiente Promedio", # <--- AQUÍ ADAPTAMOS EL NOMBRE
Lim_Inferior = lim_inf_tlc,
Media_Muestral = x_bar,
Lim_Superior = lim_sup_tlc,
Error_Estandar = paste0("+/- ", sprintf("%.2f", margen_error_95)),
Confianza = "95% (2*SE)"
) %>%
gt() %>%
tab_header(
title = md("**Tabla N°5: Estimación de la Media Poblacional (Slope)**"),
subtitle = "Aplicación del Teorema del Límite Central"
) %>%
fmt_number(
columns = c(Lim_Inferior, Media_Muestral, Lim_Superior),
decimals = 2
) %>%
cols_align(align = "center") %>%
tab_options(
heading.title.font.size = px(14),
column_labels.background.color = "#F0F0F0"
)
| Tabla N°5: Estimación de la Media Poblacional (Slope) | |||||
| Aplicación del Teorema del Límite Central | |||||
| Parametro | Lim_Inferior | Media_Muestral | Lim_Superior | Error_Estandar | Confianza |
|---|---|---|---|---|---|
| Pendiente Promedio | 0.87 | 0.87 | 0.88 | +/- 0.01 | 95% (2*SE) |
#Conclusión de Modelamiento y Estimación (Slope)
#La variable Pendiente del Terreno (Slope) presenta un comportamiento asimétrico con sesgo a la derecha, modelado de forma integral por un Régimen Log-Normal Estándar Global (\(meanlog \approx\) r round(meanlog, 4)). Gracias a la precisión de este ajuste (Pearson = 100%) y al respaldo del Teorema del Límite Central, podemos afirmar con un 95% de confianza que la media aritmética poblacional de la pendiente se encuentra en el intervalo de \(\mu \in\) [r round(lim_inf_tlc, 2); r round(lim_sup_tlc, 2)]. Esto significa que el promedio real de inclinación para estos proyectos es de r round(x_bar, 2) ± r round(margen_error_95, 2)°, con una desviación estándar que refleja una alta concentración de datos en zonas de llanura, validando la estabilidad técnica de las ubicaciones seleccionadas para las plantas solares.