Section - 8 Evaluate Model Performance

Now we get to see the results of our hard work! There are some additional data preparation steps we need to take before we can visualize the results in aggregate; if you are just looking for the charts showing the results they are shown later on in the “Visualize Results” section below.

8.1 Summarizing models

Because we know what really happened for the target variable in the test data we used in the previous step, we can get a good idea of how good the model performed on a dataset it has never seen before. We do this to avoid overfitting, which is the idea that the model may work really well on the training data we provided, but not on the new data that we want to predictions on. If the performance on the test set is good, that is a good sign. If the data is split into several subsets and each subset has consistent results for the training and test datasets, that is an even better sign the model may perform as expected.

The first row of the data is for the BTC cryptocurrency for the split number 1. For this row of data (and all others), we made predictions for the test_data using a linear regression model and saved the results in the lm_test_predictions column. The models were trained on the train_data and had not yet seen the results from the test_data, so how accurate was the model in its predictions on this data?

8.1.1 MAE

Each individual prediction can be compared to the observation of what actually happened, and for each prediction we can calculate the error between the two. We can then take all of the values for the error of the prediction relative to the actual observations, and summarize the performance as a Mean Absolute Error (MAE) of those values, which gives us a single value to use as an indicator of the accuracy of the model. The higher the MAE score, the higher the error, meaning the model performs worse when the value is larger.

8.1.2 RMSE

A common metric to evaluate the performance of a model is the Root Mean Square Error, which is similar to the MAE but squares and then takes the square root of the values. An interesting implication of this, is that the RMSE will always be larger or equal to the MAE, where a large degree of error on a single observation would get penalized more by the RMSE. The higher the RMSE value, the worse the performance of the model, and can range from 0 to infinity, meaning there is no defined limit on the amount of error you could have (unlike the next metric).

8.1.3 R Squared

The \(R^2\), also known as the coefficient of determination, is a measure that describes the strength in the correlation between the predictions made and the actual results. A value of 1.0 would mean that the predictions made were exactly identical to the actual results. A perfect score is usually concerning because even a great model shouldn’t be exactly 100% accurate and usually indicates a mistake was made that gave away the results to the model and would not perform nearly as good when put into practice in the real world, but in the case of the \(R^2\) the higher the score (from 0 to 1) the better.

8.1.4 Get Metrics

We can return the RMSE and \(R^2\) metrics for the BTC cryptocurrency and the split number 1 by using the postResample() function from the caret package:

postResample(pred = cryptodata_nested$lm_test_predictions[[1]], 
             obs = cryptodata_nested$test_data[[1]]$target_price_24h)
##        RMSE    Rsquared         MAE 
## 930.0204745   0.1973831 766.4859073

We can extract the first element to return the RMSE metric, and the second element for the R Squared (R^2) metric. We are using [[1]] to extract the first element of the lm_test_predictions and test_data and compare the predictions to the actual value of the target_price24h column.

This model used the earliest subset of the data available for the cryptocurrency. How does the same model used to predict this older subset of the data perform when applied to the most recent subset of the data from the holdout?

We can get the same summary of results comparing the lm_holdout_predictions to what actually happened to the target_price_24h column of the actual holdout_data:

postResample(pred = cryptodata_nested$lm_holdout_predictions[[1]], 
             obs = cryptodata_nested$holdout_data[[1]]$target_price_24h)
##        RMSE    Rsquared         MAE 
##          NA 0.006785391          NA

The result above may show a value of NA for the RMSE metric. We will explain and resolve the issue later on.

8.1.5 Comparing Metrics

Why not just pick one metric and stick to it? We certainly could, but these two metrics complement each other. For example, if we had a model that always predicts a 0% price change for the time period, the model may have a low error but it wouldn’t actually be very informative in the direction or magnitude of those movements and the predictions and actuals would not be very correlated with each other which would lead to a low \(R^2\) value. We are using both because it helps paint a more complete picture in this sense, and depending on the task you may want to use a different set of metrics to evaluate the performance. It is also worth mentioning that if your target variable you are predicting is either 0 or 1, this would be a classification problem where different metrics become more appropriate to use.

These are indicators that should be taken with a grain of salt individually, but comparing the results across many different models for the same cryptocurrency can help us determine which models work best for the problem, and then comparing those results across many cryptocurrencies can help us understand which cryptocurrencies we can predict with the most accuracy.

Before we can draw these comparisons however, we will need to “standardize” the values to create a fair comparison across all dataasets.

8.2 Data Prep - Adjust Prices

Because cryptocurrencies can vary dramatically in their prices with some trading in the tens of thousands of dollars and others trading for less than a cent, we need to make sure to standardize the RMSE columns to provide a fair comparison for the metric.

Therefore, before using the postResample() function, let’s convert both the predictions and the target to be the % change in price over the 24 hour period, rather than the change in price ($).

This step is particularly tedious, but it is important. As with the rest of this tutorial, try to understand what we are doing and why even if you find the code overwhelming. All we are doing in this “Adjust Prices” section is we are adjusting all of the prices to be represented as percentage change between observations, which will allow us to draw a fair comparison of the metrics across all cryptocurrencies, which would not be possible using the prices themselves. If you want to skip the tedious steps and want to see the performance of the models visualized, click here to skip ahead.

8.2.1 Add Last Price

In order to convert the first prediction made to be a percentage, we need to know the previous price, which would be the last observation from the train data. Therefore, let’s make a function to add the last_price_train column and append it to the predictions made so we can calculate the % change of the first element relative to the last observation in the train data, before later removing the value not associated with the predictions:

last_train_price <- function(train_data, predictions){
  c(tail(train_data$price_usd,1), predictions)
}

We will first perform all steps on the linear regression models to make the code a little more digestible, and we will then perform the same steps for the rest of the models.

8.2.1.1 Test

Overwrite the old predictions for the first 4 splits of the test data using the new function created above:

cryptodata_nested <- mutate(cryptodata_nested,
                            lm_test_predictions = ifelse(split < 5,
                                                         map2(train_data, lm_test_predictions, last_train_price),
                                                         NA))

The mutate() function is used to create the new column lm_test_predictions assigning the value only for the first 4 splits where the test data would actually exist (the 5th being the holdout set) using the ifelse() function.

8.2.1.2 Holdout

Do the same but for the holdout now. For the holdout we need to take the last price point of the 5th split:

cryptodata_nested_holdout <- mutate(filter(cryptodata_nested, split == 5),
                                    lm_holdout_predictions = map2(train_data, lm_holdout_predictions, last_train_price))

Now join the holdout data to all rows based on the cryptocurrency symbol alone:

cryptodata_nested <- left_join(cryptodata_nested, 
                               select(cryptodata_nested_holdout, symbol, lm_holdout_predictions),
                               by='symbol')
# Remove unwanted columns
cryptodata_nested <- select(cryptodata_nested, -lm_holdout_predictions.x, -split.y)
# Rename the columns kept
cryptodata_nested <- rename(cryptodata_nested, 
                            lm_holdout_predictions = 'lm_holdout_predictions.y',
                            split = 'split.x')
# Reset the correct grouping structure
cryptodata_nested <- group_by(cryptodata_nested, symbol, split)

8.2.2 Convert to Percentage Change

Now we have everything we need to accurately calculate the percentage change between observations including the first one. Let’s make a new function to calculate the percentage change:

standardize_perc_change <- function(predictions){
  results <- (diff(c(lag(predictions, 1), predictions)) / lag(predictions, 1))*100
  # Exclude the first element, next element will be % change of first prediction
  tail(head(results, length(predictions)), length(predictions)-1)
}

Overwrite the old predictions with the new predictions adjusted as a percentage now:

cryptodata_nested <- mutate(cryptodata_nested,
                            lm_test_predictions = ifelse(split < 5,
                                                         map(lm_test_predictions, standardize_perc_change),
                                                         NA),
                            # Holdout for all splits
                            lm_holdout_predictions = map(lm_holdout_predictions, standardize_perc_change))

8.2.3 Actuals

Now do the same thing to the actual prices. Let’s make a new column called actuals containing the real price values (rather than the predicted ones):

actuals_create <- function(train_data, test_data){
  c(tail(train_data$price_usd,1), as.numeric(unlist(select(test_data, price_usd))))
}

Use the new function to create the new column actuals:

cryptodata_nested <- mutate(cryptodata_nested,
                            actuals_test = ifelse(split < 5,
                                             map2(train_data, test_data, actuals_create),
                                             NA))

8.2.3.1 Holdout

Again, for the holdout we need the price from the training data of the 5th split to perform the first calculation:

cryptodata_nested_holdout <- mutate(filter(cryptodata_nested, split == 5),
                                    actuals_holdout = map2(train_data, holdout_data, actuals_create))

Join the holdout data to all rows based on the cryptocurrency symbol alone:

cryptodata_nested <- left_join(cryptodata_nested, 
                               select(cryptodata_nested_holdout, symbol, actuals_holdout),
                               by='symbol')
# Remove unwanted columns
cryptodata_nested <- select(cryptodata_nested, -split.y)
# Rename the columns kept
cryptodata_nested <- rename(cryptodata_nested, split = 'split.x')
# Reset the correct grouping structure
cryptodata_nested <- group_by(cryptodata_nested, symbol, split)

8.2.4 Actuals as % Change

Now we can convert the new actuals to express the price_usd as a % change relative to the previous value using adapting the function from earlier:

actuals_perc_change <- function(predictions){
  results <- (diff(c(lag(predictions, 1), predictions)) / lag(predictions, 1))*100
  # Exclude the first element, next element will be % change of first prediction
  tail(head(results, length(predictions)), length(predictions)-1)
}
cryptodata_nested <- mutate(cryptodata_nested,
                            actuals_test = ifelse(split < 5,
                                             map(actuals_test, actuals_perc_change),
                                             NA),
                            actuals_holdout = map(actuals_holdout, actuals_perc_change))

8.3 Review Summary Statistics

Now that we standardized the price to show the percentage change relative to the previous period instead of the price in dollars, we can actually compare the summary statistics across all cryptocurrencies and have it be a fair comparison.

Let’s get the same statistic as we did at the beginning of this section, but this time on the standardized values. This time to calculate the RMSE error metric let’s use the rmse() function from the hydroGOF package because it allows us to set the na.rm = T parameter, and otherwise one NA value would return NA for the overall RMSE:

hydroGOF::rmse(cryptodata_nested$lm_test_predictions[[1]], 
               cryptodata_nested$actuals_test[[1]], 
               na.rm=T)
## [1] 0.3284851

8.3.1 Calculate R^2

Now we can do the same for the R Squared metric using the same postResample() function that we used at the start of this section:

evaluate_preds_rsq <- function(predictions, actuals){

  postResample(pred = predictions, obs = actuals)[[2]]

}
cryptodata_nested <- mutate(cryptodata_nested,
                            lm_rsq_test = unlist(ifelse(split < 5,
                                                         map2(lm_test_predictions, actuals_test, evaluate_preds_rsq),
                                                         NA)),
                            lm_rsq_holdout = unlist(map2(lm_holdout_predictions, actuals_holdout, evaluate_preds_rsq)))

Look at the results:

select(cryptodata_nested, lm_rsq_test, lm_rsq_holdout)
## # A tibble: 555 x 4
## # Groups:   symbol, split [555]
##    symbol split lm_rsq_test lm_rsq_holdout
##    <chr>  <dbl>       <dbl>          <dbl>
##  1 BTC        1       0.889         0.423 
##  2 ETH        1       0.539         0.779 
##  3 EOS        1       0.466         0.154 
##  4 LTC        1       0.523         0.528 
##  5 BSV        1       0.322         0.0650
##  6 ADA        1       0.148         0.0433
##  7 ZEC        1       0.769         0.438 
##  8 HT         1       0.598         0.581 
##  9 TRX        1       0.271         0.632 
## 10 KNC        1       0.802         0.273 
## # ... with 545 more rows

8.3.2 Calculate RMSE

Similarly let’s make a function to get the RMSE metric for all models:

evaluate_preds_rmse <- function(predictions, actuals){

  hydroGOF::rmse(predictions, actuals, na.rm=T)

}

Now we can use the map2() function to use it to get the RMSE metric for both the test data and the holdout:

cryptodata_nested <- mutate(cryptodata_nested,
                            lm_rmse_test = unlist(ifelse(split < 5,
                                                         map2(lm_test_predictions, actuals_test, evaluate_preds_rmse),
                                                         NA)),
                            lm_rmse_holdout = unlist(map2(lm_holdout_predictions, actuals_holdout, evaluate_preds_rmse)))

Look at the results. Wrapping them in print(n=500) overwrites the behavior to only give a preview of the data so we can view the full results (up to 500 observations).

