3 Solución reto ampliada.
Para comenzar recordemos el objetivo final de este reto: “Vuestro reto consiste en predecir la producción eléctrica en Watios que pueden generar los paneles A y B en su orientación óptima durante los siete primeros días del año 2017”. Es decir, se trata claramente de un ejercicio de regresión, y aunque inicialmente por el enunciado podría parecer un problema de “forecasting” (predicción a futuro) no lo es, ya que para predecir la producción eléctrica en cada momento disponemos de otras variables explicativas de ese mismo momento que podemos utilizar sin ningún tipo de restricción.
Así que como productos finales tenemos que tener dos predicciones de producción eléctrica en Watios para los 7 primeros días de 2017, una para cada tipo de panel.
Para generar y entrenar los modelos se nos proporcionaron los siguientes datasets:
sunlab-faro-pv-2015.csv
sunlab-faro-pv-2016.csv
sunlab-faro_meteo_2015.csv
sunlab-faro-meteo-2016.csv
Y para realizar nuestra predicción final estos otros:
test-sunlab-meteo-2017.xlsx
test-sunlab-pv-2017.xlsx
Todo el proceso previo a la generación de los modelos lo podíamos hacer con las herramientas que quisiésemos. Nosotros utilizamos R. Para la parte de modelado se nos dio acceso a una cuenta boost de BIGml.
3.1 Carga de las librerias necesarias.
Cargamos los paquetes que vamos a necesitar para el análisis
library(tidyverse)
library(lubridate)
library(readxl)
library(GGally)
library(caret)
library(knitr)
library(gridExtra)
library(kableExtra)
3.2 Importación de los datos.
3.2.1 Datos “train”.
Los cuatro archivos de “train” facilitados en formato xls parecían tener algún tipo de problema. En el Hackathon no fuimos capaces de importarlo en R en ese formato. Así que los convertimos previamente en formato csv. En este ocasión lo que hemos hecho es ir a la página de EDP de Open Data y descargar los archivos directamente en formato csv.
Estos archivos csv utilizan como separador el punto y coma. Así que en este caso tendremos que utilizar la función read_delim() con el argumento delim = “;”.
meteo_2015 <- read_delim(file = "./data/original_data/train/sunlab-faro_meteo_2015.csv", delim = ";")
meteo_2016 <- read_delim(file = "./data/original_data/train/sunlab-faro-meteo-2016.csv", delim = ";")
prod_2015 <- read_delim(file = "./data/original_data/train/sunlab-faro-pv-2015.csv", delim = ";")
prod_2016 <- read_delim(file = "./data/original_data/train/sunlab-faro-pv-2016.csv", delim = ";")
Unimos las tablas de distintos años de meteo y producción y nos quedamos con dos únicas tablas de train.
meteo_2015_2016 <- bind_rows(meteo_2015, meteo_2016)
prod_2015_2016 <- bind_rows(prod_2015, prod_2016)
Borramos las tablas anteriores de train para que no nos ocupen espacio en memoria
rm(meteo_2015)
rm(meteo_2016)
rm(prod_2015)
rm(prod_2016)
3.2.2 Datos “test”.
En este caso las tablas a importar estaban en formato xlsx y no tuvimos ningún problema a la hora de importarlas. Así que esta vez utilizaremos los archivos facilitados durante el Hackathon.
test_meteo_2017 <- read_xlsx(path = "./data/original_data/test/test-sunlab-meteo-2017.xlsx")
test_prod_2017 <- read_xlsx(path = "./data/original_data/test/test-sunlab-pv-2017.xlsx")
3.3 Exploración y tratamiento de los datos.
3.3.1 Exploración inicial de las tablas.
En este apartado simplemente vamos a ver las variables que contiene cada tabla, el número de observaciones, formato, etc. También comprobaremos si las variables que tenemos en los datasets de train efectivamente están en las de test.
Echamos un vistazo a las variables y observaciones de la tabla de train de producción.
glimpse(prod_2015_2016)
## Observations: 420,507
## Variables: 25
## $ Datetime <dttm> 2015-01-09 12:52:00, 2015-0...
## $ `A_Vertical - Voltage DC [V]` <dbl> 29.35, 29.10, 29.10, 29.35, ...
## $ `A_Vertical - Current DC [A]` <dbl> 6.4125, 6.3975, 6.3325, 6.41...
## $ `A_Vertical - Power DC [W]` <dbl> 188.206875, 186.167250, 184....
## $ `A_Optimal - Voltage DC [V]` <dbl> 28.75, 28.45, 28.45, 28.75, ...
## $ `A_Optimal - Current DC [A]` <dbl> 7.1700, 7.1925, 7.0925, 7.21...
## $ `A_Optimal - Power DC [W]` <dbl> 206.13750, 204.62662, 201.78...
## $ `A_Horizontal - Voltage DC [V]` <dbl> 29.25, 29.50, 29.45, 29.50, ...
## $ `A_Horizontal - Current DC [A]` <dbl> 4.3225, 4.2525, 4.1825, 4.30...
## $ `A_Horizontal - Power DC [W]` <dbl> 126.43313, 125.44875, 123.17...
## $ `A_Vertical - Temperature [ºC]` <dbl> 32.6, 32.4, 33.2, 32.2, 32.8...
## $ `A_Optimal - Temperature [ºC]` <dbl> 28.8, 28.6, 29.4, 28.2, 27.8...
## $ `A_Horizontal - Temperature [ºC]` <dbl> 26.5, 26.1, 26.8, 26.4, 26.1...
## $ `B_Vertical - Voltage DC [V]` <dbl> 27.55, 27.60, 27.40, 27.40, ...
## $ `B_Vertical - Current DC [A]` <dbl> 7.0925, 7.0475, 7.0100, 7.14...
## $ `B_Vertical - Power DC [W]` <dbl> 195.39837, 194.51100, 192.07...
## $ `B_Optimal - Voltage DC [V]` <dbl> 27.85, 28.05, 27.85, 28.05, ...
## $ `B_Optimal - Current DC [A]` <dbl> 7.2225, 7.1125, 7.0450, 7.17...
## $ `B_Optimal - Power DC [W]` <dbl> 201.14663, 199.50563, 196.20...
## $ `B_Horizontal - Voltage DC [V]` <dbl> 29.05, 29.05, 28.75, 28.95, ...
## $ `B_Horizontal - Current DC [A]` <dbl> 4.1775, 4.1275, 4.1125, 4.19...
## $ `B_Horizontal - Power DC [W]` <dbl> 121.356375, 119.903875, 118....
## $ `B_Vertical - Temperature [ºC]` <dbl> 31.2, 31.5, 31.9, 31.7, 32.2...
## $ `B_Optimal - Temperature [ºC]` <dbl> 30.2, 30.0, 30.9, 29.9, 30.7...
## $ `B_Horizontal - Temperature [ºC]` <dbl> 25.3, 24.9, 25.7, 25.1, 25.6...
Vemos también los primeros 10 registros.
kable(head(prod_2015_2016, 10), booktab = TRUE)
Datetime | A_Vertical - Voltage DC [V] | A_Vertical - Current DC [A] | A_Vertical - Power DC [W] | A_Optimal - Voltage DC [V] | A_Optimal - Current DC [A] | A_Optimal - Power DC [W] | A_Horizontal - Voltage DC [V] | A_Horizontal - Current DC [A] | A_Horizontal - Power DC [W] | A_Vertical - Temperature [ºC] | A_Optimal - Temperature [ºC] | A_Horizontal - Temperature [ºC] | B_Vertical - Voltage DC [V] | B_Vertical - Current DC [A] | B_Vertical - Power DC [W] | B_Optimal - Voltage DC [V] | B_Optimal - Current DC [A] | B_Optimal - Power DC [W] | B_Horizontal - Voltage DC [V] | B_Horizontal - Current DC [A] | B_Horizontal - Power DC [W] | B_Vertical - Temperature [ºC] | B_Optimal - Temperature [ºC] | B_Horizontal - Temperature [ºC] |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2015-01-09 12:52:00 | 29.35 | 6.4125 | 188.2069 | 28.75 | 7.1700 | 206.1375 | 29.25 | 4.3225 | 126.4331 | 32.6 | 28.8 | 26.5 | 27.55 | 7.0925 | 195.3984 | 27.85 | 7.2225 | 201.1466 | 29.05 | 4.1775 | 121.3564 | 31.2 | 30.2 | 25.3 |
2015-01-09 12:14:00 | 29.10 | 6.3975 | 186.1672 | 28.45 | 7.1925 | 204.6266 | 29.50 | 4.2525 | 125.4488 | 32.4 | 28.6 | 26.1 | 27.60 | 7.0475 | 194.5110 | 28.05 | 7.1125 | 199.5056 | 29.05 | 4.1275 | 119.9039 | 31.5 | 30.0 | 24.9 |
2015-01-09 13:19:00 | 29.10 | 6.3325 | 184.2757 | 28.45 | 7.0925 | 201.7816 | 29.45 | 4.1825 | 123.1746 | 33.2 | 29.4 | 26.8 | 27.40 | 7.0100 | 192.0740 | 27.85 | 7.0450 | 196.2032 | 28.75 | 4.1125 | 118.2344 | 31.9 | 30.9 | 25.7 |
2015-01-09 12:41:00 | 29.35 | 6.4125 | 188.2069 | 28.75 | 7.2150 | 207.4313 | 29.50 | 4.3050 | 126.9975 | 32.2 | 28.2 | 26.4 | 27.40 | 7.1425 | 195.7045 | 28.05 | 7.1775 | 201.3289 | 28.95 | 4.1975 | 121.5176 | 31.7 | 29.9 | 25.1 |
2015-01-09 13:07:00 | 29.10 | 6.4050 | 186.3855 | 28.45 | 7.1975 | 204.7689 | 29.50 | 4.2600 | 125.6700 | 32.8 | 27.8 | 26.1 | 27.65 | 7.0300 | 194.3795 | 27.85 | 7.1550 | 199.2668 | 28.95 | 4.1625 | 120.5044 | 32.2 | 30.7 | 25.6 |
2015-01-09 12:57:00 | 29.00 | 6.4550 | 187.1950 | 28.45 | 7.2475 | 206.1914 | 29.25 | 4.3200 | 126.3600 | 33.3 | 29.6 | 26.8 | 27.40 | 7.1375 | 195.5675 | 27.85 | 7.2175 | 201.0074 | 28.80 | 4.2175 | 121.4640 | 31.7 | 30.9 | 25.7 |
2015-01-09 13:04:00 | 29.30 | 6.3950 | 187.3735 | 28.45 | 7.2275 | 205.6224 | 29.50 | 4.2800 | 126.2600 | 32.4 | 27.7 | 25.9 | 27.40 | 7.1225 | 195.1565 | 27.85 | 7.1750 | 199.8237 | 28.75 | 4.2050 | 120.8937 | 31.7 | 30.3 | 25.4 |
2015-01-09 13:20:00 | 29.10 | 6.3200 | 183.9120 | 28.45 | 7.0900 | 201.7105 | 29.25 | 4.2050 | 122.9963 | 33.1 | 29.1 | 26.7 | 27.15 | 7.0475 | 191.3396 | 27.85 | 7.0350 | 195.9247 | 27.75 | 4.2025 | 116.6194 | 32.0 | 31.0 | 25.8 |
2015-01-09 13:16:00 | 29.10 | 6.3750 | 185.5125 | 28.75 | 7.0700 | 203.2625 | 29.45 | 4.2025 | 123.7636 | 32.4 | 28.7 | 26.3 | 27.70 | 6.9725 | 193.1382 | 28.10 | 7.0425 | 197.8942 | 28.75 | 4.1475 | 119.2406 | 31.2 | 29.9 | 25.3 |
2015-01-09 12:48:00 | 29.30 | 6.4375 | 188.6188 | 28.75 | 7.2050 | 207.1438 | 29.50 | 4.3100 | 127.1450 | 32.1 | 28.4 | 26.4 | 27.60 | 7.1250 | 196.6500 | 28.05 | 7.2000 | 201.9600 | 29.05 | 4.1950 | 121.8648 | 30.8 | 29.5 | 25.0 |
Ejecutamos un summary() para observar los principales estadísticos de cada variable, NAs, etc.
summary(prod_2015_2016)
## Datetime A_Vertical - Voltage DC [V]
## Min. :2015-01-01 07:40:00 Min. :10.20
## 1st Qu.:2015-05-28 18:54:30 1st Qu.:26.65
## Median :2015-10-08 17:39:00 Median :27.95
## Mean :2015-12-04 18:29:34 Mean :26.28
## 3rd Qu.:2016-07-19 14:47:30 3rd Qu.:29.10
## Max. :2016-12-29 17:31:00 Max. :35.05
## NA's :13979
## A_Vertical - Current DC [A] A_Vertical - Power DC [W]
## Min. :0.000 Min. : 0.00
## 1st Qu.:0.710 1st Qu.: 15.90
## Median :1.822 Median : 50.94
## Mean :2.252 Mean : 63.56
## 3rd Qu.:3.460 3rd Qu.:100.67
## Max. :8.105 Max. :245.18
## NA's :13979 NA's :13979
## A_Optimal - Voltage DC [V] A_Optimal - Current DC [A]
## Min. :10.10 Min. : 0.000
## 1st Qu.:27.05 1st Qu.: 1.097
## Median :27.90 Median : 3.482
## Mean :27.53 Mean : 3.574
## 3rd Qu.:28.95 3rd Qu.: 5.860
## Max. :35.30 Max. :10.238
## NA's :8342 NA's :8342
## A_Optimal - Power DC [W] A_Horizontal - Voltage DC [V]
## Min. : 0.00 Min. :10.10
## 1st Qu.: 30.74 1st Qu.:26.90
## Median : 99.79 Median :27.70
## Mean :100.48 Mean :26.53
## 3rd Qu.:165.06 3rd Qu.:28.90
## Max. :307.64 Max. :35.35
## NA's :8342 NA's :9039
## A_Horizontal - Current DC [A] A_Horizontal - Power DC [W]
## Min. : 0.000 Min. : 0.00
## 1st Qu.: 1.095 1st Qu.: 27.55
## Median : 2.930 Median : 83.44
## Mean : 3.163 Mean : 87.10
## 3rd Qu.: 5.055 3rd Qu.:140.21
## Max. :10.238 Max. :304.63
## NA's :9039 NA's :9039
## A_Vertical - Temperature [ºC] A_Optimal - Temperature [ºC]
## Min. : 1.80 Min. : 2.0
## 1st Qu.:22.60 1st Qu.:21.5
## Median :30.20 Median :29.0
## Mean :29.21 Mean :28.8
## 3rd Qu.:35.80 3rd Qu.:36.0
## Max. :52.80 Max. :58.6
##
## A_Horizontal - Temperature [ºC] B_Vertical - Voltage DC [V]
## Min. : 1.40 Min. :10.30
## 1st Qu.:20.70 1st Qu.:26.45
## Median :28.30 Median :27.00
## Mean :28.59 Mean :25.48
## 3rd Qu.:36.30 3rd Qu.:27.60
## Max. :64.80 Max. :34.65
## NA's :11201
## B_Vertical - Current DC [A] B_Vertical - Power DC [W]
## Min. :0.000 Min. : 0.00
## 1st Qu.:0.678 1st Qu.: 14.95
## Median :1.847 Median : 49.89
## Mean :2.382 Mean : 64.48
## 3rd Qu.:3.695 3rd Qu.:102.69
## Max. :9.078 Max. :255.99
## NA's :11201 NA's :11201
## B_Optimal - Voltage DC [V] B_Optimal - Current DC [A]
## Min. :10.35 Min. : 0.000
## 1st Qu.:26.70 1st Qu.: 1.083
## Median :27.35 Median : 3.438
## Mean :26.31 Mean : 3.654
## 3rd Qu.:28.00 3rd Qu.: 6.250
## Max. :35.15 Max. :10.238
## NA's :6730 NA's :6730
## B_Optimal - Power DC [W] B_Horizontal - Voltage DC [V]
## Min. : 0.00 Min. :10.65
## 1st Qu.: 27.08 1st Qu.:26.70
## Median : 96.81 Median :27.35
## Mean : 99.69 Mean :26.26
## 3rd Qu.:170.25 3rd Qu.:28.35
## Max. :302.01 Max. :35.10
## NA's :6730 NA's :11902
## B_Horizontal - Current DC [A] B_Horizontal - Power DC [W]
## Min. : 0.000 Min. : 0.00
## 1st Qu.: 1.077 1st Qu.: 25.83
## Median : 2.857 Median : 81.42
## Mean : 3.120 Mean : 85.54
## 3rd Qu.: 4.980 3rd Qu.:139.04
## Max. :10.238 Max. :300.98
## NA's :11902 NA's :11902
## B_Vertical - Temperature [ºC] B_Optimal - Temperature [ºC]
## Min. : 3.20 Min. : 2.30
## 1st Qu.:23.50 1st Qu.:22.50
## Median :30.50 Median :30.90
## Mean :29.53 Mean :30.67
## 3rd Qu.:35.70 3rd Qu.:38.70
## Max. :51.10 Max. :62.00
##
## B_Horizontal - Temperature [ºC]
## Min. : 1.60
## 1st Qu.:21.10
## Median :28.30
## Mean :28.59
## 3rd Qu.:36.10
## Max. :62.10
##
En principio, a excepción de los NAs de alguna variable, no vemos a problemas evidentes con este dataset. Sí que la estructura de la tabla no nos parece la ideal, pero esto lo trataremos más tarde.
Echamos un vistazo también a los datos de test de producción.
glimpse(test_prod_2017)
## Observations: 4,243
## Variables: 25
## $ Datetime <chr> "2017-01-01T07:52:00+00:00",...
## $ `A_Vertical - Voltage DC [V]` <dbl> 19.25, 19.25, 19.20, 19.30, ...
## $ `A_Vertical - Current DC [A]` <dbl> 0.0750, 0.0850, 0.1175, 0.27...
## $ `A_Vertical - Power DC [W]` <dbl> 1.44375, 1.63625, 2.25600, 5...
## $ `A_Optimal - Voltage DC [V]` <dbl> 21.60, 32.15, 32.75, 32.95, ...
## $ `A_Optimal - Current DC [A]` <dbl> 0.1075, 0.0450, 0.0450, 0.16...
## $ `A_Optimal - Power DC [W]` <chr> "=E2*F2", NA, NA, NA, NA, NA...
## $ `A_Horizontal - Voltage DC [V]` <dbl> 18.80, 18.80, 18.75, 18.75, ...
## $ `A_Horizontal - Current DC [A]` <dbl> 0.0650, 0.0725, 0.0800, 0.10...
## $ `A_Horizontal - Power DC [W]` <dbl> 1.22200, 1.36300, 1.50000, 1...
## $ `A_Vertical - Temperature [ºC]` <dbl> 4.5, 4.7, 4.8, 5.0, 5.1, 5.3...
## $ `A_Optimal - Temperature [ºC]` <dbl> 4.0, 4.2, 4.5, 4.6, 4.8, 5.0...
## $ `A_Horizontal - Temperature [ºC]` <dbl> 3.3, 3.5, 3.7, 3.9, 4.1, 4.2...
## $ `B_Vertical - Voltage DC [V]` <dbl> 18.90, 18.90, 18.90, 18.90, ...
## $ `B_Vertical - Current DC [A]` <dbl> 0.0700, 0.0800, 0.0925, 0.10...
## $ `B_Vertical - Power DC [W]` <dbl> 1.323000, 1.512000, 1.748250...
## $ `B_Optimal - Voltage DC [V]` <dbl> 21.50, 20.95, 31.65, 33.30, ...
## $ `B_Optimal - Current DC [A]` <dbl> 0.0825, 0.0925, 0.0250, 0.02...
## $ `B_Optimal - Power DC [W]` <lgl> NA, NA, NA, NA, NA, NA, NA, ...
## $ `B_Horizontal - Voltage DC [V]` <dbl> 21.45, 19.70, 19.75, 31.25, ...
## $ `B_Horizontal - Current DC [A]` <dbl> 0.10753, 0.07750, 0.08500, 0...
## $ `B_Horizontal - Power DC [W]` <dbl> 2.306519, 1.526750, 1.678750...
## $ `B_Vertical - Temperature [ºC]` <dbl> 4.8, 4.9, 5.1, 5.3, 5.4, 5.6...
## $ `B_Optimal - Temperature [ºC]` <dbl> 4.1, 4.3, 4.5, 4.7, 4.9, 5.0...
## $ `B_Horizontal - Temperature [ºC]` <dbl> 3.2, 3.4, 3.6, 3.8, 3.9, 4.1...
El formato de alguna de las variables no es el correcto. ‘Datetime’ aparece como caracter y hay un par de variables numéricas que no han sido debidamente identificadas. Y como el dataset de train la estructura de la tabla no es la mejor. Todo esto lo corregiremos más adelante también.
Con el siguiente código comprobamos que las variables que tenemos en train también las tenemos en test.
prod_train_var <- names(prod_2015_2016)
prod_test_var <- names(test_prod_2017)
all.equal.character(prod_train_var, prod_test_var)
## [1] TRUE
rm(prod_train_var)
rm(prod_test_var)
Echamos también un vistazo a los datos meteorológicos.
glimpse(meteo_2015_2016)
## Observations: 836,903
## Variables: 10
## $ Datetime <dttm> 2015-10-29 01:43:00, 2015-10-29 ...
## $ `Ambient Temperature [ºC]` <dbl> 14.60000, 14.10000, 14.00000, 13....
## $ `Global Radiation [W/m2]` <dbl> 1.415117, 1.693405, 1.728471, 1.4...
## $ `Diffuse Radiation [W/m2]` <dbl> 1.644703, 1.639023, 1.653051, 1.6...
## $ `Ultraviolet [W/m2]` <dbl> 0.510503, 0.504588, 0.513537, 0.5...
## $ `Wind Velocity [m/s]` <dbl> 0.966667, 0.233333, 0.383333, 0.3...
## $ `Wind Direction [º]` <dbl> 52.81666, 14.49223, 62.99665, 246...
## $ `Precipitation [mm]` <dbl> NA, NA, NA, NA, NA, NA, NA, NA, N...
## $ `Atmospheric pressure [hPa]` <dbl> NA, NA, NA, NA, NA, NA, NA, NA, N...
## $ `Direct Radiation [W/m2]` <dbl> NA, NA, NA, NA, NA, NA, NA, NA, N...
head(meteo_2015_2016)
## # A tibble: 6 x 10
## Datetime `Ambient Temper~ `Global Radiati~ `Diffuse Radiat~
## <dttm> <dbl> <dbl> <dbl>
## 1 2015-10-29 01:43:00 14.6 1.42 1.64
## 2 2015-10-29 01:56:00 14.1 1.69 1.64
## 3 2015-10-29 02:00:00 14 1.73 1.65
## 4 2015-10-29 02:09:00 13.9 1.48 1.65
## 5 2015-10-29 02:13:00 14.0 1.42 1.64
## 6 2015-10-29 02:15:00 14 1.51 1.65
## # ... with 6 more variables: `Ultraviolet [W/m2]` <dbl>, `Wind Velocity
## # [m/s]` <dbl>, `Wind Direction [º]` <dbl>, `Precipitation [mm]` <dbl>,
## # `Atmospheric pressure [hPa]` <dbl>, `Direct Radiation [W/m2]` <dbl>
summary(meteo_2015_2016)
## Datetime Ambient Temperature [ºC]
## Min. :2015-01-01 00:00:00 Min. :-1.500e+09
## 1st Qu.:2015-05-26 11:06:30 1st Qu.: 1.400e+01
## Median :2015-10-18 23:12:00 Median : 1.800e+01
## Mean :2015-11-27 03:00:27 Mean :-5.358e+03
## 3rd Qu.:2016-05-17 18:32:30 3rd Qu.: 2.300e+01
## Max. :2016-12-31 23:00:00 Max. : 3.800e+01
##
## Global Radiation [W/m2] Diffuse Radiation [W/m2] Ultraviolet [W/m2]
## Min. : 0.7285 Min. : 1.6 Min. : 0.4954
## 1st Qu.: 1.5693 1st Qu.: 1.7 1st Qu.: 0.5106
## Median : 13.0063 Median : 16.5 Median : 1.5008
## Mean : 237.1203 Mean : 68.2 Mean :14.5440
## 3rd Qu.: 452.7272 3rd Qu.:103.7 3rd Qu.:26.0017
## Max. :1421.3282 Max. :720.4 Max. :84.2660
## NA's :399744
## Wind Velocity [m/s] Wind Direction [º] Precipitation [mm]
## Min. :-1.500e+09 Min. : 0.0 Min. :0
## 1st Qu.: 1.000e+00 1st Qu.:123.6 1st Qu.:0
## Median : 2.000e+00 Median :235.8 Median :0
## Mean :-5.375e+03 Mean :212.8 Mean :0
## 3rd Qu.: 3.000e+00 3rd Qu.:296.7 3rd Qu.:0
## Max. : 1.200e+01 Max. :360.0 Max. :2
## NA's :776160
## Atmospheric pressure [hPa] Direct Radiation [W/m2]
## Min. : 999.1 Min. : 1.6
## 1st Qu.:1014.3 1st Qu.: 1.7
## Median :1020.4 Median : 8.6
## Mean :1019.9 Mean : 63.9
## 3rd Qu.:1025.7 3rd Qu.: 91.9
## Max. :1033.8 Max. :790.1
## NA's :776160 NA's :437159
En este dataset vemos a primera vista más problemas. Hay 4 variables con un gran número de NAs: Diffuse Radiation [W/m2], Precipitation [mm], Atmospheric pressure [hPa] y Direct Radiation [W/m2]. Y aparte de esto también parece haber un problema con los valores mínimos de Ambient Temperature [ºC] y Wind Velocity [m/s].
Vemos también los datos meteorológicos de test.
glimpse(test_meteo_2017)
## Observations: 10,080
## Variables: 9
## $ Datetime <chr> "2017-01-01T00:00:00+01:00", "201...
## $ `Ambient Temperature [ºC]` <dbl> 6.333334, 6.300000, 6.216667, 6.2...
## $ `Global Radiation [W/m2]` <dbl> 1.684853, 1.761459, 1.854881, 2.1...
## $ `Diffuse Radiation [W/m2]` <dbl> 1.668379, 1.654411, 1.762080, 1.8...
## $ `Ultraviolet [W/m2]` <dbl> 0.503180, 0.501983, 0.501479, 0.5...
## $ `Wind Velocity [m/s]` <dbl> 4.033333, 4.583334, 4.750000, 4.9...
## $ `Wind Direction [º]` <dbl> 313.3343, 307.6674, 307.4994, 307...
## $ `Precipitation [mm]` <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ...
## $ `Atmospheric pressure [hPa]` <dbl> 1014.433, 1014.400, 1014.383, 101...
Y comprobamos que las variables que tenemos en train también las tenemos en test.
meteo_train_var <- names(meteo_2015_2016)
meteo_test_var <- names(test_meteo_2017)
all.equal.character(meteo_train_var, meteo_test_var)
## [1] "Lengths (10, 9) differ (string compare on first 9)"
rm(meteo_train_var)
rm(meteo_test_var)
El dataset de test de meteo tiene solo 9 variables, una menos que la de train. En el dataset de test no aparece el campo ‘Direct Radiation [W/m2]’. Tenemos que tener en cuenta este detalle si finalmente utilizamos este dataset para nuestros modelos.
3.3.2 Calidad de los datos.
En el Hackathon fuimos directamente a cruzar las tablas de producción e información meteorológica para tener lo antes posible un dataset de train para subir a la herramienta BIGml (además del dataset de producción nos quedamos sólo con la variable target: ‘Power_DC’). Por diversos problemas con el formato de los datos esta parte nos consumió muchísimo tiempo y la parte de exploración de datos fue reducida a la mínima expresión. En este caso vamos a dedicar más tiempo a este parte del análisis.
3.3.2.1 Dataset de producción “prod_2015_2016”.
3.3.2.1.1 Completitud de los datos.
En este apartado vamos a revisar el número de registros que tenemos, si varían con el tiempo y vamos a determinar la frecuencia de registros de los mismos.
3.3.2.1.2 Número y frecuencia de registros.
Comparamos el número de registros de 2016 contra 2015. En 2016 hay sensiblemente menos registros que en 2015.
registers_by_year <- prod_2015_2016 %>%
select(Datetime) %>%
group_by(year = year(prod_2015_2016$Datetime)) %>%
summarise(registers = n())
registers_by_year
## # A tibble: 2 x 2
## year registers
## <dbl> <int>
## 1 2015 242735
## 2 2016 177772
rm(registers_by_year)
Para tratar de ver con más detalle la naturaleza de estas diferencias echamos un vistazo a la la distribución por año-mes.
registers_by_year_month <- prod_2015_2016 %>%
select(Datetime) %>%
group_by(year = year(prod_2015_2016$Datetime),
month = month(prod_2015_2016$Datetime)) %>%
summarise(registers = n())
kable(registers_by_year_month) %>% kable_styling()
year | month | registers |
---|---|---|
2015 | 1 | 18730 |
2015 | 2 | 18297 |
2015 | 3 | 21094 |
2015 | 4 | 23460 |
2015 | 5 | 26183 |
2015 | 6 | 25146 |
2015 | 7 | 25908 |
2015 | 8 | 25252 |
2015 | 9 | 22380 |
2015 | 10 | 19067 |
2015 | 11 | 17218 |
2016 | 1 | 18492 |
2016 | 2 | 18250 |
2016 | 3 | 342 |
2016 | 6 | 19413 |
2016 | 7 | 26682 |
2016 | 8 | 25243 |
2016 | 9 | 17067 |
2016 | 10 | 17133 |
2016 | 11 | 18313 |
2016 | 12 | 16837 |
A destacar que en diciembre de 2015 y en abril y mayo de 2016 no hay ni un solo dato. Además en marzo de 2016 apenas hay registros. El resto de variaciones entre meses parecen estar simplemente relacionadas con las horas de sol, menos en invierno, más en verano.
Visualizamos los datos de la tabla en un gráfico. Pero antes añadimos los registros de año-meses que no aparecen con el valor ‘0’.
registers_by_year_month <- registers_by_year_month %>%
ungroup() %>%
add_row(year = c('2015', '2016', '2016'),
month = c('12', '4', '5'),
registers = '0') %>%
mutate(year = as.factor(year),
month = as.factor(month),
registers = as.numeric(registers))
registers_by_year_month_1 <- registers_by_year_month %>%
mutate(day = "01") %>%
unite(year_month, year, month, day, sep = '-') %>%
mutate(year_month = ymd(year_month))
ggplot(data = registers_by_year_month_1, aes(x = as.factor(year_month), y = registers, group = 1)) +
geom_line() +
theme(axis.text.x=element_text(angle=90,hjust=1,vjust=0.5, size = 10)) +
labs(x = 'Year-Month')
rm(registers_by_year_month)
Ejecutamos la función summary() para observar mejor la distribución de los datos.
summary(registers_by_year_month_1)
## year_month registers
## Min. :2015-01-01 Min. : 0
## 1st Qu.:2015-06-23 1st Qu.:17117
## Median :2015-12-16 Median :18611
## Mean :2015-12-16 Mean :17521
## 3rd Qu.:2016-06-08 3rd Qu.:23882
## Max. :2016-12-01 Max. :26682
Y lo representamos en un histograma.
ggplot(data = registers_by_year_month_1, aes(x = registers)) +
geom_histogram(bins = 23)
rm(registers_by_year_month_1)
Todos los meses, excepto 4 (los mencionados anteriormente), se encuentran entre los 18.000 y 27.000 registros.
Vamos a estimar la frecuencia de generación de los registros. Para ello restaremos a cada Datetime de cada registro su correspondiente Datetime anterior (Datetime_lagged) creando la variable time_diff, que nos dirá los minutos trascurridos entre cada registro. Una vez hecho esto hacemos un simple conteo de todas las diferencias entre registros.
register_frequency <- prod_2015_2016 %>%
select(Datetime) %>%
arrange(Datetime) %>% # Importante. Antes hay que ordenar los registros por Datetime.
mutate(Datetime_lagged = lag(Datetime)) %>%
mutate(time_diff = Datetime - Datetime_lagged) %>%
na.omit()
intervals_count <- register_frequency %>%
select(time_diff) %>%
group_by(time_diff) %>%
summarise(n = n()) %>%
arrange(desc(n))
kable(head(intervals_count, 20))
time_diff | n |
---|---|
1 mins | 419840 |
2 mins | 29 |
6 mins | 11 |
7 mins | 11 |
579 mins | 10 |
8 mins | 9 |
566 mins | 9 |
565 mins | 8 |
572 mins | 8 |
855 mins | 8 |
569 mins | 7 |
854 mins | 7 |
570 mins | 6 |
587 mins | 6 |
777 mins | 6 |
809 mins | 6 |
833 mins | 6 |
563 mins | 5 |
568 mins | 5 |
577 mins | 5 |
Lo que vemos es que en principio el registro de mediciones estaría programado para realizarse cada minuto, porque este es con diferencia el intervalo entre registros más frecuente.
Si efectivamente se registrase una medición cada minuto el número de mediciones entre 2015 y 2016 sería de 1.051.200. En la tabla hay 420.507 registros. Pero en principio si tenemos en cuenta las horas sin luz, en los que lógicamente no hay producción de electricidad, más los tres meses sin datos y los pocos datos de marzo de 2016 el número de registros en las tablas cuadran (en un caso real habría que hablar con los responsables de los datos para conocer las causas de la falta de datos; en un ejercicio como este no podemos ir más allá).
(two_year_minutes <- 2*365*24*60)
## [1] 1051200
Echamos un vistazo a las distribuciones de la duración de las paradas (excluyendo las de 1 minuto y las mayores de 15 horas).
stops <- intervals_count %>%
mutate(time_diff = as.numeric(time_diff)) %>%
filter(time_diff > 1,
time_diff < 900) # En 2019 el día más largo del año en Faro será el 21 de junio con 14 horas y 42 minutos (asi que filtramos por debajo de 15 horas)
summary(stops$time_diff)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 2.0 613.5 709.0 665.3 799.5 894.0
Lo vemos también en un histograma.
ggplot(data = stops, aes(x = time_diff)) +
geom_histogram(bins = 23)
El número total de paradas con duración compatible con paradas por falta de luz cuadra con los datos vistos hasta ahora.
night_stops <- intervals_count %>%
mutate(time_diff = as.numeric(time_diff)) %>%
filter(time_diff > 500) %>%
summarise(stops = sum(n))
night_stops
## # A tibble: 1 x 1
## stops
## <int>
## 1 580
rm(stops, night_stops, intervals_count)
3.3.2.1.3 Análisis de NAs (valores faltantes).
Generamos una tabla donde vemos hasta que punto cada variable del dataset está “completo” (un valor de 0.97 significa que la variable en cuestión tiene un 3% de NAs). Vemos que en general los registros presentes en la tabla tienen una alta completitud de datos.
data_nas <- prod_2015_2016 %>%
group_by(year = year(Datetime), month(Datetime)) %>%
summarise_all(funs(round(sum(!is.na(.))/n(), 2))) # We obtain the proportion of 'not NAs'
kable(data_nas) %>%
kable_styling() %>%
scroll_box()
year | month(Datetime) | Datetime | A_Vertical - Voltage DC [V] | A_Vertical - Current DC [A] | A_Vertical - Power DC [W] | A_Optimal - Voltage DC [V] | A_Optimal - Current DC [A] | A_Optimal - Power DC [W] | A_Horizontal - Voltage DC [V] | A_Horizontal - Current DC [A] | A_Horizontal - Power DC [W] | A_Vertical - Temperature [ºC] | A_Optimal - Temperature [ºC] | A_Horizontal - Temperature [ºC] | B_Vertical - Voltage DC [V] | B_Vertical - Current DC [A] | B_Vertical - Power DC [W] | B_Optimal - Voltage DC [V] | B_Optimal - Current DC [A] | B_Optimal - Power DC [W] | B_Horizontal - Voltage DC [V] | B_Horizontal - Current DC [A] | B_Horizontal - Power DC [W] | B_Vertical - Temperature [ºC] | B_Optimal - Temperature [ºC] | B_Horizontal - Temperature [ºC] |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
2015 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.96 | 0.96 | 0.96 | 1 | 1 | 1 |
2015 | 2 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2015 | 3 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.99 | 0.99 | 0.99 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2015 | 4 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2015 | 5 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.99 | 0.99 | 0.99 | 1 | 1 | 1 | 0.98 | 0.98 | 0.98 | 0.99 | 0.99 | 0.99 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 |
2015 | 6 | 1 | 0.98 | 0.98 | 0.98 | 0.99 | 0.99 | 0.99 | 0.99 | 0.99 | 0.99 | 1 | 1 | 1 | 0.99 | 0.99 | 0.99 | 0.99 | 0.99 | 0.99 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 |
2015 | 7 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.98 | 0.98 | 0.98 | 0.99 | 0.99 | 0.99 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 |
2015 | 8 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2015 | 9 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2015 | 10 | 1 | 0.95 | 0.95 | 0.95 | 0.97 | 0.97 | 0.97 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2015 | 11 | 1 | 0.95 | 0.95 | 0.95 | 0.97 | 0.97 | 0.97 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.96 | 0.96 | 0.96 | 1 | 1 | 1 |
2016 | 1 | 1 | 0.95 | 0.95 | 0.95 | 0.97 | 0.97 | 0.97 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.96 | 0.96 | 0.96 | 1 | 1 | 1 |
2016 | 2 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2016 | 3 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 | 0.98 | 0.98 | 0.98 | 0.99 | 0.99 | 0.99 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2016 | 6 | 1 | 0.98 | 0.98 | 0.98 | 0.99 | 0.99 | 0.99 | 0.99 | 0.99 | 0.99 | 1 | 1 | 1 | 0.99 | 0.99 | 0.99 | 0.99 | 0.99 | 0.99 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 |
2016 | 7 | 1 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.98 | 0.98 | 0.98 | 0.99 | 0.99 | 0.99 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 |
2016 | 8 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.98 | 0.98 | 0.98 | 0.99 | 0.99 | 0.99 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2016 | 9 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2016 | 10 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 0.98 | 1 | 1 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2016 | 11 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 |
2016 | 12 | 1 | 0.96 | 0.96 | 0.96 | 0.98 | 0.98 | 0.98 | 0.97 | 0.97 | 0.97 | 1 | 1 | 1 | 0.97 | 0.97 | 0.97 | 0.98 | 0.98 | 0.98 | 0.96 | 0.96 | 0.96 | 1 | 1 | 1 |
3.3.2.1.4 Transformaciones de la tabla.
Recordamos las variables del dataset de producción.
glimpse(prod_2015_2016)
## Observations: 420,507
## Variables: 25
## $ Datetime <dttm> 2015-01-09 12:52:00, 2015-0...
## $ `A_Vertical - Voltage DC [V]` <dbl> 29.35, 29.10, 29.10, 29.35, ...
## $ `A_Vertical - Current DC [A]` <dbl> 6.4125, 6.3975, 6.3325, 6.41...
## $ `A_Vertical - Power DC [W]` <dbl> 188.206875, 186.167250, 184....
## $ `A_Optimal - Voltage DC [V]` <dbl> 28.75, 28.45, 28.45, 28.75, ...
## $ `A_Optimal - Current DC [A]` <dbl> 7.1700, 7.1925, 7.0925, 7.21...
## $ `A_Optimal - Power DC [W]` <dbl> 206.13750, 204.62662, 201.78...
## $ `A_Horizontal - Voltage DC [V]` <dbl> 29.25, 29.50, 29.45, 29.50, ...
## $ `A_Horizontal - Current DC [A]` <dbl> 4.3225, 4.2525, 4.1825, 4.30...
## $ `A_Horizontal - Power DC [W]` <dbl> 126.43313, 125.44875, 123.17...
## $ `A_Vertical - Temperature [ºC]` <dbl> 32.6, 32.4, 33.2, 32.2, 32.8...
## $ `A_Optimal - Temperature [ºC]` <dbl> 28.8, 28.6, 29.4, 28.2, 27.8...
## $ `A_Horizontal - Temperature [ºC]` <dbl> 26.5, 26.1, 26.8, 26.4, 26.1...
## $ `B_Vertical - Voltage DC [V]` <dbl> 27.55, 27.60, 27.40, 27.40, ...
## $ `B_Vertical - Current DC [A]` <dbl> 7.0925, 7.0475, 7.0100, 7.14...
## $ `B_Vertical - Power DC [W]` <dbl> 195.39837, 194.51100, 192.07...
## $ `B_Optimal - Voltage DC [V]` <dbl> 27.85, 28.05, 27.85, 28.05, ...
## $ `B_Optimal - Current DC [A]` <dbl> 7.2225, 7.1125, 7.0450, 7.17...
## $ `B_Optimal - Power DC [W]` <dbl> 201.14663, 199.50563, 196.20...
## $ `B_Horizontal - Voltage DC [V]` <dbl> 29.05, 29.05, 28.75, 28.95, ...
## $ `B_Horizontal - Current DC [A]` <dbl> 4.1775, 4.1275, 4.1125, 4.19...
## $ `B_Horizontal - Power DC [W]` <dbl> 121.356375, 119.903875, 118....
## $ `B_Vertical - Temperature [ºC]` <dbl> 31.2, 31.5, 31.9, 31.7, 32.2...
## $ `B_Optimal - Temperature [ºC]` <dbl> 30.2, 30.0, 30.9, 29.9, 30.7...
## $ `B_Horizontal - Temperature [ºC]` <dbl> 25.3, 24.9, 25.7, 25.1, 25.6...
Las variables parecen todas tener el formato adecuado. Lo único que llama la atención es cómo distingue qué valores corresponden a los paneles de tipo A de los de tipo B, con una “A” o una “B” al principio de cada nombre de variable, y cómo incluye también la información de la orientación, también en el nombre de la variable, con los términos “Horizontal”, “Vertical” y “Optimal”. Quizás una forma mejor de ordenar los datos sería creando otras dos variables de formato factor, una para distinguir los paneles y otra la configuración.
Para ello lo primero que haremos será transformar a formato “largo” la tabla, reduciendo las 25 variables actuales a únicamente 2: ‘Datetime’ y ‘Measure’. Y justo después de esta transformación creamos tres nuevas variables que se referirán al tipo de panel (panel_type), la configuración del mismo (set_type) y finalmente el tipo de medición en cuestión (measure):
prod_2015_2016_long <- prod_2015_2016 %>%
gather('panel_set_measure', 'value', 2:25) %>%
rename(datetime = Datetime) %>%
mutate(panel_type = as.factor(str_sub(panel_set_measure, start = 1, end = 1)),
set_type = as.factor(ifelse(grepl('Vertical', panel_set_measure), 'Vertical',
ifelse(grepl('Horizontal', panel_set_measure), 'Horizontal', 'Optimal'))),
measure = as.factor(ifelse(grepl('Temperature', panel_set_measure), 'Temperature_DC_C',
ifelse(grepl('Voltage', panel_set_measure), 'Voltage_DC_V',
ifelse(grepl('Power', panel_set_measure), 'Power_DC_W', 'Current_DC_A'))))) %>%
select(-panel_set_measure)
Así ya nos queda una tabla con un formato más ordenado.
kable(head(prod_2015_2016_long, 10))
datetime | value | panel_type | set_type | measure |
---|---|---|---|---|
2015-01-09 12:52:00 | 29.35 | A | Vertical | Voltage_DC_V |
2015-01-09 12:14:00 | 29.10 | A | Vertical | Voltage_DC_V |
2015-01-09 13:19:00 | 29.10 | A | Vertical | Voltage_DC_V |
2015-01-09 12:41:00 | 29.35 | A | Vertical | Voltage_DC_V |
2015-01-09 13:07:00 | 29.10 | A | Vertical | Voltage_DC_V |
2015-01-09 12:57:00 | 29.00 | A | Vertical | Voltage_DC_V |
2015-01-09 13:04:00 | 29.30 | A | Vertical | Voltage_DC_V |
2015-01-09 13:20:00 | 29.10 | A | Vertical | Voltage_DC_V |
2015-01-09 13:16:00 | 29.10 | A | Vertical | Voltage_DC_V |
2015-01-09 12:48:00 | 29.30 | A | Vertical | Voltage_DC_V |
Echamos un vistazo con un summary()
summary(prod_2015_2016_long)
## datetime value panel_type
## Min. :2015-01-01 07:40:00 Min. : 0.00 A:5046084
## 1st Qu.:2015-05-28 18:54:00 1st Qu.: 6.50 B:5046084
## Median :2015-10-08 17:39:00 Median : 26.90
## Mean :2015-12-04 18:29:34 Mean : 35.51
## 3rd Qu.:2016-07-19 14:48:00 3rd Qu.: 34.40
## Max. :2016-12-29 17:31:00 Max. :307.64
## NA's :183579
## set_type measure
## Horizontal:3364056 Current_DC_A :2523042
## Optimal :3364056 Power_DC_W :2523042
## Vertical :3364056 Temperature_DC_C:2523042
## Voltage_DC_V :2523042
##
##
##
Aunque mejor sería que los valores de la medición de cada variable tenga cada una su propia columna. Así que hacemos una última transformación.
prod_2015_2016_long <- prod_2015_2016_long %>%
spread(measure, value)
kable(head(prod_2015_2016_long))
datetime | panel_type | set_type | Current_DC_A | Power_DC_W | Temperature_DC_C | Voltage_DC_V |
---|---|---|---|---|---|---|
2015-01-01 07:40:00 | A | Horizontal | NA | NA | 5.4 | NA |
2015-01-01 07:40:00 | A | Optimal | NA | NA | 6.0 | NA |
2015-01-01 07:40:00 | A | Vertical | NA | NA | 5.8 | NA |
2015-01-01 07:40:00 | B | Horizontal | NA | NA | 5.2 | NA |
2015-01-01 07:40:00 | B | Optimal | NA | NA | 6.2 | NA |
2015-01-01 07:40:00 | B | Vertical | NA | NA | 6.5 | NA |
Aplicamos las mismas transformaciones al dataset de test.
test_prod_2017_long <- test_prod_2017 %>%
gather('panel_set_measure', 'value', 2:25) %>%
rename(datetime = Datetime) %>%
mutate(panel_type = as.factor(str_sub(panel_set_measure, start = 1, end = 1)),
set_type = as.factor(ifelse(grepl('Vertical', panel_set_measure), 'Vertical',
ifelse(grepl('Horizontal', panel_set_measure), 'Horizontal', 'Optimal'))),
measure = as.factor(ifelse(grepl('Temperature', panel_set_measure), 'Temperature_DC_C',
ifelse(grepl('Voltage', panel_set_measure), 'Voltage_DC_V',
ifelse(grepl('Power', panel_set_measure), 'Power_DC_W', 'Current_DC_A'))))) %>%
select(-panel_set_measure) %>%
spread(measure, value)
3.3.2.2 Dataset de datos meteorológicos “meteo_2015_2016”.
PENDIENTE
3.3.3 Exploración y visualizaciones.
3.3.3.1 Dataset de producción “prod_2015_2016”.
Para echar un vistazo gráficamente a las relaciones entre variables vamos a tomar una muestra al azar más manejable del conjunto total de datos para poder “dibujar” de forma más rápida y ágil.
set.seed = 42
sample_prod <- sample_n(prod_2015_2016_long, 10000)
Generamos primero un cuadro donde podemos observar las distribuciones de cada variable (diagonal principal) y las relaciones entre pares de variables con gráficos de dispersión y sus correspondientes coeficientes de correlación lineal.
ggpairs(data = sample_prod, columns = 4:7)
Lo que más salta a la vista es la fortísima correlación lineal entre la variable Power_DC_W y Current_DC_A, 0.997, que también se observa muy claramente en el gráfico de dispersión correspondiente (también se ve lo que parece una pequeña anomalía de la tendencia general, la nube de puntos con una pendiente menor que se aparta un poquito del grupo general) y en sus correspondientes histogramas, que aunque en muy diferentes escalas son practicamente iguales.
El resto de pares de variables también presentan evidentes relaciones entre sí.
Vemos también en los siguientes gráficos cómo se comportan las cuatros variables a lo largo del tiempo. En este caso sólo utilizamos datos procedentes del tipo de panel A y orientación Optimal. Como los datos están limitados a un periodo muy corto (10 días) no es necesario muestrear.
prod_optimal_A <- prod_2015_2016_long %>%
filter(set_type == "Optimal",
panel_type == "A")
minute_evolution_W_A <- ggplot(data = prod_optimal_A %>% filter(datetime > '2016-09-30 23:59:59',
datetime <= '2016-10-10 23:59:59'), aes(x = datetime, y = Power_DC_W)) + geom_line()
minute_evolution_A_A <- ggplot(data = prod_optimal_A %>% filter(datetime > '2016-09-30 23:59:59',
datetime <= '2016-10-10 23:59:59'), aes(x = datetime, y = Current_DC_A)) + geom_line()
minute_evolution_V_A <- ggplot(data = prod_optimal_A %>% filter(datetime > '2016-09-30 23:59:59',
datetime <= '2016-10-10 23:59:59'), aes(x = datetime, y = Voltage_DC_V)) + geom_line()
minute_evolution_C_A <- ggplot(data = prod_optimal_A %>% filter(datetime > '2016-09-30 23:59:59',
datetime <= '2016-10-10 23:59:59'), aes(x = datetime, y = Temperature_DC_C)) + geom_line()
grid.arrange(minute_evolution_W_A,
minute_evolution_A_A,
minute_evolution_V_A,
minute_evolution_C_A,
ncol = 1)
En esta otra visualización también es evidente la similitud del comportamiento de las variables Current_DC_A y Power_DC_W. Así que antes de seguir explorando los datos, lo que tendríamos que hacer es a hacer algo que tendríamos que haber hecho nada más empezar el análisis: Definir las variables y consultar las posibles relaciones que pudiesen existir entre ellas.
Temperature_DC_C: Temperatura en grados centígrados del panel solar.
Current_DC_A: Corriente continua. Medida en amperios. Nos indica la intensidad de la corriente eléctrica.
Voltage_DC_V: Voltaje, también denominado como tensión eléctrica o diferencia de potencial, cuantifica la diferencia de potencial eléctrico entre dos puntos.
Power_DC_W: La potencia eléctrica. Medida en vatios. Es la proporción por unidad de tiempo, o ritmo, con la cual la energía eléctrica es transferida por un circuito eléctrico.
La potencia eléctrica, o los vatios, es la variable target de este problema. Es decir, la variable que tenemos que predecir. Ya hemos visto que se relaciona de forma directa con la variable Current_DC_A (amperios). Y con una sencilla búsqueda en Google podemos ver, por ejemplo aquí, cuál es la naturaleza de esa relación: Voltios (V) x Amperios (A) = Vatios (W)
Es decir, que ya tendríamos nuestro modelo. Y en este caso perfecto.
Realizamos a continuación una sencilla comprobación.
Primero hacemos la predicción de la potencia para el panel de tipo A y orientación Optimal y obtenemos su MAE (Mean Absolute Error).
prod_optimal_A <- prod_2015_2016_long %>%
filter(set_type == "Optimal",
panel_type == "A") %>%
na.omit()
pred_prod_optimal_A <- prod_optimal_A %>%
select(Current_DC_A, Voltage_DC_V, Power_DC_W) %>%
mutate(W_prediction = Current_DC_A * Voltage_DC_V, # Nuestro modelo
abs_error = trunc(abs(W_prediction - Power_DC_W),6)) # Los errores
# Y para calcular el MAE de nuestro modelo lo único que nos queda por hacer
# es obtener la media de la columna abs_error
MAE <- mean(pred_prod_optimal_A$abs_error)
MAE
## [1] 0
Como es normal tenemos obtenemos un MAE igual a cero.
Repetimos la misma operación para el panel de tipo B
prod_optimal_B<- prod_2015_2016_long %>%
filter(set_type == "Optimal",
panel_type == "B") %>%
na.omit()
pred_prod_optimal_B <- prod_optimal_B %>%
select(Current_DC_A, Voltage_DC_V, Power_DC_W) %>%
mutate(W_prediction = Current_DC_A * Voltage_DC_V, # Nuestro modelo
abs_error = trunc(abs(W_prediction - Power_DC_W),6)) # Los errores
# Y para calcular el MAE de nuestro modelo lo único que nos queda por hacer
# es obtener la media de la columna abs_error
MAE <- mean(pred_prod_optimal_A$abs_error)
MAE
## [1] 0
Y obtenemos un MAE de cero también.
Ahora simplemente restaría hacer la predicción para los 7 primeros días de 2017.
# pendiente
Resuelto el problema exploramos los datos un poco más. Y generamos tres conjuntos de gráficos donde enfrentamos a la variable target a cada una de las otras tres, pero introduciendo además las variables panel_type y set_type. Las imágenes obtenidas son muy similares a las vistas anteriormente. Como elemento destacable vemos que esa “irregularidad” que veíamos entre la variable Power_DC_W y Current_DC_A proviene principalmente de la configuración horizontal del panel tipo A.
ggplot(data = sample_prod, aes(x = Current_DC_A, y = Power_DC_W, color = set_type)) +
geom_point(alpha = 0.5) +
facet_grid(panel_type ~ set_type)
ggplot(data = sample_prod, aes(x = Voltage_DC_V, y = Power_DC_W, color = set_type)) +
geom_point(alpha = 0.5) +
facet_grid(panel_type ~ set_type)
ggplot(data = sample_prod, aes(x = Temperature_DC_C, y = Power_DC_W, color = set_type)) +
geom_point(alpha = 0.5) +
facet_grid(panel_type ~ set_type)
Y generamos de nuevo la tabla de correlaciones y distribuciones, pero en esta ocasión únicamente para los dos conjuntos a predecir Configuración ‘Optimal’ y tipo de panel ‘A’ y ‘B’.
prod_optimal_A <- sample_prod %>%
filter(set_type == "Optimal",
panel_type == "A")
ggpairs(data = prod_optimal_A, columns = 4:7)
prod_optimal_B <- sample_prod %>%
filter(set_type == "Optimal",
panel_type == "B")
ggpairs(data = prod_optimal_B, columns = 4:7)
3.4 Generación de modelos base.
El problema ya estaría resuelto. Pero de todas formas vamos a comprobar hasta que punto un modelo sencillo de regresión final se aproximaría a la solución.
Para ello vamos a dividir la tabla de producción que tenemos en formato semilargo en dos datasets, uno para realizar el training y otro para ir realizando validaciones de los modelos y poder detectar problemas de overfitting.
Train period: 01-01-2015 - 30-09-2016 Validation period: 01-10-2016 - 31-12-2016
3.4.1 Modelos base para paneles A
train_period_A <- prod_2015_2016_long %>%
filter(datetime <= '2016-09-30 23:59:59',
panel_type == "A",
set_type == "Optimal") %>%
select(-panel_type,
-set_type) %>%
na.omit()
validation_period_A <- prod_2015_2016_long %>%
filter(datetime > '2016-09-30 23:59:59',
panel_type == "A",
set_type == "Optimal") %>%
select(-panel_type,
-set_type) %>%
na.omit()
Vemos la tabla de training.
head(train_period_A)
## # A tibble: 6 x 5
## datetime Current_DC_A Power_DC_W Temperature_DC_C Voltage_DC_V
## <dttm> <dbl> <dbl> <dbl> <dbl>
## 1 2015-01-01 07:47:00 0.0725 1.70 6 23.4
## 2 2015-01-01 07:48:00 0.0775 1.81 5.9 23.3
## 3 2015-01-01 07:49:00 0.0825 1.93 5.9 23.4
## 4 2015-01-01 07:50:00 0.0875 2.05 5.8 23.4
## 5 2015-01-01 07:51:00 0.095 2.22 5.7 23.4
## 6 2015-01-01 07:52:00 0.1 2.34 5.7 23.4
3.4.1.1 Linear regression
Siguiendo la teoría vamos a intentar explicar la variable Power_DC_W en función de las variables Current_DC_A y Voltage_DC_V
model_lr_1 <- lm(data = train_period_A, Power_DC_W ~ Current_DC_A + Voltage_DC_V)
print(model_lr_1)
##
## Call:
## lm(formula = Power_DC_W ~ Current_DC_A + Voltage_DC_V, data = train_period_A)
##
## Coefficients:
## (Intercept) Current_DC_A Voltage_DC_V
## -30.77 27.44 1.20
summary(model_lr_1)
##
## Call:
## lm(formula = Power_DC_W ~ Current_DC_A + Voltage_DC_V, data = train_period_A)
##
## Residuals:
## Min 1Q Median 3Q Max
## -46.297 -2.101 -0.899 1.777 27.308
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -30.769038 0.073713 -417.4 <2e-16 ***
## Current_DC_A 27.444885 0.002733 10042.1 <2e-16 ***
## Voltage_DC_V 1.200082 0.002770 433.3 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3.93 on 361002 degrees of freedom
## Multiple R-squared: 0.9969, Adjusted R-squared: 0.9969
## F-statistic: 5.836e+07 on 2 and 361002 DF, p-value: < 2.2e-16
Obtenemos un p-value claramente por debajo de 0,05. Las relaciones que establece el modelo se consideran significativas Y el R cuadrado es casi igual a 1, es decir, el modelo está explicando prácticamente toda la variabilidad de la variable target Power_DC_W.
Vamos a calcular el MAE, que forzosamente tendrá que ser muy bajo.
# Extraemos los fitted_values (las predicciones) del modelo para compararlos con los valores reales
predicted_values <- as.data.frame(model_lr_1$fitted.values)
real_values <- as.data.frame(train_period_A$Power_DC_W)
mae_train <- bind_cols(predicted_values,
real_values) %>%
rename(predicted_values = 'model_lr_1$fitted.values',
real_values = 'train_period_A$Power_DC_W') %>%
mutate(abs_error = abs(real_values - predicted_values)) %>%
summarise(mae = mean(abs_error))
mae_train
## mae
## 1 2.856888
Utilizamos este modelo inicial para hacer las predicciones con el periodo de validación
validation_predicted_values <- as.data.frame(predict(model_lr_1, validation_period_A))
validation_real_values <- as.data.frame(validation_period_A$Power_DC_W)
mae_validation <- bind_cols(validation_predicted_values,
validation_real_values) %>%
rename(predicted_values = 'predict(model_lr_1, validation_period_A)',
real_values = 'validation_period_A$Power_DC_W') %>%
mutate(abs_error = abs(real_values - predicted_values)) %>%
summarise(mae = mean(abs_error))
mae_validation
## mae
## 1 2.923732
rss <- sum((validation_predicted_values - validation_real_values) ^ 2) ## residual sum of squares
tss <- sum((validation_real_values - mean(validation_real_values$`validation_period_A$Power_DC_W`)) ^ 2) ## total sum of squares
R_Squared <- 1 - rss/tss
R_Squared
## [1] 0.9969133
Al validar el modelo con el último trimestre de 2016 obtenemos resultados muy parecidos. El modelo es casi perfecto.
3.4.2 Modelos base para paneles B
PENDIENTE
3.5 Descripción de la generación de los modelos con BIGml.
¿Qué hubiese pasado si no nos hubiésemos percatado de la relación directa entre las variables de la tabla de producción y hubiésemos cruzado todas las variables y llevado la tabla resultante a BIGml para intentar crear nuestro modelo?
PENDIENTE