1 Configuración y Carga de Datos

##### UNIVERSIDAD CENTRAL DEL ECUADOR #####
#### AUTOR: MARTIN SARMIENTO ####
### CARRERA: INGENIERÍA EN PETRÓLEOS #####


#### VARIABLE INCLINACIÓN ÓPTIMA ####
## DATASET ##
setwd("~/R/OPTIMAL_TILT")
# Cargar dataset
Datos <- read.csv("Dataset_Mundial_Final.csv", sep = ";", dec = ",", fileEncoding = "latin1")
# Estructura de los datos
str(Datos)
## 'data.frame':    58978 obs. of  29 variables:
##  $ ï..OBJECTID           : int  2 3 4 5 6 7 8 9 10 11 ...
##  $ code                  : chr  "00001-AFG-P" "00002-AFG-P" "00003-AFG-P" "00004-AFG-P" ...
##  $ plant_name            : chr  "Badghis Solar Power Plant" "Balkh solar farm" "Behsood solar farm" "Dab Pal 4 solar farm" ...
##  $ country               : chr  "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
##  $ operational_status    : chr  "cancelled - inferred 4 y" "cancelled - inferred 4 y" "cancelled - inferred 4 y" "shelved - inferred 2 y" ...
##  $ longitude             : num  62.9 67.1 70.4 66.2 65.7 ...
##  $ latitude              : num  35.1 36.7 34.4 33.8 31.7 ...
##  $ elevation             : int  918 359 629 2288 1060 1060 1392 398 410 1012 ...
##  $ area                  : num  6.74 10.72 487.73 111.8 1929.96 ...
##  $ size                  : chr  "Small" "Small" "Small" "Small" ...
##  $ slope                 : num  7.38 0.49 1.1 6.16 1.23 ...
##  $ slope_type            : chr  "Moderado" "Plano o casi plano" "Plano o casi plano" "Moderado" ...
##  $ curvature             : num  -0.024 0 0 0.045 -0.005 -0.005 -0.015 0 0 -0.009 ...
##  $ curvature_type        : chr  "Superficies cóncavas / Valles" "Superficies planas o intermedias" "Superficies planas o intermedias" "Superficies convexas / Crestas" ...
##  $ aspect                : num  96.8 358.5 36.2 305.8 248.4 ...
##  $ aspect_type           : chr  "East" "North" "Northeast" "Northwest" ...
##  $ dist_to_road          : num  7037.1 92.7 112.1 1705.3 115.8 ...
##  $ ambient_temperature   : num  14.4 17.88 21.32 8.86 19.64 ...
##  $ ghi                   : num  5.82 5.58 5.8 6.75 6.62 ...
##  $ humidity              : num  47.7 42.3 36.4 37.3 24.2 ...
##  $ wind_speed            : num  0.039 0.954 0.234 0.943 0.37 ...
##  $ wind_direction        : num  187.5 207.4 255.6 160.3 97.7 ...
##  $ dt_wind               : chr  "South" "Southwest" "West" "South" ...
##  $ solar_aptitude        : num  0.72 0.635 0.685 0.659 0.819 0.819 0.818 0.642 0.63 0.374 ...
##  $ solar_aptitude_rounded: int  7 6 7 7 8 8 8 6 6 4 ...
##  $ solar_aptittude_class : chr  "Alta" "Alta" "Alta" "Alta" ...
##  $ capacity              : num  32 40 60 3000 100 100 36 50 25 100 ...
##  $ optimal_tilt          : num  30 31 31.1 33 31 ...
##  $ pv_potential          : num  4.61 4.41 4.57 5.42 5.17 ...
# Cargamos las librerias
library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(gt)
library(e1071)

2 Cálculo de Intervalos y Frecuencias

#Extraer variable
Variable <- na.omit(Datos$optimal_tilt)
N <- length(Variable)

# Cálculo Límites Decimales #
# Cálculos básicos
min_dec <- min(Variable)
max_dec <- max(Variable)
k_dec <- floor(1 + 3.322 * log10(N))
rango_dec <- max(Variable) - min(Variable)
amplitud_dec <- rango_dec / k_dec

# Generamos los cortes exactos
cortes_dec <- seq(min(Variable), max(Variable), length.out = k_dec + 1)
cortes_dec[length(cortes_dec)] <- max(Variable) + 0.0001