print(select(cryptodata_nested, lm_rmse_test, lm_rmse_holdout, lm_rsq_test, lm_rsq_holdout), n=500)
## # A tibble: 555 x 6
## # Groups:   symbol, split [555]
##     symbol split lm_rmse_test lm_rmse_holdout lm_rsq_test lm_rsq_holdout
##     <chr>  <dbl>        <dbl>           <dbl>       <dbl>          <dbl>
##   1 BTC        1        0.328           0.673    0.889          0.423   
##   2 ETH        1        0.860           0.465    0.539          0.779   
##   3 EOS        1        1.22            0.812    0.466          0.154   
##   4 LTC        1        1.17            0.491    0.523          0.528   
##   5 BSV        1        0.909           0.584    0.322          0.0650  
##   6 ADA        1        2.11            0.474    0.148          0.0433  
##   7 ZEC        1        0.719           1.15     0.769          0.438   
##   8 HT         1        0.824           0.994    0.598          0.581   
##   9 TRX        1        0.981           0.305    0.271          0.632   
##  10 KNC        1        0.813           0.490    0.802          0.273   
##  11 XMR        1        2.94            0.727    0.474          0.658   
##  12 ZRX        1        1.68            0.548    0.416          0.235   
##  13 BAT        1        1.96            0.516    0.366          0.0114  
##  14 BNT        1        1.30            0.576    0.0883         0.537   
##  15 MANA       1        2.09            0.647    0.213          0.0256  
##  16 CROOLD     1        0.752           0.491    0.674          0.207   
##  17 DGB        1        1.17            0.849    0.721          0.0365  
##  18 ENJ        1        2.14            0.433    0.385          0.786   
##  19 XEM        1        1.21            0.353    0.349          0.574   
##  20 BTG        1        0.902           0.518    0.653          0.0789  
##  21 KMD        1        1.59            0.573    0.328          0.567   
##  22 ARDR       1        2.15            0.466    0.224          0.464   
##  23 ELF        1        1.73            2.89     0.774          0.0804  
##  24 NEXO       1        0.962           1.12     0.549          0.945   
##  25 CHZ        1        2.07            1.10     0.479          0.0662  
##  26 BRD        1        2.68            1.39     0.0247         0.724   
##  27 CKB        1        4.29            1.09     0.668          0.726   
##  28 DCR        1        1.17            0.538    0.345          0.496   
##  29 IQ         1        3.86            0.856    0.848          0.462   
##  30 VIB        1        1.16            0.497    0.185          0.845   
##  31 WAXP       1      184.              0.604    0.0304         0.706   
##  32 OAX        1        0.860           4.07     0.548          0.0304  
##  33 LEO        1        0.766           3.14     0.519          0.320   
##  34 AVA        1        1.08            0.463    0.398          0.651   
##  35 NAV        1        0.381           5.55     0.00722        0.0492  
##  36 SMART      1        2.11            1.78     0.0982         0.799   
##  37 JST        1        1.27            1.35     0.311          0.315   
##  38 SRN        1        1.27            3.48     0.275          0.486   
##  39 ARPA       1        0.980           0.923    0.875          0.494   
##  40 IHF        1        1.26            1.57     0.0501         0.0159  
##  41 MDX        1        1.69            1.01     0.622          0.871   
##  42 XYM        1        0.807           0.568    0.636          0.929   
##  43 NEO        1        1.10            0.486    0.453          0.240   
##  44 JULD       1        0.874           4.18     0.777          0.0731  
##  45 COMP       1        1.83            0.873    0.0561         0.000188
##  46 ZIL        1        1.85            2.54     0.0362         0.505   
##  47 BOSON      1        5.37            3.00     0.829          0.601   
##  48 ZKS        1        1.30          206.       0.764          0.00179 
##  49 ORN        1        0.771           0.658    0.780          0.248   
##  50 BOR        1       21.0             1.83     0.162          0.976   
##  51 MAID       1        1.48            1.02     0.0788         0.0380  
##  52 WBTC       1      127.              0.643    0.0231         0.595   
##  53 VLX        1        0.822           0.612    0.711          0.539   
##  54 SXP        1        0.874           0.534    0.815          0.197   
##  55 ZAP        1        2.70            1.70     0.00912        0.212   
##  56 ALPHA      1        3.67            0.496    0.236          0.347   
##  57 CLT        1        1.64            2.61     0.567          0.0109  
##  58 JUV        1        0.839          75.5      0.663          0.00140 
##  59 CVCOIN     1        8.86            2.99     0.0500         0.699   
##  60 INJ        1        1.39            0.318    0.268          0.856   
##  61 CRPT       1       10.0             2.01     0.0362         0.396   
##  62 WRX        1        1.96            0.353    0.202          0.840   
##  63 ONE        1        1.31            1.55     0.743          0.805   
##  64 IQN        1        1.59            0.872    0.0172         0.800   
##  65 XCH        1        0.677           0.433    0.748          0.127   
##  66 ICP        1        1.19            1.01     0.680          0.210   
##  67 SENSO      1        2.35            3.31     0.170          0.197   
##  68 TLM        1        1.52            0.754    0.848          0.0313  
##  69 EGLD       1        2.06            0.586    0.000493       0.746   
##  70 BIST       1        0.584           0.634    0.00824        0.661   
##  71 KLV        1        3.17            0.843    0.129          0.0253  
##  72 LOC        1        0.601           1.26     0.677          0.696   
##  73 FIL        1        1.59            0.467    0.676          0.534   
##  74 LPT        1        5.97           12.3      0.208          0.168   
##  75 EVX        1        0.988           3.87     0.340          0.679   
##  76 1INCH      1        1.26            0.434    0.921          0.801   
##  77 TON        1        0.340           1.11     0.532          0.196   
##  78 MITH       1        1.37            0.588    0.226          0.552   
##  79 ACH        1        4.98            0.722    0.631          0.0764  
##  80 DOGE       1        0.805           0.979    0.717          0.430   
##  81 OGN        1        1.56            0.660    0.491          0.00607 
##  82 SOLO       1        4.33           11.9      0.0701         0.000302
##  83 POND       1        3.63            1.41     0.0464         0.122   
##  84 REEF       1        4.84            0.629    0.0337         0.384   
##  85 SUN        1       11.5             1.30     0.0182         1       
##  86 CTSI       1        1.74            1.38     0.588          0.684   
##  87 NCT        1        5.94            0.746    0.150          0.733   
##  88 ETC        1        0.600           0.703    0.825          0.365   
##  89 UNI        1        1.20            0.465    0.718          0.625   
##  90 NEAR       1        3.45            0.924    0.00110        0.945   
##  91 DASH       1        2.57            0.694    0.0306         0.509   
##  92 RSR        1        1.34            1.10     0.650          0.625   
##  93 BIZZ       1        3.86            6.78     0.0713         0.225   
##  94 DODO       1        1.42            0.887    0.366          0.0626  
##  95 LSK        1        1.88            0.517    0.00293        0.599   
##  96 PERP       1        0.964           0.783    0.733          0.316   
##  97 PLA        1        2.56            6.33     0.585          0.00719 
##  98 SBTC       1        1.78            2.23     0.977          0.0995  
##  99 ETN        1        5.21            1.37     0.0932         0.0374  
## 100 OCEAN      1        2.97            1.00     0.123          0.898   
## 101 XVG        1        2.17            1.14     0.000452       0.0122  
## 102 RAMP       1        1.92            0.596    0.121          0.178   
## 103 SWM        1        5.68            1.10     0.168          0.467   
## 104 ETP        1       11.5             0.549    0.0355         0.854   
## 105 TV         1        8.67            1.85     0.365          0.0808  
## 106 DAISY      1        3.89            3.77     0.103          0.155   
## 107 LPT        2        1.58           12.3      0.260          0.168   
## 108 BAL        1        1.58            0.531    0.348          0.272   
## 109 ZKS        2        1.05          206.       0.701          0.00179 
## 110 SWM        2        1.12            1.10     0.0512         0.467   
## 111 PLA        2        2.68            6.33     0.643          0.00719 
## 112 SBTC       2        0.609           2.23     0.0194         0.0995  
## 113 LEO        2        1.73            3.14     0.499          0.320   
## 114 MDX        2        4.76            1.01     0.271          0.871   
## 115 BOR        2        4.00            1.83     0.152          0.976   
## 116 MAID       2        1.53            1.02     0.875          0.0380  
## 117 EVX        2        4.80            3.87     0.270          0.679   
## 118 NAV        2        1.86            5.55     0.279          0.0492  
## 119 OAX        2        2.47            4.07     0.476          0.0304  
## 120 SRN        2        3.56            3.48     0.241          0.486   
## 121 ORN        2        1.79            0.658    0.617          0.248   
## 122 CRPT       2        7.53            2.01     0.0677         0.396   
## 123 SMART      2        1.53            1.78     0.0465         0.799   
## 124 JULD       2       28.4             4.18     0.00137        0.0731  
## 125 ZIL        2        1.62            2.54     0.862          0.505   
## 126 DODO       2        3.08            0.887    0.758          0.0626  
## 127 MITH       2        1.87            0.588    0.735          0.552   
## 128 LSK        2        2.30            0.517    0.667          0.599   
## 129 WBTC       2        2.31            0.643    0.458          0.595   
## 130 BIST       2        1.99            0.634    0.0138         0.661   
## 131 OGN        2        2.13            0.660    0.153          0.00607 
## 132 ETC        2        3.48            0.703    0.882          0.365   
## 133 DGB        2        3.30            0.849    0.447          0.0365  
## 134 JST        2        1.46            1.35     0.744          0.315   
## 135 BOSON      2       12.4             3.00     0.456          0.601   
## 136 INJ        2        3.84            0.318    0.646          0.856   
## 137 CTSI       2        1.99            1.38     0.471          0.684   
## 138 BTC        2        0.858           0.673    0.819          0.423   
## 139 ETH        2        1.23            0.465    0.947          0.779   
## 140 EOS        2        1.69            0.812    0.896          0.154   
## 141 LTC        2        4.80            0.491    0.814          0.528   
## 142 BSV        2        1.86            0.584    0.743          0.0650  
## 143 ADA        2        1.76            0.474    0.555          0.0433  
## 144 ZEC        2        1.67            1.15     0.797          0.438   
## 145 HT         2        2.09            0.994    0.144          0.581   
## 146 TRX        2        1.46            0.305    0.856          0.632   
## 147 KNC        2        2.90            0.490    0.440          0.273   
## 148 XMR        2        2.42            0.727    0.387          0.658   
## 149 ZRX        2        2.39            0.548    0.678          0.235   
## 150 BAT        2        1.92            0.516    0.686          0.0114  
## 151 BNT        2        0.924           0.576    0.860          0.537   
## 152 MANA       2        1.82            0.647    0.767          0.0256  
## 153 ENJ        2        3.14            0.433    0.260          0.786   
## 154 XEM        2        3.51            0.353    0.537          0.574   
## 155 BTG        2        1.62            0.518    0.834          0.0789  
## 156 ARDR       2        8.20            0.466    0.271          0.464   
## 157 KMD        2        3.60            0.573    0.503          0.567   
## 158 ELF        2        2.36            2.89     0.805          0.0804  
## 159 NEXO       2        1.80            1.12     0.540          0.945   
## 160 CHZ        2        3.52            1.10     0.418          0.0662  
## 161 BRD        2        2.51            1.39     0.307          0.724   
## 162 CKB        2        1.88            1.09     0.858          0.726   
## 163 DCR        2        5.38            0.538    0.448          0.496   
## 164 WAXP       2        2.98            0.604    0.197          0.706   
## 165 AVA        2        3.17            0.463    0.128          0.651   
## 166 ARPA       2        2.32            0.923    0.570          0.494   
## 167 IHF        2        0.898           1.57     0.751          0.0159  
## 168 XYM        2        0.982           0.568    0.810          0.929   
## 169 NEO        2        2.61            0.486    0.577          0.240   
## 170 COMP       2        1.54            0.873    0.807          0.000188
## 171 ALPHA      2        2.37            0.496    0.696          0.347   
## 172 CLT        2        5.18            2.61     0.336          0.0109  
## 173 WRX        2        3.58            0.353    0.0857         0.840   
## 174 ONE        2        3.47            1.55     0.820          0.805   
## 175 XCH        2        1.82            0.433    0.608          0.127   
## 176 ICP        2        2.44            1.01     0.669          0.210   
## 177 SENSO      2        3.42            3.31     0.170          0.197   
## 178 TLM        2        4.44            0.754    0.597          0.0313  
## 179 LOC        2        1.89            1.26     0.394          0.696   
## 180 VIB        2       46.4             0.497    0.00377        0.845   
## 181 KLV        2        3.73            0.843    0.462          0.0253  
## 182 FIL        2        5.86            0.467    0.286          0.534   
## 183 1INCH      2        3.20            0.434    0.642          0.801   
## 184 ACH        2        2.31            0.722    0.382          0.0764  
## 185 DOGE       2        0.754           0.979    0.807          0.430   
## 186 NCT        2        5.38            0.746    0.447          0.733   
## 187 UNI        2        1.95            0.465    0.591          0.625   
## 188 NEAR       2        3.64            0.924    0.644          0.945   
## 189 DASH       2        1.87            0.694    0.552          0.509   
## 190 RSR        2        2.25            1.10     0.685          0.625   
## 191 BIZZ       2        2.01            6.78     0.000556       0.225   
## 192 PERP       2        1.82            0.783    0.809          0.316   
## 193 VLX        2        3.94            0.612    0.393          0.539   
## 194 SXP        2        5.25            0.534    0.278          0.197   
## 195 TON        2        0.449           1.11     0.568          0.196   
## 196 POND       2        7.47            1.41     0.0858         0.122   
## 197 SOLO       2        0.964          11.9      0.0624         0.000302
## 198 CVCOIN     2        2.29            2.99     0.00167        0.699   
## 199 OCEAN      2        1.89            1.00     0.796          0.898   
## 200 ZAP        2        3.95            1.70     0.127          0.212   
## 201 JUV        2        1.41           75.5      0.703          0.00140 
## 202 CROOLD     2        1.19            0.491    0.676          0.207   
## 203 SUN        2       10.6             1.30     0.0553         1       
## 204 IQN        2        2.32            0.872    0.494          0.800   
## 205 BTMC       1        4.09            5.04     0.754          0.719   
## 206 RAMP       2        3.84            0.596    0.0663         0.178   
## 207 EGLD       2        8.21            0.586    0.341          0.746   
## 208 REEF       2        2.70            0.629    0.462          0.384   
## 209 XVG        2        1.29            1.14     0.285          0.0122  
## 210 TV         2        1.95            1.85     0.726          0.0808  
## 211 ETN        2        1.07            1.37     0.816          0.0374  
## 212 ETP        2        0.656           0.549    0.143          0.854   
## 213 IQ         2        7.38            0.856    0.0420         0.462   
## 214 DAISY      2        0.413           3.77     0.176          0.155   
## 215 BAL        2        0.820           0.531    0.688          0.272   
## 216 ZKS        3        1.25          206.       0.984          0.00179 
## 217 LPT        3        4.01           12.3      1              0.168   
## 218 SWM        3        7.18            1.10     0.00225        0.467   
## 219 SBTC       3        3.70            2.23     0.917          0.0995  
## 220 PLA        3        3.14            6.33     0.566          0.00719 
## 221 YUCT       1       20.1             4.62     0.0417         0.0485  
## 222 MAID       3        0.744           1.02     0.0773         0.0380  
## 223 LEO        3        8.89            3.14     0.140          0.320   
## 224 BTMC       2        1.11            5.04     0.245          0.719   
## 225 EVX        3        7.61            3.87     0.467          0.679   
## 226 OCEAN      3        1.44            1.00     0.205          0.898   
## 227 NAV        3        2.78            5.55     0.406          0.0492  
## 228 DODO       3        1.46            0.887    0.466          0.0626  
## 229 SMART      3        2.83            1.78     0.781          0.799   
## 230 LSK        3        0.898           0.517    0.585          0.599   
## 231 ZIL        3        2.49            2.54     0.322          0.505   
## 232 SRN        3        2.53            3.48     0.271          0.486   
## 233 BIST       3        0.466           0.634    0.215          0.661   
## 234 JUV        3        3.96           75.5      0.909          0.00140 
## 235 ETC        3        0.816           0.703    0.886          0.365   
## 236 CTSI       3        1.92            1.38     0.205          0.684   
## 237 VIB        3        0.808           0.497    0.638          0.845   
## 238 JST        3        6.35            1.35     0.113          0.315   
## 239 INJ        3        1.31            0.318    0.369          0.856   
## 240 DGB        3        0.646           0.849    0.789          0.0365  
## 241 BOSON      3       21.5             3.00     0.742          0.601   
## 242 BTC        3        1.58            0.673    0.222          0.423   
## 243 ETH        3        2.26            0.465    0.143          0.779   
## 244 EOS        3        2.72            0.812    0.0831         0.154   
## 245 LTC        3        1.46            0.491    0.0587         0.528   
## 246 BSV        3        1.71            0.584    0.0830         0.0650  
## 247 ZEC        3        2.22            1.15     0.0139         0.438   
## 248 TRX        3        1.63            0.305    0.0532         0.632   
## 249 KNC        3        1.78            0.490    0.247          0.273   
## 250 ZRX        3        2.74            0.548    0.196          0.235   
## 251 BAT        3        1.32            0.516    0.327          0.0114  
## 252 BNT        3        2.00            0.576    0.168          0.537   
## 253 MANA       3        1.70            0.647    0.535          0.0256  
## 254 ENJ        3        1.19            0.433    0.641          0.786   
## 255 XEM        3        1.96            0.353    0.210          0.574   
## 256 ARDR       3        3.39            0.466    0.0895         0.464   
## 257 KMD        3        0.973           0.573    0.717          0.567   
## 258 ELF        3        2.47            2.89     0.0215         0.0804  
## 259 NEXO       3        1.72            1.12     0.307          0.945   
## 260 CHZ        3        1.43            1.10     0.322          0.0662  
## 261 CKB        3        1.16            1.09     0.615          0.726   
## 262 BRD        3        1.87            1.39     0.190          0.724   
## 263 DCR        3        1.16            0.538    0.788          0.496   
## 264 WAXP       3        1.25            0.604    0.671          0.706   
## 265 AVA        3        1.14            0.463    0.335          0.651   
## 266 ARPA       3        1.80            0.923    0.464          0.494   
## 267 IHF        3        0.536           1.57     0.923          0.0159  
## 268 XYM        3        1.67            0.568    0.291          0.929   
## 269 NEO        3        1.82            0.486    0.312          0.240   
## 270 COMP       3        1.70            0.873    0.486          0.000188
## 271 CLT        3        1.86            2.61     0.00946        0.0109  
## 272 WRX        3        1.79            0.353    0.103          0.840   
## 273 ONE        3        2.06            1.55     0.782          0.805   
## 274 XCH        3        2.37            0.433    0.278          0.127   
## 275 ICP        3        1.12            1.01     0.569          0.210   
## 276 SENSO      3        4.26            3.31     0.117          0.197   
## 277 TLM        3        2.21            0.754    0.172          0.0313  
## 278 LOC        3        0.239           1.26     0.00329        0.696   
## 279 DOGE       3        0.587           0.979    0.435          0.430   
## 280 HT         3        3.94            0.994    0.233          0.581   
## 281 XMR        3        1.86            0.727    0.403          0.658   
## 282 BTG        3        1.73            0.518    0.257          0.0789  
## 283 ALPHA      3        2.42            0.496    0.354          0.347   
## 284 FIL        3        2.42            0.467    0.0863         0.534   
## 285 ACH        3        4.28            0.722    0.228          0.0764  
## 286 NCT        3        2.10            0.746    0.0313         0.733   
## 287 UNI        3        2.27            0.465    0.0278         0.625   
## 288 NEAR       3        2.23            0.924    0.373          0.945   
## 289 DASH       3        1.90            0.694    0.164          0.509   
## 290 RSR        3        2.64            1.10     0.458          0.625   
## 291 BIZZ       3       14.4             6.78     0.377          0.225   
## 292 ADA        3        1.10            0.474    0.802          0.0433  
## 293 SXP        3        1.21            0.534    0.419          0.197   
## 294 CVCOIN     3        2.58            2.99     0.181          0.699   
## 295 1INCH      3        2.57            0.434    0.111          0.801   
## 296 MITH       3        3.07            0.588    0.00577        0.552   
## 297 PERP       3        1.52            0.783    0.553          0.316   
## 298 KLV        3        1.16            0.843    0.172          0.0253  
## 299 TON        3        0.514           1.11     0.0559         0.196   
## 300 OAX        3        2.52            4.07     0.704          0.0304  
## 301 JULD       3        5.64            4.18     0.439          0.0731  
## 302 WBTC       3        1.53            0.643    0.0607         0.595   
## 303 VLX        3        1.29            0.612    0.460          0.539   
## 304 OGN        3        4.14            0.660    0.0211         0.00607 
## 305 ORN        3        1.40            0.658    0.734          0.248   
## 306 POND       3        1.99            1.41     0.593          0.122   
## 307 CRPT       3       11.8             2.01     0.219          0.396   
## 308 RAMP       3        0.831           0.596    0.837          0.178   
## 309 CROOLD     3        3.12            0.491    0.0288         0.207   
## 310 REEF       3        0.683           0.629    0.870          0.384   
## 311 IQN        3        0.228           0.872    0.275          0.800   
## 312 EGLD       3        2.10            0.586    0.560          0.746   
## 313 SOLO       3        1.94           11.9      0.772          0.000302
## 314 XVG        3        1.86            1.14     0.276          0.0122  
## 315 ETN        3        1.42            1.37     0.494          0.0374  
## 316 SUN        3        1.86            1.30     0.525          1       
## 317 ZAP        3        6.34            1.70     0.364          0.212   
## 318 IDEX       1        7.29            0.877    0.593          0.157   
## 319 VIDT       1        8.54            0.496    0.299          0.584   
## 320 MDX        3        3.71            1.01     0.372          0.871   
## 321 TV         3        0.868           1.85     0.0459         0.0808  
## 322 IQ         3        2.73            0.856    0.0322         0.462   
## 323 DAISY      3       44.3             3.77     0.000278       0.155   
## 324 ETP        3        2.09            0.549    0.240          0.854   
## 325 BOR        3       17.9             1.83     0.000444       0.976   
## 326 BAL        3        0.489           0.531    0.694          0.272   
## 327 YUCT       2        2.21            4.62     0.184          0.0485  
## 328 LPT        4        0.594          12.3      0.298          0.168   
## 329 BTMC       3        1.32            5.04     0.846          0.719   
## 330 IDEX       2        8.49            0.877    0.0181         0.157   
## 331 VIDT       2        3.02            0.496    0.329          0.584   
## 332 ZKS        4        2.66          206.       0.137          0.00179 
## 333 SWM        4        5.77            1.10     0.0119         0.467   
## 334 MAID       4        2.07            1.02     0.526          0.0380  
## 335 JUV        4        7.53           75.5      0.622          0.00140 
## 336 SMART      4      NaN               1.78    NA              0.799   
## 337 EVX        4        1.13            3.87     0.129          0.679   
## 338 LSK        4        0.949           0.517    0.771          0.599   
## 339 SRN        4        1.11            3.48     0.934          0.486   
## 340 ZIL        4        0.592           2.54     0.653          0.505   
## 341 BIST       4       17.5             0.634    0.0275         0.661   
## 342 SUN        4        1.30            1.30     0.862          1       
## 343 JST        4        2.50            1.35     0.151          0.315   
## 344 MITH       4        3.58            0.588    0.552          0.552   
## 345 INJ        4        1.13            0.318    0.412          0.856   
## 346 BOSON      4       10.8             3.00     0.0162         0.601   
## 347 BTC        4        0.686           0.673    0.616          0.423   
## 348 ETH        4        0.393           0.465    0.566          0.779   
## 349 EOS        4        0.591           0.812    0.554          0.154   
## 350 LTC        4        0.430           0.491    0.663          0.528   
## 351 BSV        4        1.59            0.584    0.328          0.0650  
## 352 ZEC        4        0.381           1.15     0.607          0.438   
## 353 TRX        4        0.495           0.305    0.779          0.632   
## 354 KNC        4        0.473           0.490    0.840          0.273   
## 355 BAT        4        0.404           0.516    0.697          0.0114  
## 356 BNT        4        0.343           0.576    0.717          0.537   
## 357 MANA       4        0.590           0.647    0.734          0.0256  
## 358 DGB        4        0.437           0.849    0.838          0.0365  
## 359 ENJ        4        0.498           0.433    0.678          0.786   
## 360 XEM        4        0.355           0.353    0.879          0.574   
## 361 ARDR       4        0.905           0.466    0.888          0.464   
## 362 ELF        4        0.774           2.89     0.715          0.0804  
## 363 NEXO       4        0.694           1.12     0.443          0.945   
## 364 CHZ        4        1.81            1.10     0.431          0.0662  
## 365 CKB        4        1.31            1.09     0.410          0.726   
## 366 BRD        4        0.430           1.39     0.922          0.724   
## 367 DCR        4        0.744           0.538    0.663          0.496   
## 368 WAXP       4        0.988           0.604    0.681          0.706   
## 369 AVA        4        1.86            0.463    0.687          0.651   
## 370 ARPA       4        3.21            0.923    0.264          0.494   
## 371 IHF        4        0.556           1.57     0.568          0.0159  
## 372 XYM        4        4.72            0.568    0.264          0.929   
## 373 NEO        4        0.472           0.486    0.646          0.240   
## 374 COMP       4        0.517           0.873    0.738          0.000188
## 375 CLT        4        1.36            2.61     0.367          0.0109  
## 376 WRX        4        1.87            0.353    0.246          0.840   
## 377 ONE        4        1.22            1.55     0.935          0.805   
## 378 XCH        4        0.730           0.433    0.166          0.127   
## 379 ICP        4        0.412           1.01     0.923          0.210   
## 380 SENSO      4        0.924           3.31     0.00867        0.197   
## 381 TLM        4        1.34            0.754    0.551          0.0313  
## 382 LOC        4        0.464           1.26     0.511          0.696   
## 383 ETC        4        0.511           0.703    0.871          0.365   
## 384 HT         4        0.825           0.994    0.0181         0.581   
## 385 XMR        4        1.07            0.727    0.00145        0.658   
## 386 ZRX        4        0.637           0.548    0.780          0.235   
## 387 BTG        4        1.14            0.518    0.284          0.0789  
## 388 KMD        4        1.59            0.573    0.497          0.567   
## 389 ALPHA      4        0.817           0.496    0.594          0.347   
## 390 FIL        4        0.361           0.467    0.910          0.534   
## 391 ACH        4        1.75            0.722    0.310          0.0764  
## 392 DOGE       4       11.0             0.979    0.0995         0.430   
## 393 NEAR       4        0.604           0.924    0.815          0.945   
## 394 DASH       4        0.452           0.694    0.692          0.509   
## 395 RSR        4        0.432           1.10     0.824          0.625   
## 396 BIZZ       4        1.04            6.78     0.450          0.225   
## 397 ADA        4        0.607           0.474    0.456          0.0433  
## 398 SXP        4        0.427           0.534    0.861          0.197   
## 399 1INCH      4        0.353           0.434    0.917          0.801   
## 400 NCT        4       12.0             0.746    0.605          0.733   
## 401 UNI        4        0.858           0.465    0.148          0.625   
## 402 PERP       4        1.51            0.783    0.320          0.316   
## 403 TON        4        0.669           1.11     0.397          0.196   
## 404 VIB        4        3.47            0.497    0.0915         0.845   
## 405 CTSI       4        1.45            1.38     0.616          0.684   
## 406 ORN        4        1.12            0.658    0.00122        0.248   
## 407 KLV        4        0.383           0.843    0.781          0.0253  
## 408 OAX        4        0.947           4.07     0.0592         0.0304  
## 409 DODO       4        0.615           0.887    0.834          0.0626  
## 410 WBTC       4        0.669           0.643    0.604          0.595   
## 411 OGN        4        0.643           0.660    0.672          0.00607 
## 412 PLA        4        3.97            6.33     0.0184         0.00719 
## 413 NAV        4        9.63            5.55     0.00197        0.0492  
## 414 OCEAN      4        2.43            1.00     0.00755        0.898   
## 415 POND       4        1.45            1.41     0.139          0.122   
## 416 EGLD       4        0.712           0.586    0.690          0.746   
## 417 CROOLD     4        0.525           0.491    0.588          0.207   
## 418 VLX        4        0.523           0.612    0.519          0.539   
## 419 CRPT       4        2.06            2.01     0.580          0.396   
## 420 IQN        4        0.367           0.872    0.851          0.800   
## 421 RAMP       4        0.793           0.596    0.462          0.178   
## 422 CVCOIN     4        3.64            2.99     0.0418         0.699   
## 423 SOLO       4        1.18           11.9      0.441          0.000302
## 424 SBTC       4        2.33            2.23     0.331          0.0995  
## 425 REEF       4        0.515           0.629    0.758          0.384   
## 426 XVG        4        0.717           1.14     0.803          0.0122  
## 427 ETN        4        1.34            1.37     0.230          0.0374  
## 428 JULD       4        2.43            4.18     0.0923         0.0731  
## 429 ZAP        4        1.73            1.70     0.399          0.212   
## 430 LEO        4        8.02            3.14     0.371          0.320   
## 431 YUCT       3        2.17            4.62     0.416          0.0485  
## 432 MDX        4        1.51            1.01     0.492          0.871   
## 433 ETP        4        3.04            0.549    0.235          0.854   
## 434 DAISY      4        3.54            3.77     0.491          0.155   
## 435 IQ         4        2.15            0.856    0.0108         0.462   
## 436 BOR        4        0.158           1.83     0.450          0.976   
## 437 IDEX       3        1.99            0.877    0.348          0.157   
## 438 BAL        4        1.32            0.531    0.00418        0.272   
## 439 VIDT       3        1.61            0.496    0.0300         0.584   
## 440 TV         4      362.              1.85     0.0164         0.0808  
## 441 BTMC       4        2.33            5.04     0.946          0.719   
## 442 YUCT       4        1.59            4.62     0.417          0.0485  
## 443 SUN        5       NA               1.30    NA              1       
## 444 IDEX       4        1.22            0.877    0.0129         0.157   
## 445 VIDT       4        2.26            0.496    0.0103         0.584   
## 446 EVX        5       NA               3.87    NA              0.679   
## 447 LPT        5       NA              12.3     NA              0.168   
## 448 JUV        5       NA              75.5     NA              0.00140 
## 449 ZIL        5       NA               2.54    NA              0.505   
## 450 MAID       5       NA               1.02    NA              0.0380  
## 451 MITH       5       NA               0.588   NA              0.552   
## 452 SMART      5       NA               1.78    NA              0.799   
## 453 OCEAN      5       NA               1.00    NA              0.898   
## 454 SWM        5       NA               1.10    NA              0.467   
## 455 BIST       5       NA               0.634   NA              0.661   
## 456 LSK        5       NA               0.517   NA              0.599   
## 457 BOSON      5       NA               3.00    NA              0.601   
## 458 INJ        5       NA               0.318   NA              0.856   
## 459 BTC        5       NA               0.673   NA              0.423   
## 460 EOS        5       NA               0.812   NA              0.154   
## 461 ETH        5       NA               0.465   NA              0.779   
## 462 LTC        5       NA               0.491   NA              0.528   
## 463 BSV        5       NA               0.584   NA              0.0650  
## 464 ZEC        5       NA               1.15    NA              0.438   
## 465 TRX        5       NA               0.305   NA              0.632   
## 466 KNC        5       NA               0.490   NA              0.273   
## 467 BAT        5       NA               0.516   NA              0.0114  
## 468 BNT        5       NA               0.576   NA              0.537   
## 469 MANA       5       NA               0.647   NA              0.0256  
## 470 XEM        5       NA               0.353   NA              0.574   
## 471 ARDR       5       NA               0.466   NA              0.464   
## 472 NEXO       5       NA               1.12    NA              0.945   
## 473 CHZ        5       NA               1.10    NA              0.0662  
## 474 CKB        5       NA               1.09    NA              0.726   
## 475 BRD        5       NA               1.39    NA              0.724   
## 476 DCR        5       NA               0.538   NA              0.496   
## 477 WAXP       5       NA               0.604   NA              0.706   
## 478 AVA        5       NA               0.463   NA              0.651   
## 479 ARPA       5       NA               0.923   NA              0.494   
## 480 IHF        5       NA               1.57    NA              0.0159  
## 481 XYM        5       NA               0.568   NA              0.929   
## 482 NEO        5       NA               0.486   NA              0.240   
## 483 CLT        5       NA               2.61    NA              0.0109  
## 484 WRX        5       NA               0.353   NA              0.840   
## 485 ONE        5       NA               1.55    NA              0.805   
## 486 XCH        5       NA               0.433   NA              0.127   
## 487 ICP        5       NA               1.01    NA              0.210   
## 488 SENSO      5       NA               3.31    NA              0.197   
## 489 TLM        5       NA               0.754   NA              0.0313  
## 490 LOC        5       NA               1.26    NA              0.696   
## 491 ETC        5       NA               0.703   NA              0.365   
## 492 HT         5       NA               0.994   NA              0.581   
## 493 XMR        5       NA               0.727   NA              0.658   
## 494 ZRX        5       NA               0.548   NA              0.235   
## 495 CROOLD     5       NA               0.491   NA              0.207   
## 496 DGB        5       NA               0.849   NA              0.0365  
## 497 ENJ        5       NA               0.433   NA              0.786   
## 498 ELF        5       NA               2.89    NA              0.0804  
## 499 SRN        5       NA               3.48    NA              0.486   
## 500 COMP       5       NA               0.873   NA              0.000188
## # ... with 55 more rows

