Chapter 2 Small Scale Example

Relationship between: Wind Speed and Energy Production of Wind Turbines

2.1 Intro

will insert a introduction here

…

We will use wind speed data from 6 locations. We want to see whether using temporal and nonlinear features improve our forecast accuracy.

The relationships that we want to analyse are as follows:

  1. Linear relationship between the wind speed and energy production
  2. Temporal (e.g. features from past and (forecasts) from future)
  3. Non-linear (e.g. square of features)
  4. Combination of Temporal and Non-linear
  5. Polynomial
  6. Lagged version of Polynomial
  7. Polynomials of Temporal features

In the following sections we will show that all these relationships matter for wind power forecasting.

Also we will see that dimensions increase exponentially as we try to derive more features. So we will have a motivation for dimension reduction.

2.1.1 The Data

Our data has much more locations and ranges over a longer time. However, for this report we have reduced size for easiness, also we will see that even reduced size will not be small enough.

All 6 datasets have 6 months of observations. First 5 months will be used for training and the last month will be used for test.

5 of our 6 datasets have 9 locations (3x3 grid) and dataset-1 has 4 locations (2x2).

Here is a look of our datasets.

head(dt1)
##          date hour production     loc1     loc2     loc3      loc4
## 1: 2017-01-02    0          0 1.490839 1.184061 1.141621 0.4410216
## 2: 2017-01-02    1          0 1.700052 1.307793 1.385103 0.6289321
## 3: 2017-01-02    2          0 1.910143 1.432977 1.628786 0.8169388
## 4: 2017-01-02    3          0 2.120849 1.559263 1.872592 1.0049876
## 5: 2017-01-02    4          0 2.225062 1.552668 2.059827 0.9891467
## 6: 2017-01-02    5          0 2.332381 1.547259 2.249153 0.9741549
head(dt2)
##          date hour production     loc1     loc2     loc3     loc4     loc5     loc6     loc7     loc8     loc9
## 1: 2017-01-02    0       7.00 2.420620 2.644277 2.922619 2.624881 3.144026 2.994879 2.288274 2.910344 3.524599
## 2: 2017-01-02    1       7.00 2.415885 2.607095 2.823119 2.617361 3.118926 3.009090 2.321575 2.935492 3.521327
## 3: 2017-01-02    2      10.00 2.411182 2.570002 2.723692 2.612287 3.095949 3.023605 2.354877 2.962141 3.521237
## 4: 2017-01-02    3      20.00 2.406512 2.533002 2.624348 2.609674 3.075142 3.038421 2.388179 2.990251 3.524330
## 5: 2017-01-02    4      27.00 2.522292 2.622637 2.607378 2.641233 3.119695 3.146457 2.564588 3.112134 3.559182
## 6: 2017-01-02    5      17.09 2.638823 2.712524 2.590410 2.672959 3.164253 3.254569 2.741650 3.234055 3.594219

2.1.2 Linear

It is clear that wind speed can give us some idea about the wind energy production. Let’s see:

dfit1 = cv.glmnet(x = as.matrix(dt1[,-c(1:3)]), y = dt1$production, type.measure = "mae")
pred.test1 = predict(dfit1, as.matrix(dte1[,-c(1:3)]), s = "lambda.1se")
predict(dfit1,type="coef") # coefficients
## 5 x 1 sparse Matrix of class "dgCMatrix"
##             lambda.1se
## (Intercept) -13.913447
## loc1         -2.136017
## loc2         -1.279383
## loc3         -1.319780
## loc4         11.030869
print(paste("Train Error: ", min(dfit1$cvup)), sep = "") # train error
## [1] "Train Error:  16.08646661956"
print(paste("Test Error: ", mean(abs(pred.test1 - dte1$production))), sep = "") # test error
## [1] "Test Error:  13.4394665091161"

We can see that our model does not only depend on the “intercept” also we have seen that test error is not too bad (actually it is better than the train error which we will discuss a little later.)

Before looking at the all results, here is our workflow:

# fit
dfit1 = cv.glmnet(x = as.matrix(dt1[,-c(1:3)]), y = dt1$production, type.measure = "mae")
# train error
error.train1 = min(dfit1$cvup)
# prediction
pred.test1 = predict(dfit1, as.matrix(dte1[,-c(1:3)]), s = "lambda.1se")
# test error
error.test1 = mean(abs(pred.test1 - dte1$production))
# result
res1 = data.table(station = "aliaga", model = "base", train_error = error.train1, test_error = error.test1)
# big result table
dresults = rbind(dresults, res1)

Now, let’s look at the results from all 6 stations:

dresults %>% filter(model == "base") # we are filtering it but you don't need to as we just have results of the base models right now.
##    station model train_error test_error
## 1:  aliaga  base   16.115594  13.446084
## 2:   bares  base    8.368780   6.469539
## 3:   dinar  base   25.054550  19.934007
## 4:  geycek  base   21.612130  18.296658
## 5:    soke  base    7.570961   8.648788
## 6:    soma  base   14.451429  13.176678

These results are our base results. We are expecting to outperform the base models in the following sections.

You may have noticed that test error is less than train error in 5 of 6 stations. It is highly likely because of the train/test splits we are using. The train set have observations from january to may which contains more instability compared to test dates (june). This is actually not a good practice but we ensure you that in the “bigger” project we did not have such results. Just for the reduced version, we may compare our models with their train results and use the test results just for checking a possible overfit.

2.1.3 Lagged

Thinking about the nature of wind as a fluid, we think that the wind speed from past and (forecasts) from the future would be also related.

2.1.3.1 Some Details:

A custom function for deriving lagged datasets:

createLagged <- function(data, lags, other.head = 0, other.tail = 0) {
    # remove 0 if it exists
    # get min/max for removing first/last rows for lag/lead
    minlag = min(0,lags)
    maxlag = max(0,lags)
    startN = 1 - minlag
    endN = nrow(data) - maxlag
    # seperating others
    other = c(other.head, other.tail)
    tobe.head = data[(startN:endN),..other.head]
    tobe.tail = data[(startN:endN),..other.tail]
    dat = data.table() # empty data table
    # adding lagged values one by one (column by column)
    for(i in lags){
        lagdat = data[((startN+i):(endN+i)),-..other]
        if(i<0){
            lagname = paste('lag',-i,sep='')
            colnames(lagdat) = paste(colnames(lagdat),lagname,sep='_')
        } else if(i>0){
            lagname = paste('lead',i,sep='')
            colnames(lagdat) = paste(colnames(lagdat),lagname,sep='_')
        }
        dat = cbind(dat,lagdat)
    }
    dat = cbind(tobe.head,dat,tobe.tail)
    return(dat)
}
# deriving lagged datasets
dt_lag1 = createLagged(data = dt1, lags = c(-1:1), other.head = c(1:3), other.tail = 0)
head(dt_lag1)
##          date hour production loc1_lag1 loc2_lag1 loc3_lag1 loc4_lag1     loc1     loc2     loc3      loc4 loc1_lead1
## 1: 2017-01-02    1          0  1.490839  1.184061  1.141621 0.4410216 1.700052 1.307793 1.385103 0.6289321   1.910143
## 2: 2017-01-02    2          0  1.700052  1.307793  1.385103 0.6289321 1.910143 1.432977 1.628786 0.8169388   2.120849
## 3: 2017-01-02    3          0  1.910143  1.432977  1.628786 0.8169388 2.120849 1.559263 1.872592 1.0049876   2.225062
## 4: 2017-01-02    4          0  2.120849  1.559263  1.872592 1.0049876 2.225062 1.552668 2.059827 0.9891467   2.332381
## 5: 2017-01-02    5          0  2.225062  1.552668  2.059827 0.9891467 2.332381 1.547259 2.249153 0.9741549   2.442396
## 6: 2017-01-02    6          0  2.332381  1.547259  2.249153 0.9741549 2.442396 1.543049 2.440082 0.9600521   2.311337
##    loc2_lead1 loc3_lead1 loc4_lead1
## 1:   1.432977   1.628786  0.8169388
## 2:   1.559263   1.872592  1.0049876
## 3:   1.552668   2.059827  0.9891467
## 4:   1.547259   2.249153  0.9741549
## 5:   1.543049   2.440082  0.9600521
## 6:   1.345251   2.382790  0.6608412
dim(dt_lag1)
## [1] 3598   15
dim(dt_lag2)
## [1] 3598   30