# Frecuencias
inter_dec <- cut(Variable, breaks = cortes_dec, include.lowest = TRUE, right = FALSE)
ni_dec <- as.vector(table(inter_dec))
hi_dec <- (ni_dec/N)*100

# Cálculos de Frecuencias
sum_ni <- sum(ni_dec)
hi_dec <- (ni_dec / sum_ni) * 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)))

# Construcción del Dataframe Decimal
TDF_Decimal <- data.frame(
 Li = round(cortes_dec[1:k_dec], 2),
 Ls = round(cortes_dec[2:(k_dec+1)], 2),
 MC = round((cortes_dec[1:k_dec] + cortes_dec[2:(k_dec+1)]) / 2, 2),
 ni = ni_dec,
 hi = round(hi_dec, 2),
 Ni_asc = cumsum(ni_dec),
 Ni_desc = rev(cumsum(rev(ni_dec))),
 Hi_asc = cumsum(round(hi_dec, 2)),
 Hi_desc = rev(cumsum(rev(round(hi_dec, 2)))))


# Cálculo Límites Enteros #
BASE <- 10
min_int <- floor(min(Variable) / BASE) * BASE
max_int <- ceiling(max(Variable) / BASE) * BASE
k_int_sug <- floor(1 + 3.322 * log10(N))
Rango_int <- max_int - min_int

# Calculamos la amplitud necesaria
Amplitud_int <- ceiling((Rango_int / k_int_sug) / 10) * 10
if(Amplitud_int == 0) Amplitud_int <- 10

# Generamos cortes 
cortes_int <- seq(from = min_int, by = Amplitud_int, length.out = k_int_sug + 1)

# Aseguramos cobertura del máximo solo si es estrictamente necesario
if(max(cortes_int) < max(Variable)) {
  cortes_int <- c(cortes_int, max(cortes_int) + Amplitud_int)
}

while(length(cortes_int) > 2 && cortes_int[length(cortes_int)-1] >= max(Variable)) {
  cortes_int <- cortes_int[-length(cortes_int)]
}

K_real <- length(cortes_int) - 1
lim_inf_int <- cortes_int[1:K_real]
lim_sup_int <- cortes_int[2:(K_real+1)]

# Frecuencias
inter_int <- cut(Variable, breaks = cortes_int, include.lowest = TRUE, right = FALSE)
ni_int <- as.vector(table(inter_int))

# Cálculos de Frecuencias
hi_int <- (ni_int / N) * 100
Ni_asc_int <- cumsum(ni_int)
Ni_desc_int <- rev(cumsum(rev(ni_int)))
Hi_asc_int <- cumsum(hi_int)
Hi_desc_int <- rev(cumsum(rev(hi_int)))

# Construcción del Dataframe Entero
TDF_Enteros <- data.frame(
 Li = lim_inf_int,
 Ls = lim_sup_int,
 MC = (lim_inf_int + lim_sup_int) / 2,
 ni = ni_int,
 hi = round(hi_int, 2),
 Ni_asc = Ni_asc_int,
 Ni_desc = Ni_desc_int,
 Hi_asc = round(Hi_asc_int, 2),
 Hi_desc = round(Hi_desc_int, 2))

3 Tabla de Distribución de Frecuencias

3.1 Tabla con Límites Decimales

#### Crear de fila de totales ####
totales_dec <- c("TOTAL", "-", "-", sum(TDF_Decimal$ni), 100, "-", "-", "-", "-")
TDF_Dec_Final <- rbind(mutate(TDF_Decimal, across(everything(), as.character)), totales_dec)

# Generar GT Decimal
TDF_Dec_Final %>%
 gt() %>%
 tab_header(title = md("**Tabla N°1 de Distribución de Frecuencias de Inclinación Óptima (°) de las Plantas Solares**")) %>%
 cols_label(
  Li = "Lim. Inf",
  Ls = "Lim. Sup",
  MC = "Marca Clase",
  ni = "Frec. Abs (ni)",
  hi = "Frec. Rel (%)",
  Ni_asc = "Ni (Asc)",
  Ni_desc = "Ni (Desc)",
  Hi_asc = "Hi Asc (%)",
  Hi_desc = "Hi Desc (%)"
 ) %>%
 tab_options(heading.title.font.size = px(14), column_labels.background.color = "#F0F0F0")