Out of 555 groups, 316 had an equal or lower RMSE score for the holdout than the test set.

8.4 Adjust Prices - All Models

Let’s repeat the same steps that we outlined above for all models.

8.4.1 Add Last Price

cryptodata_nested <- mutate(cryptodata_nested,
                            # XGBoost:
                            xgb_test_predictions = ifelse(split < 5,
                                                         map2(train_data, xgb_test_predictions, last_train_price),
                                                         NA),
                            # Neural Network:
                            nnet_test_predictions = ifelse(split < 5,
                                                         map2(train_data, nnet_test_predictions, last_train_price),
                                                         NA),
                            # Random Forest:
                            rf_test_predictions = ifelse(split < 5,
                                                         map2(train_data, rf_test_predictions, last_train_price),
                                                         NA),
                            # PCR:
                            pcr_test_predictions = ifelse(split < 5,
                                                         map2(train_data, pcr_test_predictions, last_train_price),
                                                         NA))
8.4.1.0.1 Holdout
cryptodata_nested_holdout <- mutate(filter(cryptodata_nested, split == 5),
                                    # XGBoost:
                                    xgb_holdout_predictions = map2(train_data, xgb_holdout_predictions, last_train_price),
                                    # Neural Network:
                                    nnet_holdout_predictions = map2(train_data, nnet_holdout_predictions, last_train_price),
                                    # Random Forest:
                                    rf_holdout_predictions = map2(train_data, rf_holdout_predictions, last_train_price),
                                    # PCR:
                                    pcr_holdout_predictions = map2(train_data, pcr_holdout_predictions, last_train_price))