Notice that looking at only t-1 and t+1 triples the size of our data. Thinking about a time window of 7 hours may be problematic with bigger datasets.

2.1.3.2 Results:

Our approach is again same. We again used cv.glmnet().

dresults[order(station)] %>% filter(model %in% c("base","lagged"))
##     station  model train_error test_error
##  1:  aliaga   base   16.115594  13.446084
##  2:  aliaga lagged   15.819267  13.337983
##  3:   bares   base    8.368780   6.469539
##  4:   bares lagged    8.115166   6.476837
##  5:   dinar   base   25.054550  19.934007
##  6:   dinar lagged   24.704932  19.893682
##  7:  geycek   base   21.612130  18.296658
##  8:  geycek lagged   21.413134  17.767683
##  9:    soke   base    7.570961   8.648788
## 10:    soke lagged    7.451066   8.643861
## 11:    soma   base   14.451429  13.176678
## 12:    soma lagged   14.248784  13.019966

We see better results in all of the stations.

2.1.4 Powered

Recall the formula for “kinetic energy”: \[ E_k = \frac{1}{2} m v^2 \text{, where $m$ is mass and $v$ is velocity.} \]

We can see that square of velocity is related with kinetic energy. Also previous studies show that cube of wind velocity is also correlated with wind energy production however we will be only using squares for sake of simplicity.

Some information about the powered datasets:

head(dt_pow1)
##          date hour production     loc1     loc2     loc3      loc4  loc1_p2  loc2_p2  loc3_p2   loc4_p2
## 1: 2017-01-02    0          0 1.490839 1.184061 1.141621 0.4410216 2.222600 1.402001 1.303299 0.1945001
## 2: 2017-01-02    1          0 1.700052 1.307793 1.385103 0.6289321 2.890178 1.710323 1.918511 0.3955556
## 3: 2017-01-02    2          0 1.910143 1.432977 1.628786 0.8169388 3.648644 2.053423 2.652944 0.6673890
## 4: 2017-01-02    3          0 2.120849 1.559263 1.872592 1.0049876 4.498000 2.431300 3.506600 1.0100000
## 5: 2017-01-02    4          0 2.225062 1.552668 2.059827 0.9891467 4.950900 2.410778 4.242889 0.9784111
## 6: 2017-01-02    5          0 2.332381 1.547259 2.249153 0.9741549 5.440000 2.394011 5.058689 0.9489778
dim(dt_pow1)
## [1] 3600   11
dim(dt_pow2)
## [1] 3600   21

Note that this also doubles the size of our dataset. (what will happen when we will combine lag and power :) )

Skipping the details, here are our results:

dresults[order(station)] %>% filter(model %in% c("base","lagged","powered"))
##     station   model train_error test_error
##  1:  aliaga    base   16.115594  13.446084
##  2:  aliaga  lagged   15.819267  13.337983
##  3:  aliaga powered   15.929062  13.466087
##  4:   bares    base    8.368780   6.469539
##  5:   bares  lagged    8.115166   6.476837
##  6:   bares powered    8.187200   6.652139
##  7:   dinar    base   25.054550  19.934007
##  8:   dinar  lagged   24.704932  19.893682
##  9:   dinar powered   23.836830  20.203461
## 10:  geycek    base   21.612130  18.296658
## 11:  geycek  lagged   21.413134  17.767683
## 12:  geycek powered   20.743009  18.897305
## 13:    soke    base    7.570961   8.648788
## 14:    soke  lagged    7.451066   8.643861
## 15:    soke powered    6.883059   8.060418
## 16:    soma    base   14.451429  13.176678
## 17:    soma  lagged   14.248784  13.019966
## 18:    soma powered   13.869595  13.363834

We again see that adding powered features gives an improvement over the base case. Also, there is no visible ranking between lagged and powered models. In some stations lagged model is better, in others powered one significantly improves the performance.