Tabla N°1 de Distribución de Frecuencias de Inclinación Óptima (°) de las Plantas Solares
Lim. Inf Lim. Sup Marca Clase Frec. Abs (ni) Frec. Rel (%) Ni (Asc) Ni (Desc) Hi Asc (%) Hi Desc (%)
-2.01 1.18 -0.42 36 0.06 36 58978 0.06 100
1.18 4.36 2.77 192 0.33 228 58942 0.39 99.94
4.36 7.55 5.96 397 0.67 625 58750 1.06 99.61
7.55 10.74 9.15 649 1.1 1274 58353 2.16 98.94
10.74 13.93 12.34 986 1.67 2260 57704 3.83 97.84
13.93 17.12 15.52 1810 3.07 4070 56718 6.9 96.17
17.12 20.31 18.71 2191 3.71 6261 54908 10.61 93.1
20.31 23.49 21.9 2989 5.07 9250 52717 15.68 89.39
23.49 26.68 25.09 3052 5.17 12302 49728 20.85 84.32
26.68 29.87 28.28 5412 9.18 17714 46676 30.03 79.15
29.87 33.06 31.46 11427 19.38 29141 41264 49.41 69.97
33.06 36.25 34.65 14255 24.17 43396 29837 73.58 50.59
36.25 39.44 37.84 13590 23.04 56986 15582 96.62 26.42
39.44 42.62 41.03 1525 2.59 58511 1992 99.21 3.38
42.62 45.81 44.22 348 0.59 58859 467 99.8 0.79
45.81 49 47.41 119 0.2 58978 119 100 0.2
TOTAL - - 58978 100 - - - -

3.2 Tabla con Límites Enteros

#### Crear de fila de totales ####
totales_int <- c("TOTAL", "-", "-", sum(TDF_Enteros$ni), 100, "-", "-", "-", "-")
TDF_Int_Final <- rbind(mutate(TDF_Enteros, across(everything(), as.character)), totales_int)

# Generar GT Enteros
TDF_Int_Final %>%
 gt() %>%
 tab_header(
  title = md("**Tabla N°2 de Distribución de Frecuencias de Inclinación Óptima (°) de las Plantas Solares**")) %>%
 cols_label(
  Li = "Lim. Inf",
  Ls = "Lim. Sup",
  MC = "Marca Clase",
  ni = "Frec. Abs (ni)",
  hi = "Frec. Rel (%)",
  Ni_asc = "Ni (Asc)",
  Ni_desc = "Ni (Desc)",
  Hi_asc = "Hi Asc (%)",
  Hi_desc = "Hi Desc (%)"
 ) %>%

 fmt_number(columns = c(Li, Ls), decimals = 0) %>%
 fmt_number(columns = c(hi, Hi_asc, Hi_desc), decimals = 2) %>%
 tab_options(heading.title.font.size = px(14), column_labels.background.color = "#F0F0F0")
Tabla N°2 de Distribución de Frecuencias de Inclinación Óptima (°) de las Plantas Solares
Lim. Inf Lim. Sup Marca Clase Frec. Abs (ni) Frec. Rel (%) Ni (Asc) Ni (Desc) Hi Asc (%) Hi Desc (%)
-10 0 -5 1 0 1 58978 0 100
0 10 5 1090 1.85 1091 58977 1.85 100
10 20 15 4366 7.4 5457 57887 9.25 98.15
20 30 25 12341 20.92 17798 53521 30.18 90.75
30 40 35 39363 66.74 57161 41180 96.92 69.82
40 50 45 1817 3.08 58978 1817 100 3.08
TOTAL - - 58978 100 - - - -

4 Análisis Gráfico

4.1 Histogramas de Cantidad

par(mar = c(8, 7, 5, 2)) 
barplot(TDF_Enteros$ni, 
        names.arg = TDF_Enteros$MC,
        main = "",
        xlab = "", 
        ylab = "",
        col = "#C6E2FF",
        ylim = c(0, max(TDF_Enteros$ni) * 1.2),
        space = 0, 
        las = 2, 
        cex.names = 0.7)