Join the holdout data to all rows based on the cryptocurrency symbol alone:

cryptodata_nested <- left_join(cryptodata_nested, 
                               select(cryptodata_nested_holdout, symbol, 
                                      xgb_holdout_predictions, nnet_holdout_predictions, 
                                      rf_holdout_predictions, pcr_holdout_predictions),
                               by='symbol')
# Remove unwanted columns
cryptodata_nested <- select(cryptodata_nested, -xgb_holdout_predictions.x, 
                            -nnet_holdout_predictions.x,-rf_holdout_predictions.x, 
                            -pcr_holdout_predictions.x, -split.y)
# Rename the columns kept
cryptodata_nested <- rename(cryptodata_nested, 
                            xgb_holdout_predictions = 'xgb_holdout_predictions.y',
                            nnet_holdout_predictions = 'nnet_holdout_predictions.y',
                            rf_holdout_predictions = 'rf_holdout_predictions.y',
                            pcr_holdout_predictions = 'pcr_holdout_predictions.y',
                            split = 'split.x')
# Reset the correct grouping structure
cryptodata_nested <- group_by(cryptodata_nested, symbol, split)

8.4.2 Convert to % Change

Overwrite the old predictions with the new predictions adjusted as a percentage now:

cryptodata_nested <- mutate(cryptodata_nested,
                            # XGBoost:
                            xgb_test_predictions = ifelse(split < 5,
                                                         map(xgb_test_predictions, standardize_perc_change),
                                                         NA),
                            # holdout - all splits
                            xgb_holdout_predictions = map(xgb_holdout_predictions, standardize_perc_change),
                            # nnet:
                            nnet_test_predictions = ifelse(split < 5,
                                                         map(nnet_test_predictions, standardize_perc_change),
                                                         NA),
                            # holdout - all splits
                            nnet_holdout_predictions = map(nnet_holdout_predictions, standardize_perc_change),
                            # Random Forest:
                            rf_test_predictions = ifelse(split < 5,
                                                         map(rf_test_predictions, standardize_perc_change),
                                                         NA),
                            # holdout - all splits
                            rf_holdout_predictions = map(rf_holdout_predictions, standardize_perc_change),
                            # PCR:
                            pcr_test_predictions = ifelse(split < 5,
                                                         map(pcr_test_predictions, standardize_perc_change),
                                                         NA),
                            # holdout - all splits
                            pcr_holdout_predictions = map(pcr_holdout_predictions, standardize_perc_change))