Let’s combine them:

2.1.5 Lagged and Powered

We predict that using both temporal and nonlinear features will give us a better model. However with this section the dimensions will start to increase a lot.

dim(dt_lagpow1)
## [1] 3598   27
dim(dt_lagpow2)
## [1] 3598   57

We used to have 4 or 9 features. Now we have 24 and 54 features. And note that we could have used a wider time window and take the cube of these features.

With more detailed features our performance increased in all stations:

dresults[order(station)] %>% filter(model %in% c("base","lagged","powered", "lagged + powered"))
##     station            model train_error test_error
##  1:  aliaga             base   16.115594  13.446084
##  2:  aliaga           lagged   15.819267  13.337983
##  3:  aliaga          powered   15.929062  13.466087
##  4:  aliaga lagged + powered   15.613828  13.392450
##  5:   bares             base    8.368780   6.469539
##  6:   bares           lagged    8.115166   6.476837
##  7:   bares          powered    8.187200   6.652139
##  8:   bares lagged + powered    8.003236   6.630835
##  9:   dinar             base   25.054550  19.934007
## 10:   dinar           lagged   24.704932  19.893682
## 11:   dinar          powered   23.836830  20.203461
## 12:   dinar lagged + powered   23.287946  19.776765
## 13:  geycek             base   21.612130  18.296658
## 14:  geycek           lagged   21.413134  17.767683
## 15:  geycek          powered   20.743009  18.897305
## 16:  geycek lagged + powered   20.441426  18.363553
## 17:    soke             base    7.570961   8.648788
## 18:    soke           lagged    7.451066   8.643861
## 19:    soke          powered    6.883059   8.060418
## 20:    soke lagged + powered    6.789591   8.074436
## 21:    soma             base   14.451429  13.176678
## 22:    soma           lagged   14.248784  13.019966
## 23:    soma          powered   13.869595  13.363834
## 24:    soma lagged + powered   13.551715  13.056804
##     station            model train_error test_error

2.1.6 Polynomial

As we have shown that all these derived features improve our performance, also again thinking about the fluid dynamics, we think that polynomials of wind speed features would give another nonlinear and temporal perspective.

We will be using polym() function for creating our polynomial data:

dt_poly1 = cbind(dt1[,1:3], polym(as.matrix(dt1[,-c(1:3)]),degree = 2, raw = TRUE)) # raw=TRUE means:: dont' scale

Checking the dimensions:

head(dt_poly1)
##          date hour production  1.0.0.0  2.0.0.0  0.1.0.0  1.1.0.0  0.2.0.0  0.0.1.0  1.0.1.0  0.1.1.0  0.0.2.0
## 1: 2017-01-02    0          0 1.490839 2.222600 1.184061 1.765245 1.402001 1.141621 1.701973 1.351750 1.303299
## 2: 2017-01-02    1          0 1.700052 2.890178 1.307793 2.223317 1.710323 1.385103 2.354748 1.811428 1.918511
## 3: 2017-01-02    2          0 1.910143 3.648644 1.432977 2.737190 2.053423 1.628786 3.111214 2.334013 2.652944
## 4: 2017-01-02    3          0 2.120849 4.498000 1.559263 3.306960 2.431300 1.872592 3.971484 2.919862 3.506600
## 5: 2017-01-02    4          0 2.225062 4.950900 1.552668 3.454782 2.410778 2.059827 4.583243 3.198228 4.242889
## 6: 2017-01-02    5          0 2.332381 5.440000 1.547259 3.608798 2.394011 2.249153 5.245881 3.480023 5.058689
##      0.0.0.1   1.0.0.1   0.1.0.1   0.0.1.1   0.0.0.2
## 1: 0.4410216 0.6574921 0.5221967 0.5034797 0.1945001
## 2: 0.6289321 1.0692175 0.8225132 0.8711358 0.3955556
## 3: 0.8169388 1.5604695 1.1706544 1.3306185 0.6673890
## 4: 1.0049876 2.1314268 1.5670396 1.8819315 1.0100000
## 5: 0.9891467 2.2009124 1.5358163 2.0374714 0.9784111
## 6: 0.9741549 2.2721002 1.5072702 2.1910234 0.9489778
dim(dt_poly1)
## [1] 3600   17
dim(dt_poly2)
## [1] 3600   57