mtext("Cantidad", side = 2, line = 4.5, cex = 1, font = 1)
mtext("Inclinación Óptima (°)", side = 1, line = 4)

mtext("Gráfica N°1: Distribución de Cantidad de Plantas Solares por Inclinación Óptima", 
      side = 3, 
      line = 2, 
      adj = 0.5, 
      cex = 0.9, 
      font = 2)

par(mar = c(8, 7, 5, 2))
barplot(TDF_Enteros$ni, 
        main="",
        xlab = "",
        ylab = "",
        names.arg = TDF_Enteros$MC,
        col = "#C6E2FF",
        space = 0,
        cex.names = 0.7,
        las = 2,
        ylim = c(0, 58978)) 
mtext("Cantidad", side = 2, line = 4.5, cex = 1, font = 1)
mtext("Inclinación Óptima (°)", side = 1, line = 4)

mtext("Gráfica N°2: Distribución de Cantidad de Plantas Solares por Inclinación Óptima", 
      side = 3, 
      line = 2, 
      adj = 0.5, 
      cex = 0.9, 
      font = 2)

4.2 Histogramas Porcentuales

par(mar = c(8, 5, 5, 2))
bp3 <- barplot(TDF_Enteros$hi, 
        main = "", 
        xlab = "", 
        ylab = "Porcentaje (%)", 
        col = "#C6E2FF", 
        space = 0, 
        names.arg = TDF_Enteros$MC, 
        cex.names = 0.7, 
        las = 2, 
        ylim = c(0, max(TDF_Enteros$hi) * 1.2))
mtext("Inclinación Óptima (°)", side = 1, line = 4)

mtext("Gráfica N°3: Distribución Porcentual de las Plantas Solares por Inclinación Óptima", 
      side = 3, 
      line = 2, 
      adj = 0.5, 
      cex = 0.9, 
      font = 2)

text(x = bp3, 
     y = TDF_Enteros$hi, 
     labels = paste0(round(TDF_Enteros$hi, 2), "%"), 
     pos = 3, cex = 0.6, col = "black")

par(mar = c(8, 5, 5, 2))
bp4 <- barplot(TDF_Enteros$hi, 
        main = "", 
        xlab = "", 
        ylab = "Porcentaje (%)", 
        col = "#C6E2FF", 
        space = 0, 
        names.arg = TDF_Enteros$MC, 
        las = 2, 
        cex.names = 0.7, 
        ylim = c(0, 100)) 
mtext("Inclinación Óptima(°)", side = 1, line = 4)

mtext("Gráfica N°4: Distribución Porcentual de las Plantas Solares por Inclinación", 
      side = 3, 
      line = 2, 
      adj = 0.5, 
      cex = 0.9, 
      font = 2)

text(x = bp4, 
     y = TDF_Enteros$hi, 
     labels = paste0(round(TDF_Enteros$hi, 2), "%"), 
     pos = 3, cex = 0.6, col = "black")

4.3 Diagrama de Cajas (Boxplot)

par(mar = c(5, 5, 4, 2))
boxplot(Variable, 
        horizontal = TRUE,
        col = "#C6E2FF",
        xlab = "Inclinación Óptima (°)",
        cex.main = 0.9,
        main = "Gráfica N°5: Distribución de la Inclinación Óptima en las Plantas Solares")

4.4 Ojivas

par(mar = c(5, 5, 7, 10), xpd = TRUE)

# Coordenadas
x_asc <- TDF_Enteros$Ls
x_desc <- TDF_Enteros$Li
y_asc <- TDF_Enteros$Ni_asc
y_desc <- TDF_Enteros$Ni_desc

# 1. Dibujar la Ascendente 
plot(x_asc, y_asc,
     type = "b", 
     main = "",
     xlab = "Inclinación Óptima (°)",
     ylab = "Frecuencia acumulada",
     col = "black",
     pch = 19, 
     xlim = c(min(TDF_Enteros$Li), max(x_asc)), 
     ylim = c(0, sum(TDF_Enteros$ni)),
     bty = "l"
)

# 2. Agregar la Descendente 
lines(x_desc, y_desc, col = "#9FB6CD", type = "b", pch = 19)

grid()
mtext("Gráfica N°6: Ojivas Ascendentes y Descendentes de la\nDistribución de la Inclinación Óptima en las Plantas Solares", 
      side = 3, 
      line = 3, 
      adj = 0.5, 
      cex = 0.9, 
      font = 2)