8.4.3 Add Metrics

Add the RMSE and \(R^2\) metrics:

cryptodata_nested <- mutate(cryptodata_nested,
                            # XGBoost - RMSE - Test
                            xgb_rmse_test = unlist(ifelse(split < 5,
                                                         map2(xgb_test_predictions, actuals_test, evaluate_preds_rmse),
                                                         NA)),
                            # And holdout:
                            xgb_rmse_holdout = unlist(map2(xgb_holdout_predictions, actuals_holdout ,evaluate_preds_rmse)),
                            # XGBoost - R^2 - Test
                            xgb_rsq_test = unlist(ifelse(split < 5,
                                                         map2(xgb_test_predictions, actuals_test, evaluate_preds_rsq),
                                                         NA)),
                            # And holdout:
                            xgb_rsq_holdout = unlist(map2(xgb_holdout_predictions, actuals_holdout, evaluate_preds_rsq)),
                            # Neural Network - RMSE - Test
                            nnet_rmse_test = unlist(ifelse(split < 5,
                                                         map2(nnet_test_predictions, actuals_test, evaluate_preds_rmse),
                                                         NA)),
                            # And holdout:
                            nnet_rmse_holdout = unlist(map2(nnet_holdout_predictions, actuals_holdout, evaluate_preds_rmse)),
                            # Neural Network - R^2 - Test
                            nnet_rsq_test = unlist(ifelse(split < 5,
                                                         map2(nnet_test_predictions, actuals_test, evaluate_preds_rsq),
                                                         NA)),
                            # And holdout:
                            nnet_rsq_holdout = unlist(map2(nnet_holdout_predictions, actuals_holdout, evaluate_preds_rsq)),
                            # Random Forest - RMSE - Test
                            rf_rmse_test = unlist(ifelse(split < 5,
                                                         map2(rf_test_predictions, actuals_test, evaluate_preds_rmse),
                                                         NA)),
                            # And holdout:
                            rf_rmse_holdout = unlist(map2(rf_holdout_predictions, actuals_holdout, evaluate_preds_rmse)),
                            # Random Forest - R^2 - Test
                            rf_rsq_test = unlist(ifelse(split < 5,
                                                         map2(rf_test_predictions, actuals_test, evaluate_preds_rsq),
                                                         NA)),
                            # And holdout:
                            rf_rsq_holdout = unlist(map2(rf_holdout_predictions, actuals_holdout, evaluate_preds_rsq)),
                            # PCR - RMSE - Test
                            pcr_rmse_test = unlist(ifelse(split < 5,
                                                         map2(pcr_test_predictions, actuals_test, evaluate_preds_rmse),
                                                         NA)),
                            # And holdout:
                            pcr_rmse_holdout = unlist(map2(pcr_holdout_predictions, actuals_holdout, evaluate_preds_rmse)),
                            # PCR - R^2 - Test
                            pcr_rsq_test = unlist(ifelse(split < 5,
                                                         map2(pcr_test_predictions, actuals_test, evaluate_preds_rsq),
                                                         NA)),
                            # And holdout:
                            pcr_rsq_holdout = unlist(map2(pcr_holdout_predictions, actuals_holdout, evaluate_preds_rsq)))