With polynomials, the dimensions increase even faster. (It is a fun exercise to write a function for calculating how many features there will be for different degrees of polynomials.)

Not to bore you with details our results are as follows:

dresults[order(station)] %>% filter(model %in% c("base","lagged","powered", "lagged + powered", "poly(2)"))
##     station            model train_error test_error
##  1:  aliaga             base   16.115594  13.446084
##  2:  aliaga           lagged   15.819267  13.337983
##  3:  aliaga          powered   15.929062  13.466087
##  4:  aliaga lagged + powered   15.613828  13.392450
##  5:  aliaga          poly(2)   15.284348  13.547195
##  6:   bares             base    8.368780   6.469539
##  7:   bares           lagged    8.115166   6.476837
##  8:   bares          powered    8.187200   6.652139
##  9:   bares lagged + powered    8.003236   6.630835
## 10:   bares          poly(2)    8.024843   6.630476
## 11:   dinar             base   25.054550  19.934007
## 12:   dinar           lagged   24.704932  19.893682
## 13:   dinar          powered   23.836830  20.203461
## 14:   dinar lagged + powered   23.287946  19.776765
## 15:   dinar          poly(2)   23.456418  20.640282
## 16:  geycek             base   21.612130  18.296658
## 17:  geycek           lagged   21.413134  17.767683
## 18:  geycek          powered   20.743009  18.897305
## 19:  geycek lagged + powered   20.441426  18.363553
## 20:  geycek          poly(2)   20.410482  19.122731
## 21:    soke             base    7.570961   8.648788
## 22:    soke           lagged    7.451066   8.643861
## 23:    soke          powered    6.883059   8.060418
## 24:    soke lagged + powered    6.789591   8.074436
## 25:    soke          poly(2)    6.717682   7.934136
## 26:    soma             base   14.451429  13.176678
## 27:    soma           lagged   14.248784  13.019966
## 28:    soma          powered   13.869595  13.363834
## 29:    soma lagged + powered   13.551715  13.056804
## 30:    soma          poly(2)   13.487807  13.253549
##     station            model train_error test_error

We were hoping to outperform the powered models. We sure did. However we see that in some stations lagged+powered models are better. So we think that using polynomial features from the past can improve our model even more!

2.1.7 Polynomial then Lagged

Did you notice something weird in the title? Why didn’t we just say Polynomial + Lagged or Lagged + Polynomial, just like in Lagged + Powered?

Because now we are only interested in the polynomial combinations of our features and their results in different time indices. It is much easier than checking the polynomial combinations of lagged results.

colnames(dt_polylag1)
##  [1] "date"          "hour"          "production"    "1.0.0.0_lag1"  "2.0.0.0_lag1"  "0.1.0.0_lag1"  "1.1.0.0_lag1" 
##  [8] "0.2.0.0_lag1"  "0.0.1.0_lag1"  "1.0.1.0_lag1"  "0.1.1.0_lag1"  "0.0.2.0_lag1"  "0.0.0.1_lag1"  "1.0.0.1_lag1" 
## [15] "0.1.0.1_lag1"  "0.0.1.1_lag1"  "0.0.0.2_lag1"  "1.0.0.0"       "2.0.0.0"       "0.1.0.0"       "1.1.0.0"      
## [22] "0.2.0.0"       "0.0.1.0"       "1.0.1.0"       "0.1.1.0"       "0.0.2.0"       "0.0.0.1"       "1.0.0.1"      
## [29] "0.1.0.1"       "0.0.1.1"       "0.0.0.2"       "1.0.0.0_lead1" "2.0.0.0_lead1" "0.1.0.0_lead1" "1.1.0.0_lead1"
## [36] "0.2.0.0_lead1" "0.0.1.0_lead1" "1.0.1.0_lead1" "0.1.1.0_lead1" "0.0.2.0_lead1" "0.0.0.1_lead1" "1.0.0.1_lead1"
## [43] "0.1.0.1_lead1" "0.0.1.1_lead1" "0.0.0.2_lead1"
dim(dt_polylag1)
## [1] 3598   45
dim(dt_polylag2)
## [1] 3598  165