legend("left", 
       legend = c("Ascendente", "Descendente"), 
       col = c("black", "#9FB6CD"), 
       lty = 1, 
       pch = 1, 
       cex = 0.6, 
       inset = c(0.05, 0.05),
       bty = "n")

5 Indicadores Estadísticos

## INDICADORES DE TENDENCIA CENTRAL
# Media aritmética
media <- round(mean(Variable), 2)

# Mediana
mediana <- round(median(Variable), 2)

# Moda
max_frecuencia <- max(TDF_Enteros$ni)
moda_vals <- TDF_Enteros$MC[TDF_Enteros$ni == max_frecuencia]
moda_txt <- paste(round(moda_vals, 2), collapse = ", ")

## INDICADORES DE DISPERSIÓN
# Varianza
varianza <- var(Variable)

# Desviación Estándar
sd_val <- sd(Variable)

# Coeficiente de Variación
cv <- round((sd_val / abs(media)) * 100, 2)

## INDICADORES DE FORMA
# Coeficiente de Asimetría
asimetria <- skewness(Variable, type = 2)

# Curtosis
curtosis <- kurtosis(Variable)

# Outliers
Q1 <- quantile(Variable, 0.25)
Q3 <- quantile(Variable, 0.75)
IQR_val <- Q3 - Q1
lim_inf <- Q1 - 1.5 * IQR_val
lim_sup <- Q3 + 1.5 * IQR_val

outliers_data <- Variable[Variable < lim_inf | Variable > lim_sup]
num_outliers <- length(outliers_data)

if(num_outliers > 0){
  rango_outliers <- paste0(num_outliers, " [", round(min(outliers_data), 2), "; ", round(max(outliers_data), 2), "]")
} else {
  rango_outliers <- "0 [Sin Outliers]"
}

tabla_indicadores <- data.frame(
 "Variable" = c("Inclinación Óptima (°)"),
 "Rango_MinMax" = paste0("[", round(min(Variable), 2), "; ", round(max(Variable), 2), "]"),
 "X" = c(media),
 "Me" = c(mediana),
 "Mo" = c(moda_txt),
 "V" = c(varianza),
 "Sd" = c(sd_val),
 "Cv" = c(cv),
 "As" = c(asimetria),
 "K" = c(curtosis),
 "Outliers" = rango_outliers)

# Generar Tabla GT
tabla_conclusiones_gt <- tabla_indicadores %>%
 gt() %>%
 tab_header(title = md("**Tabla N°3 de Conclusiones de Inclinación Óptima de las Plantas Solares**")) %>%
 tab_source_note(source_note = "Autor: Martin Sarmiento") %>%
 cols_label(
  Variable = "Variable",
  Rango_MinMax = "Rango",
  X = "Media (X)",
  Me = "Mediana (Me)",
  Mo = "Moda (Mo)",
  V = "Varianza (V)",
  Sd = "Desv. Est. (Sd)",
  Cv = "C.V. (%)",
  As = "Asimetría (As)",
  K = "Curtosis (K)",
  Outliers = "Outliers [Intervalo]"
 ) %>%
 tab_options(
  heading.title.font.size = px(16),
  column_labels.background.color = "#F0F0F0"
 )

tabla_conclusiones_gt
Tabla N°3 de Conclusiones de Inclinación Óptima de las Plantas Solares
Variable Rango Media (X) Mediana (Me) Moda (Mo) Varianza (V) Desv. Est. (Sd) C.V. (%) Asimetría (As) Curtosis (K) Outliers [Intervalo]
Inclinación Óptima (°) [-2.01; 49] 31.27 33.57 35 56.39691 7.509787 24.02 -1.311292 1.508296 2557 [-2.01; 14.92]
Autor: Martin Sarmiento

6 Conclusiones

La variable “Inclinación Óptima” fluctúa entre -2.01° y 49° y sus valores se encuentran alrededor de 33.57°, con una desviación estándar de 7.509787, siendo una variable homogénea, cuyos valores se concentran en la parte media alta de la variable con la agregación de valores atípicos de 2557 outliers; por todo lo anterior, el comportamiento de la variable es regular.