Now we have RMSE and \(R^2\) values for every model created for every cryptocurrency and split of the data:

select(cryptodata_nested, lm_rmse_test, lm_rsq_test, lm_rmse_holdout, lm_rsq_holdout)
## # A tibble: 555 x 6
## # Groups:   symbol, split [555]
##    symbol split lm_rmse_test lm_rsq_test lm_rmse_holdout lm_rsq_holdout
##    <chr>  <dbl>        <dbl>       <dbl>           <dbl>          <dbl>
##  1 BTC        1        0.328       0.889           0.673         0.423 
##  2 ETH        1        0.860       0.539           0.465         0.779 
##  3 EOS        1        1.22        0.466           0.812         0.154 
##  4 LTC        1        1.17        0.523           0.491         0.528 
##  5 BSV        1        0.909       0.322           0.584         0.0650
##  6 ADA        1        2.11        0.148           0.474         0.0433
##  7 ZEC        1        0.719       0.769           1.15          0.438 
##  8 HT         1        0.824       0.598           0.994         0.581 
##  9 TRX        1        0.981       0.271           0.305         0.632 
## 10 KNC        1        0.813       0.802           0.490         0.273 
## # ... with 545 more rows

Only the results for the linear regression model are shown. There are equivalent columns for the XGBoost, neural network, random forest and PCR models.

8.5 Evaluate Metrics Across Splits

Next, let’s evaluate the metrics across all splits and keeping moving along with the model validation plan as was originally outlined. Let’s create a new dataset called [cryptodata_metrics][splits]{style=“color: blue;”} that is not grouped by the split column and is instead only grouped by the symbol:

cryptodata_metrics <- group_by(select(ungroup(cryptodata_nested),-split),symbol)

8.5.1 Evaluate RMSE Test

Now for each model we can create a new column giving the average RMSE for the 4 cross-validation test splits:

rmse_test <- mutate(cryptodata_metrics,
                      lm = mean(lm_rmse_test, na.rm = T),
                      xgb = mean(xgb_rmse_test, na.rm = T),
                      nnet = mean(nnet_rmse_test, na.rm = T),
                      rf = mean(rf_rmse_test, na.rm = T),
                      pcr = mean(pcr_rmse_test, na.rm = T))

Now we can use the gather() function to summarize the columns as rows:

rmse_test <- unique(gather(select(rmse_test, lm:pcr), 'model', 'rmse', -symbol))
# Show results
rmse_test
## # A tibble: 555 x 3
## # Groups:   symbol [111]
##    symbol model  rmse
##    <chr>  <chr> <dbl>
##  1 BTC    lm    0.862
##  2 ETH    lm    1.19 
##  3 EOS    lm    1.56 
##  4 LTC    lm    1.96 
##  5 BSV    lm    1.52 
##  6 ADA    lm    1.39 
##  7 ZEC    lm    1.25 
##  8 HT     lm    1.92 
##  9 TRX    lm    1.14 
## 10 KNC    lm    1.49 
## # ... with 545 more rows

Now tag the results as having been for the test set:

rmse_test$eval_set <- 'test'

8.5.2 Holdout

Now repeat the same process for the holdout set:

rmse_holdout <- mutate(cryptodata_metrics,
                      lm = mean(lm_rmse_holdout, na.rm = T),
                      xgb = mean(xgb_rmse_holdout, na.rm = T),
                      nnet = mean(nnet_rmse_holdout, na.rm = T),
                      rf = mean(rf_rmse_holdout, na.rm = T),
                      pcr = mean(pcr_rmse_holdout, na.rm = T))

Again, use the gather() function to summarize the columns as rows:

rmse_holdout <- unique(gather(select(rmse_holdout, lm:pcr), 'model', 'rmse', -symbol))
# Show results
rmse_holdout
## # A tibble: 555 x 3
## # Groups:   symbol [111]
##    symbol model  rmse
##    <chr>  <chr> <dbl>
##  1 BTC    lm    0.673
##  2 ETH    lm    0.465
##  3 EOS    lm    0.812
##  4 LTC    lm    0.491
##  5 BSV    lm    0.584
##  6 ADA    lm    0.474
##  7 ZEC    lm    1.15 
##  8 HT     lm    0.994
##  9 TRX    lm    0.305
## 10 KNC    lm    0.490
## # ... with 545 more rows

Now tag the results as having been for the holdout set:

rmse_holdout$eval_set <- 'holdout'

8.5.3 Union Results

Now we can union() the results to stack the rows from the two datasets on top of each other:

rmse_scores <- union(rmse_test, rmse_holdout)

8.6 Evaluate R^2

Now let’s repeat the same steps we took for the RMSE metrics above for the \(R^2\) metric as well.

8.6.1 Test

For each model again we will create a new column giving the average \(R^2\) for the 4 cross-validation test splits:

rsq_test <- mutate(cryptodata_metrics,
                      lm = mean(lm_rsq_test, na.rm = T),
                      xgb = mean(xgb_rsq_test, na.rm = T),
                      nnet = mean(nnet_rsq_test, na.rm = T),
                      rf = mean(rf_rsq_test, na.rm = T),
                      pcr = mean(pcr_rsq_test, na.rm = T))

Now we can use the gather() function to summarize the columns as rows:

rsq_test <- unique(gather(select(rsq_test, lm:pcr), 'model', 'rsq', -symbol))
# Show results
rsq_test
## # A tibble: 555 x 3
## # Groups:   symbol [111]
##    symbol model   rsq
##    <chr>  <chr> <dbl>
##  1 BTC    lm    0.636
##  2 ETH    lm    0.549
##  3 EOS    lm    0.500
##  4 LTC    lm    0.515
##  5 BSV    lm    0.369
##  6 ADA    lm    0.490
##  7 ZEC    lm    0.547
##  8 HT     lm    0.248
##  9 TRX    lm    0.490
## 10 KNC    lm    0.582
## # ... with 545 more rows

Now tag the results as having been for the test set

rsq_test$eval_set <- 'test'

8.6.2 Holdout

Do the same and calculate the averages for the holdout sets:

rsq_holdout <- mutate(cryptodata_metrics,
                      lm = mean(lm_rsq_holdout, na.rm = T),
                      xgb = mean(xgb_rsq_holdout, na.rm = T),
                      nnet = mean(nnet_rsq_holdout, na.rm = T),
                      rf = mean(rf_rsq_holdout, na.rm = T),
                      pcr = mean(pcr_rsq_holdout, na.rm = T))

Now we can use the gather() function to summarize the columns as rows:

rsq_holdout <- unique(gather(select(rsq_holdout, lm:pcr), 'model', 'rsq', -symbol))
# Show results
rsq_holdout
## # A tibble: 555 x 3
## # Groups:   symbol [111]
##    symbol model    rsq
##    <chr>  <chr>  <dbl>
##  1 BTC    lm    0.423 
##  2 ETH    lm    0.779 
##  3 EOS    lm    0.154 
##  4 LTC    lm    0.528 
##  5 BSV    lm    0.0650
##  6 ADA    lm    0.0433
##  7 ZEC    lm    0.438 
##  8 HT     lm    0.581 
##  9 TRX    lm    0.632 
## 10 KNC    lm    0.273 
## # ... with 545 more rows

Now tag the results as having been for the holdout set:

rsq_holdout$eval_set <- 'holdout'

8.6.3 Union Results

rsq_scores <- union(rsq_test, rsq_holdout)

8.7 Visualize Results

Now we can take the same tools we learned in the Visualization section from earlier and visualize the results of the models.

8.7.1 RMSE Visualization

8.7.2 Both

Now we have everything we need to use the two metrics to compare the results.

8.7.2.1 Join Datasets