Now the dimensions of our datasets are tripled compared to the polynomial data. It is again a huge increase but the following section will be scarier.

Looking at the results:

dresults[order(station)] %>% filter(model %in% c("base","lagged","powered", "lagged + powered", "poly(2)", "poly(2) > lag"))
##     station            model train_error test_error
##  1:  aliaga             base   16.115594  13.446084
##  2:  aliaga           lagged   15.819267  13.337983
##  3:  aliaga          powered   15.929062  13.466087
##  4:  aliaga lagged + powered   15.613828  13.392450
##  5:  aliaga          poly(2)   15.284348  13.547195
##  6:  aliaga    poly(2) > lag   15.061376  13.356530
##  7:   bares             base    8.368780   6.469539
##  8:   bares           lagged    8.115166   6.476837
##  9:   bares          powered    8.187200   6.652139
## 10:   bares lagged + powered    8.003236   6.630835
## 11:   bares          poly(2)    8.024843   6.630476
## 12:   bares    poly(2) > lag    7.841424   6.523750
## 13:   dinar             base   25.054550  19.934007
## 14:   dinar           lagged   24.704932  19.893682
## 15:   dinar          powered   23.836830  20.203461
## 16:   dinar lagged + powered   23.287946  19.776765
## 17:   dinar          poly(2)   23.456418  20.640282
## 18:   dinar    poly(2) > lag   22.247506  19.667580
## 19:  geycek             base   21.612130  18.296658
## 20:  geycek           lagged   21.413134  17.767683
## 21:  geycek          powered   20.743009  18.897305
## 22:  geycek lagged + powered   20.441426  18.363553
## 23:  geycek          poly(2)   20.410482  19.122731
## 24:  geycek    poly(2) > lag   20.116372  18.523240
## 25:    soke             base    7.570961   8.648788
## 26:    soke           lagged    7.451066   8.643861
## 27:    soke          powered    6.883059   8.060418
## 28:    soke lagged + powered    6.789591   8.074436
## 29:    soke          poly(2)    6.717682   7.934136
## 30:    soke    poly(2) > lag    6.647820   7.965204
## 31:    soma             base   14.451429  13.176678
## 32:    soma           lagged   14.248784  13.019966
## 33:    soma          powered   13.869595  13.363834
## 34:    soma lagged + powered   13.551715  13.056804
## 35:    soma          poly(2)   13.487807  13.253549
## 36:    soma    poly(2) > lag   13.065487  12.976022
##     station            model train_error test_error

Great results! We have seen nice improvements in all stations.

Now we have another question in our mind: can polynomial combinations of features from different times be useful ,too, for our forecasting?

2.1.8 Polynomial of Lagged (Lagged then Polynomial)

Now we will combine features from different times. As we are looking at a time window of only size 3 and on 4 locations, things should not be very big. However with a wider time window and more locations this exercise may only make things complicated.

While trying to create polynomials for stations 2 to 6 we get the following error: > Error: vector memory exhausted (limit reached?)

As we can see our memory is not enough. This problem can be solved but we will stop there for now and in the next chapter we will try to handle this.

However we were able to create the dataset for station1, let’s see its performance:

fit.lagpoly1 = cv.glmnet(x = as.matrix(dt_lagpoly1[,-c(1:3)]), y = dt_lagpoly1$production, type.measure = 'mae')
error.train1 = min(fit.lagpoly1$cvup)
pred.test1 = predict(fit.lagpoly1, as.matrix(dte_lagpoly1[,-c(1:3)]), s = "lambda.1se")
error.test1 = mean(abs(pred.test1 - dte_lagpoly1$production))
res1 = data.table(station = "aliaga", model = "lag > poly(2)", train_error = error.train1, test_error = error.test1)
rbind(dresults %>% filter(station == "aliaga") %>% filter(model %in% c("base","lagged","powered", "lagged + powered", "poly(2)", "poly(2) > lag")), res1)
##    station            model train_error test_error
## 1:  aliaga             base    16.11559   13.44608
## 2:  aliaga           lagged    15.81927   13.33798
## 3:  aliaga          powered    15.92906   13.46609
## 4:  aliaga lagged + powered    15.61383   13.39245
## 5:  aliaga          poly(2)    15.28435   13.54719
## 6:  aliaga    poly(2) > lag    15.06138   13.35653
## 7:  aliaga    lag > poly(2)    14.90441   13.37074

