1 Configuración y Carga de Datos

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


#### VARIABLE DIRECCION DEL VIENTO ####
## DATASET ##
setwd("~/R/WIND_DIRECTION")
# Cargar dataset
Datos <- read.csv("DataSet_prov.csv", sep = ";", dec = ",", fileEncoding = "latin1")
# Estructura de los datos
str(Datos)
## 'data.frame':    5075 obs. of  30 variables:
##  $ FID_                  : int  0 2 3 4 5 6 10 11 12 13 ...
##  $ OBJECTID              : int  127 129 130 131 132 133 137 138 139 140 ...
##  $ code                  : chr  "00127-ARG-P" "00129-ARG-G" "00130-ARG-P" "00131-ARG-P" ...
##  $ plant_name            : chr  "Aconcagua solar farm" "Altiplano 200 Solar Power Plant" "Altiplano 200 Solar Power Plant" "Anchoris solar farm" ...
##  $ country               : chr  "Argentina" "Argentina" "Argentina" "Argentina" ...
##  $ operational_status    : chr  "announced" "operating" "operating" "construction" ...
##  $ longitude             : num  -68.9 -66.9 -66.9 -68.9 -70.3 ...
##  $ latitude              : num  -33 -24.1 -24.1 -33.3 -37.4 ...
##  $ elevation             : int  929 4000 4000 937 865 858 570 1612 665 3989 ...
##  $ area                  : num  250 4397290 5774 645 241 ...
##  $ size                  : chr  "Pequeña" "Grande" "Pequeña" "Pequeña" ...
##  $ slope                 : num  0.574 1.603 6.243 0.903 1.791 ...
##  $ slope_type            : chr  "Plano o casi plano" "Plano o casi plano" "Moderado" "Plano o casi plano" ...
##  $ curvature             : num  0.000795 -0.002781 -0.043699 0.002781 -0.002384 ...
##  $ curvature_type        : chr  "Superficies planas o intermedias" "Superficies planas o intermedias" "Superficies cóncavas / Valles" "Superficies planas o intermedias" ...
##  $ aspect                : num  55.1 188.7 270.9 108.4 239.3 ...
##  $ aspect_type           : chr  "Northeast" "South" "West" "East" ...
##  $ dist_to_road          : num  127 56015 52697 336 34 ...
##  $ ambient_temperature   : num  12.6 6.8 6.8 13.1 11.4 ...
##  $ ghi                   : num  6.11 8.01 7.88 6.12 6.22 ...
##  $ humidity              : num  53.7 53.7 53.7 53.7 53.7 ...
##  $ wind_speed            : num  3.78 7.02 8.33 3.87 6.56 ...
##  $ wind_direction        : num  55.1 55.1 55.1 55.1 55.1 ...
##  $ dt_wind               : chr  "Northeast" "Northeast" "Northeast" "Northeast" ...
##  $ solar_aptitude        : num  0.746 0.8 0.727 0.595 0.657 ...
##  $ solar_aptitude_rounded: int  7 8 7 6 7 7 7 8 7 8 ...
##  $ solar_aptittude_class : chr  "Alta" "Alta" "Alta" "Media" ...
##  $ capacity              : num  25 101 107 180 20 ...
##  $ optimal_tilt          : int  31 26 26 31 33 30 31 29 31 27 ...
##  $ pv_potential          : num  4.98 6.39 6.39 4.97 5 ...
# 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$wind_direction)
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

# Cálculos básicos
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
Amplitud_raw <- Rango_int / k_int_sug

Amplitud_int <- ceiling(Amplitud_raw / 10) * 10
if(Amplitud_int == 0) Amplitud_int <- 10

# Generar cortes enteros
cortes_int <- seq(from = min_int, by = Amplitud_int, length.out = k_int_sug + 2)

cortes_int <- cortes_int[cortes_int <= (max_int + Amplitud_int)]