First join the two objects rmse_scores and rsq_scores into the new object **plot_scores:

plot_scores <- merge(rmse_scores, rsq_scores)

8.7.2.2 Plot Results

Now we can plot the results on a chart:

ggplot(plot_scores, aes(x=rsq, y=rmse, color = model)) +
  geom_point() +
  ylim(c(0,10))

Running the same code wrapped in the ggplotly() function from the plotly package (as we have already done) we can make the chart interactive. Try hovering over the points on the chart with your mouse.

ggplotly(ggplot(plot_scores, aes(x=rsq, y=rmse, color = model, symbol = symbol)) +
           geom_point() +
           ylim(c(0,10)),
         tooltip = c("model", "symbol", "rmse", "rsq"))

The additional tooltip argument was passed to ggpltoly() to specify the label when hovering over the individual points.

8.7.3 Results by the Cryptocurrency

We can use the facet_wrap() function from ggplot2 to create an individual chart for each cryptocurrency:

ggplot(plot_scores, aes(x=rsq, y=rmse, color = model)) +
  geom_point() +
  geom_smooth() +
  ylim(c(0,10)) +
  facet_wrap(~symbol)

Every 12 hours once this document reaches this point, the results are saved to GitHub using the pins package (which we used to read in the data at the start), and a separate script running on a different server creates the complete dataset in our database over time. You won’t be able to run the code shown below (nor do you have a reason to):

# register board
board_register("github", repo = "predictcrypto/pins", token=pins_key)
# Add current date time
plot_scores$last_refreshed <- Sys.time()
# pin data
pin(plot_scores, board='github', name='crypto_tutorial_results_latest')

8.8 Interactive Dashboard

Use the interactive app below to explore the results over time by the individual cryptocurrency. Use the filters on the left sidebar to visualize the results you are interested in:

If you have trouble viewing the embedded dashboard you can open it here instead: https://predictcrypto.shinyapps.io/tutorial_latest_model_summary/

The default view shows the holdout results for all models. Another interesting comparison to make is between the holdout and the test set for fewer models (2 is ideal).

The app shown above also has a button to Show Code. If you were to show the code and copy and paste it into an RStudio session on your computer into a file with the .Rmd file extension and you then Knit the file, the same exact app should show up on your computer, no logins or setup outside of the packages required for the code to run; RStudio should automatically prompt you to install packages that are not currently installed on your computer.

8.9 Visualizations - Historical Metrics

We can pull the same data into this R session using the pin_get() function:

metrics_historical <- pin_get(name = "full_metrics")

The data is limited to metrics for runs from the past 30 days and includes new data every 12 hours. Using the tools we used in the data prep section, we can answer a couple more questions.

8.9.1 Best Models

Overall, which model has the best metrics for all runs from the last 30 days?

8.9.1.1 Summarize the data

# First create grouped data
best_models <- group_by(metrics_historical, model, eval_set)
# Now summarize the data
best_models <- summarize(best_models,
                         rmse = mean(rmse, na.rm=T),
                         rsq  = mean(rsq, na.rm=T))
# Show results
best_models
## # A tibble: 10 x 4
## # Groups:   model [5]
##    model eval_set  rmse    rsq
##    <chr> <chr>    <dbl>  <dbl>
##  1 lm    holdout  15.5  0.506 
##  2 lm    test      4.07 0.478 
##  3 nnet  holdout   4.31 0.149 
##  4 nnet  test      4.63 0.164 
##  5 pcr   holdout   2.72 0.252 
##  6 pcr   test      2.92 0.278 
##  7 rf    holdout   3.96 0.114 
##  8 rf    test      3.83 0.129 
##  9 xgb   holdout   5.01 0.0693
## 10 xgb   test      4.56 0.0885

8.9.1.2 Plot RMSE by Model

ggplot(best_models, aes(model, rmse, fill = eval_set)) + 
  geom_bar(stat = "identity", position = 'dodge') +
  ggtitle('RMSE by Model', 'Comparing Test and Holdout')

8.9.1.3 Plot \(R^2\) by Model

ggplot(best_models, aes(model, rsq, fill = eval_set)) + 
  geom_bar(stat = "identity", position = 'dodge') +
  ggtitle('R^2 by Model', 'Comparing Test and Holdout')

8.9.2 Most Predictable Cryptocurrency

Overall, which cryptocurrency has the best metrics for all runs from the last 30 days?

8.9.2.1 Summarize the data

# First create grouped data
predictable_cryptos <- group_by(metrics_historical, symbol, eval_set)
# Now summarize the data
predictable_cryptos <- summarize(predictable_cryptos,
                         rmse = mean(rmse, na.rm=T),
                         rsq  = mean(rsq, na.rm=T))
# Arrange from most predictable (according to R^2) to least 
predictable_cryptos <- arrange(predictable_cryptos, desc(rsq))
# Show results
predictable_cryptos
## # A tibble: 178 x 4
## # Groups:   symbol [89]
##    symbol eval_set  rmse   rsq
##    <chr>  <chr>    <dbl> <dbl>
##  1 NAV    test      3.30 0.434
##  2 POA    holdout   4.60 0.423
##  3 CUR    holdout   6.09 0.410
##  4 CND    test      1.84 0.374
##  5 CND    holdout   5.24 0.360
##  6 SEELE  holdout   8.88 0.355
##  7 ADXN   test      9.26 0.348
##  8 RCN    test      5.03 0.337
##  9 BTC    test      1.32 0.331
## 10 SUN    holdout   3.17 0.330
## # ... with 168 more rows

Show the top 15 most predictable cryptocurrencies (according to the \(R^2\)) using the formattable package (Ren and Russell 2016) to color code the cells:

formattable(head(predictable_cryptos ,15), 
            list(rmse = color_tile("#71CA97", "red"),
                 rsq = color_tile("firebrick1", "#71CA97")))
symbol eval_set rmse rsq
NAV test 3.299791 0.4338237
POA holdout 4.596582 0.4229192
CUR holdout 6.088416 0.4098434
CND test 1.835020 0.3737691
CND holdout 5.238670 0.3601346
SEELE holdout 8.876745 0.3548372
ADXN test 9.263022 0.3478368
RCN test 5.030197 0.3367737
BTC test 1.321379 0.3307333
SUN holdout 3.172350 0.3299285
AAB test 39.511003 0.3262746
ETH test 1.721290 0.3197170
LTC test 2.102832 0.3189001
LEO test 1.695632 0.3166553
RCN holdout 7.564985 0.3130917

8.9.3 Accuracy Over Time

8.9.3.1 Summarize the data

# First create grouped data
accuracy_over_time <- group_by(metrics_historical, date_utc)
# Now summarize the data
accuracy_over_time <- summarize(accuracy_over_time, 
                                rmse = mean(rmse, na.rm=T),
                                rsq  = mean(rsq, na.rm=T))
# Ungroup data
accuracy_over_time <- ungroup(accuracy_over_time)
# Convert date/time
accuracy_over_time$date_utc <- anytime(accuracy_over_time$date_utc)
# Show results
accuracy_over_time
## # A tibble: 30 x 3
##    date_utc             rmse   rsq
##    <dttm>              <dbl> <dbl>
##  1 2021-01-12 00:00:00  4.05 0.241
##  2 2021-01-13 00:00:00  4.29 0.236
##  3 2021-01-14 00:00:00  4.06 0.251
##  4 2021-01-16 00:00:00  4.25 0.214
##  5 2021-01-17 00:00:00  3.78 0.199
##  6 2021-01-18 00:00:00  3.88 0.212
##  7 2021-01-19 00:00:00  3.86 0.204
##  8 2021-01-20 00:00:00  4.65 0.207
##  9 2021-01-21 00:00:00  3.41 0.222
## 10 2021-01-22 00:00:00  4.05 0.231
## # ... with 20 more rows

8.9.3.2 Plot RMSE

Remember, for RMSE the lower the score, the more accurate the models were.

ggplot(accuracy_over_time, aes(x = date_utc, y = rmse, group = 1)) +
  # Plot RMSE over time
  geom_point(color = 'red', size = 2) +
  geom_line(color = 'red', size = 1)

8.9.3.3 Plot R^2

For the R^2 recall that we are looking at the correlation between the predictions made and what actually happened, so the higher the score the better, with a maximum score of 1 that would mean the predictions were 100% correlated with each other and therefore identical.

ggplot(accuracy_over_time, aes(x = date_utc, y = rsq, group = 1)) +
  # Plot R^2 over time
  geom_point(aes(x = date_utc, y = rsq), color = 'dark green', size = 2) +
  geom_line(aes(x = date_utc, y = rsq), color = 'dark green', size = 1)

Refer back to the interactive dashboard to take a more specific subset of results instead of the aggregate analysis shown above.

References

Ren, Kun, and Kenton Russell. 2016. Formattable: Create Formattable Data Structures. https://CRAN.R-project.org/package=formattable.