We were able to reduce our error, once more. This is a good motivation for us to move on. Before finalizing this section let’s try one more thing.

2.1.9 Polynomial of degree 3

Just for future motivation, lets try the base model of polynomial with degree 3:

dresults[order(station)]
##     station            model train_error test_error
##  1:  aliaga             base   16.115594  13.446084
##  2:  aliaga           lagged   15.819267  13.337983
##  3:  aliaga          powered   15.929062  13.466087
##  4:  aliaga lagged + powered   15.613828  13.392450
##  5:  aliaga          poly(2)   15.284348  13.547195
##  6:  aliaga    poly(2) > lag   15.061376  13.356530
##  7:  aliaga          poly(3)   13.932274  12.602298
##  8:   bares             base    8.368780   6.469539
##  9:   bares           lagged    8.115166   6.476837
## 10:   bares          powered    8.187200   6.652139
## 11:   bares lagged + powered    8.003236   6.630835
## 12:   bares          poly(2)    8.024843   6.630476
## 13:   bares    poly(2) > lag    7.841424   6.523750
## 14:   bares          poly(3)    7.366621   6.156572
## 15:   dinar             base   25.054550  19.934007
## 16:   dinar           lagged   24.704932  19.893682
## 17:   dinar          powered   23.836830  20.203461
## 18:   dinar lagged + powered   23.287946  19.776765
## 19:   dinar          poly(2)   23.456418  20.640282
## 20:   dinar    poly(2) > lag   22.247506  19.667580
## 21:   dinar          poly(3)   21.762741  18.844985
## 22:  geycek             base   21.612130  18.296658
## 23:  geycek           lagged   21.413134  17.767683
## 24:  geycek          powered   20.743009  18.897305
## 25:  geycek lagged + powered   20.441426  18.363553
## 26:  geycek          poly(2)   20.410482  19.122731
## 27:  geycek    poly(2) > lag   20.116372  18.523240
## 28:  geycek          poly(3)   18.541052  17.200868
## 29:    soke             base    7.570961   8.648788
## 30:    soke           lagged    7.451066   8.643861
## 31:    soke          powered    6.883059   8.060418
## 32:    soke lagged + powered    6.789591   8.074436
## 33:    soke          poly(2)    6.717682   7.934136
## 34:    soke    poly(2) > lag    6.647820   7.965204
## 35:    soke          poly(3)    6.599493   7.894552
## 36:    soma             base   14.451429  13.176678
## 37:    soma           lagged   14.248784  13.019966
## 38:    soma          powered   13.869595  13.363834
## 39:    soma lagged + powered   13.551715  13.056804
## 40:    soma          poly(2)   13.487807  13.253549
## 41:    soma    poly(2) > lag   13.065487  12.976022
## 42:    soma          poly(3)   12.364350  11.836208
##     station            model train_error test_error

Wow! Our poly(3) models are outperforming poly(2)>lag models. However combination of lag and poly(3) would again create dimension problems. So we stop here.

# dlevel_order is factorized version of "models". needed for ordered model names
plot1 = ggplot(dresults) +
    geom_point(mapping = aes(x = dlevel_order, y = train_error, color = station)) +
    labs(x = "Models", y = "Mean Absolute Error", title = "Final Results", subtitle = "Error Comparison")
print(plot1)

2.1.10 Final Words

In this section, we have shown that there are many ways to derive new features that increases performance. However with more features more problems occur, especially because of size. In the following section we will try to keep our dimensions low while trying to make use of these new features.

We will talk about:

  1. Sparse Polynomials
  2. Principal Component Analysis (PCA)
  3. Kronecker PCA
  4. Sparse Kronecker PCA
  5. Kernel PCA …