# Asegurar cobertura del máximo
while(max(cortes_int) < max(Variable)) {
 cortes_int <- c(cortes_int, max(cortes_int) + Amplitud_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 Dirección del Viento (°) 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 Dirección del Viento (°) de las Plantas Solares
Lim. Inf Lim. Sup Marca Clase Frec. Abs (ni) Frec. Rel (%) Ni (Asc) Ni (Desc) Hi Asc (%) Hi Desc (%)
0 27.12 13.56 1049 20.67 1049 5075 20.67 100
27.12 54.23 40.67 87 1.71 1136 4026 22.38 79.33
54.23 81.35 67.79 217 4.28 1353 3939 26.66 77.62
81.35 108.46 94.9 1349 26.58 2702 3722 53.24 73.34
108.46 135.58 122.02 1402 27.63 4104 2373 80.87 46.76
135.58 162.69 149.13 337 6.64 4441 971 87.51 19.13
162.69 189.81 176.25 126 2.48 4567 634 89.99 12.49
189.81 216.92 203.37 102 2.01 4669 508 92 10.01
216.92 244.04 230.48 167 3.29 4836 406 95.29 8
244.04 271.15 257.6 173 3.41 5009 239 98.7 4.71
271.15 298.27 284.71 47 0.93 5056 66 99.63 1.3
298.27 325.38 311.83 18 0.35 5074 19 99.98 0.37
325.38 352.5 338.94 1 0.02 5075 1 100 0.02
TOTAL - - 5075 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 Dirección del Viento (°) 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 Dirección del Viento (°) de las Plantas Solares
Lim. Inf Lim. Sup Marca Clase Frec. Abs (ni) Frec. Rel (%) Ni (Asc) Ni (Desc) Hi Asc (%) Hi Desc (%)
0 30 15 1080 21.28 1080 5075 21.28 100
30 60 45 138 2.72 1218 3995 24 78.72
60 90 75 407 8.02 1625 3857 32.02 76
90 120 105 1762 34.72 3387 3450 66.74 67.98
120 150 135 932 18.36 4319 1688 85.1 33.26
150 180 165 160 3.15 4479 756 88.26 14.9
180 210 195 178 3.51 4657 596 91.76 11.74
210 240 225 123 2.42 4780 418 94.19 8.24
240 270 255 211 4.16 4991 295 98.34 5.81
270 300 285 66 1.3 5057 84 99.65 1.66
300 330 315 17 0.33 5074 18 99.98 0.35
330 360 345 1 0.02 5075 1 100 0.02
360 390 375 0 0 5075 0 100 0
TOTAL - - 5075 100 - - - -

4 Análisis Gráfico

4.1 Histogramas de Cantidad

color_sutil <- "#66CDAA"

par(mar = c(8, 5, 4, 2)) 
barplot(TDF_Enteros$ni, 
        names.arg = TDF_Enteros$MC,
        main = "Gráfica N°1: Distribución de Cantidad de Plantas Solares por Dirección del Viento",
        cex.main = 0.9,
        xlab = "", 
        ylab = "Cantidad",
        col = color_sutil,
        space = 0,
        las = 2, 
        cex.names = 0.7)
mtext("Dirección del Viento (°)", side = 1, line = 4)

color_grafico <- "#66CDAA"

par(mar = c(8, 5, 4, 2))
barplot(TDF_Enteros$ni, 
        main="Gráfica N°2: Distribución de Cantidad de Plantas Solares por Dirección del Viento",
        cex.main = 0.8,
        xlab = "",
        ylab = "Cantidad",
        names.arg = TDF_Enteros$MC,
        col = color_sutil,
        space = 0,
        cex.names = 0.7,
        las = 2,
        ylim = c(0, sum(TDF_Enteros$ni))) 
mtext("Dirección del Viento (°)", side = 1, line = 4)

4.2 Histogramas Porcentuales

color_grafico <- "#66CDAA"

par(mar = c(8, 5, 4, 2))
barplot(TDF_Enteros$hi, 
        main="Gráfica N°3: Distribución Porcentual de las Plantas Solares por Dirección del Viento",
        cex.main = 0.9,
        xlab = "",
        ylab = "Porcentaje (%)",
        col = color_sutil,
        space = 0,
        names.arg = TDF_Enteros$MC,
        cex.names = 0.7,
        las = 2,
        ylim = c(0, max(TDF_Enteros$hi) * 1.1))
mtext("Dirección del Viento (°)", side = 1, line = 4)

color_grafico <- "#66CDAA"

par(mar = c(8, 5, 4, 2))
barplot(TDF_Enteros$hi, 
        main="Gráfica N°4: Distribución Porcentual de las Plantas Solares por Dirección del Viento",
        cex.main = 0.8,
        xlab = "",
        ylab = "Porcentaje (%)",
        col = color_sutil,
        space = 0,
        names.arg = TDF_Enteros$MC,
        las = 2,
        cex.names = 0.7,
        ylim = c(0, 100)) 
mtext("Dirección del Viento (°)", side = 1, line = 4)

4.3 Diagrama de Cajas (Boxplot)

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

4.4 Ojivas

par(mar = c(5, 5, 4, 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 = "Gráfica N°6: Ojivas Ascendentes y Descendentes de la Distribución de la Dirección del Viento en las Plantas Solares",
     cex.main = 0.6,
     xlab = "Dirección (°)",
     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 = "#009688", type = "b", pch = 19)

grid()
legend("right", 
       legend = c("Ascendente", "Descendente"), 
       col = c("black", "#009688"), 
       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
outliers_data <- boxplot.stats(Variable)$out

if(length(outliers_data) > 0) {
 num_out <- length(outliers_data)
 min_out <- round(min(outliers_data), 2)
 max_out <- round(max(outliers_data), 2)

 # Formato Total [Min; Max]
 msg_atipicos <- paste0(" ", num_out, " [", min_out, " ; ", max_out, "]")
} else {
 msg_atipicos <- "No hay presencia de valores atípicos"
}


tabla_indicadores <- data.frame(
 "Variable" = c("Dirección del Viento (°)"),
 "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" = msg_atipicos
)

# Generar Tabla GT
tabla_conclusiones_gt <- tabla_indicadores %>%
 gt() %>%
 tab_header(title = md("**Tabla N°3 de Conclusiones de Velocidad del Viento 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"
 ) %>%
 tab_options(
  heading.title.font.size = px(16),
  column_labels.background.color = "#f0f0f0"
 )

tabla_conclusiones_gt
Tabla N°3 de Conclusiones de Velocidad del Viento 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
Dirección del Viento (°) [0; 352.5] 101.46 108 105 4756.249 68.96557 67.97 0.3668505 0.3351819 435 [200 ; 352.5]
Autor: Martin Sarmiento

6 Conclusiones

La variable “Dirección del Viento” fluctúa entre 0° y 352.5° y sus valores se encuentran alrededor de 108°, con una desviación estándar de 68.96557, siendo una variable heterogénea, cuyos valores se concentran en la parte media baja de la variable con la agregación de valores atípicos de 435 outliers; por todo lo anterior, el comportamiento de la variable es muy perjudicial.