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
## 340.209154126 0.006779914 246.918414270
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.05531543 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 ($).
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:
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:
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.1289772
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:
## # A tibble: 385 x 4
## # Groups: symbol, split [385]
## symbol split lm_rsq_test lm_rsq_holdout
## <chr> <dbl> <dbl> <dbl>
## 1 BTC 1 0.854 0.257
## 2 ETH 1 0.598 0.0837
## 3 EOS 1 0.168 0.387
## 4 LTC 1 0.563 0.337
## 5 BSV 1 0.0520 0.00519
## 6 ADA 1 0.722 0.00427
## 7 ZEC 1 0.494 0.234
## 8 HT 1 0.426 0.637
## 9 TRX 1 0.0659 0.212
## 10 XMR 1 0.111 0.0455
## # ... with 375 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).
## # A tibble: 385 x 6
## # Groups: symbol, split [385]
## symbol split lm_rmse_test lm_rmse_holdout lm_rsq_test lm_rsq_holdout
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 BTC 1 0.129 0.456 0.854 0.257
## 2 ETH 1 0.330 0.644 0.598 0.0837
## 3 EOS 1 0.823 0.732 0.168 0.387
## 4 LTC 1 0.749 0.900 0.563 0.337
## 5 BSV 1 2.07 1.86 0.0520 0.00519
## 6 ADA 1 0.402 0.978 0.722 0.00427
## 7 ZEC 1 0.540 0.860 0.494 0.234
## 8 HT 1 0.155 8.19 0.426 0.637
## 9 TRX 1 0.860 0.699 0.0659 0.212
## 10 XMR 1 1.24 0.825 0.111 0.0455
## 11 KNC 1 0.735 0.622 0.165 0.0251
## 12 ZRX 1 0.513 0.613 0.814 0.130
## 13 BAT 1 0.932 0.792 0.183 0.121
## 14 BNT 1 0.493 0.266 0.609 0.848
## 15 MANA 1 0.866 0.728 0.216 0.203
## 16 ENJ 1 1.52 0.848 0.132 0.000545
## 17 XEM 1 0.549 0.638 0.688 0.201
## 18 BTG 1 1.22 0.812 0.783 0.351
## 19 KMD 1 1.15 1.85 0.0619 0.396
## 20 ELF 1 2.00 1.02 0.00241 0.128
## 21 NEXO 1 0.507 0.894 0.623 0.313
## 22 CHZ 1 1.25 0.733 0.0664 0.0320
## 23 BRD 1 2.44 3.28 0.775 0.868
## 24 DCR 1 0.182 1.62 0.910 0.0798
## 25 WAXP 1 0.621 0.642 0.595 0.220
## 26 VIB 1 2.10 7.36 0.198 0.155
## 27 AVA 1 0.734 0.609 0.170 0.589
## 28 MXC 1 2.65 263. 0.199 0.0329
## 29 GMTT 1 0.825 5.52 0.00524 0.00523
## 30 ARNM 1 1.78 1.08 0.0184 0.661
## 31 GAS 1 0.716 1.40 0.762 0.0110
## 32 DODO 1 0.388 1.33 0.828 0.677
## 33 DYDX 1 1.65 1.36 0.798 0.155
## 34 1INCH 1 0.754 0.608 0.362 0.00934
## 35 OXT 1 1.12 1.25 0.893 0.166
## 36 BAKE 1 1.28 0.755 0.118 0.0460
## 37 DOT 1 0.504 0.750 0.461 0.0666
## 38 FIDA 1 1.24 0.690 0.00225 0.145
## 39 INJ 1 351. 1.40 0.240 0.000992
## 40 TOMO 1 NaN 1.20 NA 0.406
## 41 QTUM 1 0.358 0.615 0.758 0.782
## 42 TOWN 1 4.10 1.13 0.935 0.435
## 43 IOTA 1 0.533 0.628 0.612 0.0245
## 44 NEAR 1 0.551 0.869 0.757 0.0363
## 45 LSK 1 0.961 0.588 0.110 0.201
## 46 PERP 1 9.01 1.07 0.0190 0.140
## 47 RIF 1 0.971 6.80 0.400 0.0742
## 48 ATOM 1 0.617 0.899 0.473 0.00386
## 49 ALICE 1 0.608 0.747 0.799 0.247
## 50 MTLX 1 0.863 0.870 0.683 0.832
## 51 PLI 1 2.79 2.54 0.496 0.287
## 52 XDC 1 0.435 0.451 0.151 0.758
## 53 SNX 1 0.588 2.14 0.554 0.168
## 54 FEI 1 0.0114 59.7 0.0793 0.000863
## 55 THETA 1 0.590 0.589 0.868 0.283
## 56 RUNE 1 1.22 0.579 0.00787 0.194
## 57 KLAY 1 0.676 0.489 0.239 0.141
## 58 XCH 1 0.409 0.718 0.781 0.335
## 59 BAL 1 0.754 0.744 0.241 0.124
## 60 REN 1 1.14 0.488 0.339 0.899
## 61 VGX 1 0.729 1.45 0.00861 0.000499
## 62 GLMR 1 0.807 0.958 0.0432 0.0344
## 63 SAND 1 0.707 0.708 0.370 0.177
## 64 ANKR 1 1.06 0.968 0.0451 0.0738
## 65 FTT 1 0.458 1.46 0.0917 0.255
## 66 BCH 1 0.376 0.665 0.734 0.260
## 67 GMT 1 2.87 0.819 0.443 0.179
## 68 T 1 4.10 0.514 0.00199 0.437
## 69 DASH 1 1.27 1.19 0.758 0.263
## 70 SXP 1 0.579 0.625 0.0881 0.302
## 71 LAZIO 1 0.843 0.339 0.000572 0.539
## 72 ARDR 1 22.4 11.2 0.230 0.00682
## 73 SKL 1 1.01 0.749 0.964 0.673
## 74 APT 1 2.12 1.35 0.133 0.0000839
## 75 ETHW 1 1.21 0.791 0.126 0.0574
## 76 ARNM 2 3.73 1.08 0.268 0.661
## 77 XRD 1 2.12 234. 0.507 0.0180
## 78 OXT 2 1.16 1.25 0.000788 0.166
## 79 GAS 2 1.72 1.40 0.135 0.0110
## 80 BRD 2 2.11 3.28 1.00 0.868
## 81 MTLX 2 4.91 0.870 0.0270 0.832
## 82 WAXP 2 0.791 0.642 0.477 0.220
## 83 DYDX 2 1.47 1.36 0.303 0.155
## 84 VGX 2 2.40 1.45 0.447 0.000499
## 85 XMR 2 0.701 0.825 0.246 0.0455
## 86 EOS 2 1.11 0.732 0.0475 0.387
## 87 HT 2 0.781 8.19 0.186 0.637
## 88 BTG 2 0.813 0.812 0.243 0.351
## 89 XDC 2 0.391 0.451 0.568 0.758
## 90 VIB 2 5.84 7.36 0.00590 0.155
## 91 LSK 2 1.09 0.588 0.488 0.201
## 92 SXP 2 0.610 0.625 0.746 0.302
## 93 BTC 2 0.234 0.456 0.708 0.257
## 94 ETH 2 0.757 0.644 0.817 0.0837
## 95 LTC 2 0.691 0.900 0.423 0.337
## 96 BSV 2 0.811 1.86 0.0804 0.00519
## 97 ADA 2 0.824 0.978 0.333 0.00427
## 98 ZEC 2 1.02 0.860 0.366 0.234
## 99 TRX 2 0.598 0.699 0.142 0.212
## 100 KNC 2 0.644 0.622 0.804 0.0251
## 101 ZRX 2 0.855 0.613 0.540 0.130
## 102 BAT 2 0.588 0.792 0.894 0.121
## 103 MANA 2 1.65 0.728 0.182 0.203
## 104 ENJ 2 2.14 0.848 0.0575 0.000545
## 105 XEM 2 0.866 0.638 0.527 0.201
## 106 KMD 2 0.493 1.85 0.479 0.396
## 107 ELF 2 0.744 1.02 0.857 0.128
## 108 NEXO 2 1.04 0.894 0.235 0.313
## 109 CHZ 2 1.87 0.733 0.00809 0.0320
## 110 DCR 2 1.27 1.62 0.393 0.0798
## 111 AVA 2 1.78 0.609 0.172 0.589
## 112 1INCH 2 0.549 0.608 0.532 0.00934
## 113 BAKE 2 1.33 0.755 0.0865 0.0460
## 114 DOT 2 1.05 0.750 0.441 0.0666
## 115 FIDA 2 8.81 0.690 0.139 0.145
## 116 QTUM 2 1.37 0.615 0.466 0.782
## 117 IOTA 2 0.705 0.628 0.431 0.0245
## 118 NEAR 2 1.96 0.869 0.00107 0.0363
## 119 RIF 2 0.507 6.80 0.567 0.0742
## 120 ATOM 2 0.380 0.899 0.836 0.00386
## 121 ALICE 2 2.88 0.747 0.0128 0.247
## 122 PLI 2 0.572 2.54 0.437 0.287
## 123 SNX 2 0.536 2.14 0.896 0.168
## 124 THETA 2 1.10 0.589 0.462 0.283
## 125 RUNE 2 1.71 0.579 0.0865 0.194
## 126 BAL 2 1.27 0.744 0.0316 0.124
## 127 GLMR 2 1.72 0.958 0.462 0.0344
## 128 SAND 2 2.12 0.708 0.00638 0.177
## 129 ANKR 2 1.09 0.968 0.686 0.0738
## 130 BCH 2 0.555 0.665 0.780 0.260
## 131 FTT 2 1.92 1.46 0.000358 0.255
## 132 GMT 2 2.49 0.819 0.123 0.179
## 133 DASH 2 0.663 1.19 0.435 0.263
## 134 MXC 2 2.25 263. 0.151 0.0329
## 135 DODO 2 2.49 1.33 0.191 0.677
## 136 KLAY 2 1.45 0.489 0.449 0.141
## 137 XCH 2 0.692 0.718 0.199 0.335
## 138 T 2 1.06 0.514 0.522 0.437
## 139 LAZIO 2 1.37 0.339 0.290 0.539
## 140 TOWN 2 1.88 1.13 0.0844 0.435
## 141 FEI 2 2945. 59.7 0.0128 0.000863
## 142 REN 2 0.691 0.488 0.581 0.899
## 143 BNT 2 1.01 0.266 0.101 0.848
## 144 ARDR 2 1.20 11.2 0.164 0.00682
## 145 GMTT 2 7.88 5.52 0.288 0.00523
## 146 ETHW 2 1.16 0.791 0.311 0.0574
## 147 PERP 2 1.19 1.07 0.515 0.140
## 148 APT 2 1.24 1.35 0.705 0.0000839
## 149 SKL 2 1.34 0.749 0.825 0.673
## 150 TOMO 2 0.811 1.20 0.730 0.406
## 151 XRD 2 681. 234. 0.00155 0.0180
## 152 OXT 3 0.537 1.25 0.268 0.166
## 153 INJ 2 0.989 1.40 0.418 0.000992
## 154 GAS 3 2.45 1.40 0.0498 0.0110
## 155 AGIX 1 1.30 2.70 0.141 0.0188
## 156 WAXP 3 0.543 0.642 0.826 0.220
## 157 DYDX 3 0.739 1.36 0.883 0.155
## 158 LSK 3 0.457 0.588 0.838 0.201
## 159 HT 3 1.92 8.19 0.0284 0.637
## 160 EOS 3 0.759 0.732 0.744 0.387
## 161 XMR 3 0.515 0.825 0.150 0.0455
## 162 BTG 3 0.402 0.812 0.932 0.351
## 163 XCH 3 0.333 0.718 0.947 0.335
## 164 BTC 3 0.279 0.456 0.771 0.257
## 165 XDC 3 0.252 0.451 0.928 0.758
## 166 VGX 3 1.39 1.45 0.0234 0.000499
## 167 VIB 3 2.41 7.36 0.412 0.155
## 168 DODO 3 6.63 1.33 0.151 0.677
## 169 DASH 3 0.323 1.19 0.905 0.263
## 170 ETH 3 0.314 0.644 0.687 0.0837
## 171 BSV 3 0.351 1.86 0.768 0.00519
## 172 ADA 3 0.383 0.978 0.840 0.00427
## 173 ZEC 3 0.567 0.860 0.694 0.234
## 174 TRX 3 0.229 0.699 0.650 0.212
## 175 KNC 3 0.598 0.622 0.824 0.0251
## 176 BAT 3 0.625 0.792 0.717 0.121
## 177 MANA 3 0.504 0.728 0.781 0.203
## 178 ENJ 3 1.10 0.848 0.560 0.000545
## 179 XEM 3 0.312 0.638 0.845 0.201
## 180 KMD 3 0.625 1.85 0.623 0.396
## 181 ELF 3 1.94 1.02 0.346 0.128
## 182 NEXO 3 0.684 0.894 0.468 0.313
## 183 CHZ 3 0.378 0.733 0.708 0.0320
## 184 DCR 3 1.09 1.62 0.530 0.0798
## 185 AVA 3 1.03 0.609 0.282 0.589
## 186 1INCH 3 0.677 0.608 0.304 0.00934
## 187 BAKE 3 1.71 0.755 0.490 0.0460
## 188 DOT 3 0.943 0.750 0.688 0.0666
## 189 FIDA 3 3.33 0.690 0.152 0.145
## 190 TOWN 3 1.04 1.13 0.631 0.435
## 191 NEAR 3 0.663 0.869 0.635 0.0363
## 192 ALICE 3 0.886 0.747 0.754 0.247
## 193 SNX 3 0.820 2.14 0.764 0.168
## 194 THETA 3 0.553 0.589 0.740 0.283
## 195 RUNE 3 0.637 0.579 0.576 0.194
## 196 BAL 3 0.337 0.744 0.882 0.124
## 197 GLMR 3 1.44 0.958 0.503 0.0344
## 198 SAND 3 0.478 0.708 0.880 0.177
## 199 ANKR 3 4.76 0.968 0.000442 0.0738
## 200 FTT 3 1.48 1.46 0.144 0.255
## 201 BCH 3 0.470 0.665 0.726 0.260
## 202 GMT 3 1.55 0.819 0.201 0.179
## 203 LTC 3 0.361 0.900 0.437 0.337
## 204 ZRX 3 1.18 0.613 0.577 0.130
## 205 MXC 3 225. 263. 0.0534 0.0329
## 206 QTUM 3 1.02 0.615 0.572 0.782
## 207 IOTA 3 0.504 0.628 0.559 0.0245
## 208 RIF 3 11.5 6.80 0.100 0.0742
## 209 ATOM 3 0.602 0.899 0.573 0.00386
## 210 PLI 3 0.859 2.54 0.220 0.287
## 211 KLAY 3 0.686 0.489 0.806 0.141
## 212 SXP 3 0.896 0.625 0.0536 0.302
## 213 LAZIO 3 0.834 0.339 0.525 0.539
## 214 T 3 0.422 0.514 0.787 0.437
## 215 BNT 3 0.613 0.266 0.812 0.848
## 216 REN 3 1.76 0.488 0.00341 0.899
## 217 ARDR 3 2.29 11.2 0.385 0.00682
## 218 FEI 3 83.5 59.7 0.254 0.000863
## 219 ARNM 3 4.12 1.08 0.00259 0.661
## 220 ETHW 3 0.745 0.791 0.344 0.0574
## 221 GMTT 3 4.47 5.52 0.000763 0.00523
## 222 PERP 3 5.05 1.07 0.0913 0.140
## 223 MTLX 3 0.555 0.870 0.733 0.832
## 224 BRD 3 12.2 3.28 0.142 0.868
## 225 APT 3 0.692 1.35 0.412 0.0000839
## 226 SKL 3 1.21 0.749 0.273 0.673
## 227 TOMO 3 6.80 1.20 0.00116 0.406
## 228 XRD 3 228. 234. 0.0000508 0.0180
## 229 AGIX 2 5.59 2.70 0.0000682 0.0188
## 230 OXT 4 1.75 1.25 0.611 0.166
## 231 INJ 3 1.30 1.40 0.500 0.000992
## 232 GAS 4 1.73 1.40 0.489 0.0110
## 233 WAXP 4 0.781 0.642 0.959 0.220
## 234 LSK 4 12.6 0.588 0.228 0.201
## 235 HT 4 3.23 8.19 0.384 0.637
## 236 XCH 4 0.877 0.718 0.412 0.335
## 237 BTC 4 1.59 0.456 0.590 0.257
## 238 VGX 4 32.6 1.45 0.153 0.000499
## 239 BTG 4 1.75 0.812 0.171 0.351
## 240 XMR 4 1.20 0.825 0.140 0.0455
## 241 DODO 4 1.96 1.33 0.839 0.677
## 242 XDC 4 0.525 0.451 0.866 0.758
## 243 EOS 4 1.40 0.732 0.652 0.387
## 244 VIB 4 1.94 7.36 0.0845 0.155
## 245 DASH 4 3.30 1.19 0.824 0.263
## 246 ETH 4 0.603 0.644 0.701 0.0837
## 247 BSV 4 1.16 1.86 0.181 0.00519
## 248 ADA 4 0.928 0.978 0.0701 0.00427
## 249 ZEC 4 0.894 0.860 0.686 0.234
## 250 TRX 4 2.42 0.699 0.00842 0.212
## 251 KNC 4 1.08 0.622 0.359 0.0251
## 252 BAT 4 1.18 0.792 0.539 0.121
## 253 MANA 4 1.02 0.728 0.385 0.203
## 254 ENJ 4 1.14 0.848 0.241 0.000545
## 255 XEM 4 1.16 0.638 0.630 0.201
## 256 KMD 4 1.17 1.85 0.396 0.396
## 257 ELF 4 6.06 1.02 0.787 0.128
## 258 NEXO 4 2.27 0.894 0.160 0.313
## 259 CHZ 4 1.13 0.733 0.0903 0.0320
## 260 DCR 4 1.48 1.62 0.295 0.0798
## 261 AVA 4 0.849 0.609 0.403 0.589
## 262 1INCH 4 1.12 0.608 0.208 0.00934
## 263 BAKE 4 0.986 0.755 0.377 0.0460
## 264 DOT 4 0.927 0.750 0.430 0.0666
## 265 FIDA 4 1.23 0.690 0.278 0.145
## 266 TOWN 4 4.96 1.13 0.000451 0.435
## 267 NEAR 4 1.09 0.869 0.254 0.0363
## 268 ALICE 4 1.29 0.747 0.120 0.247
## 269 PLI 4 2.20 2.54 0.188 0.287
## 270 SNX 4 1.94 2.14 0.292 0.168
## 271 THETA 4 0.754 0.589 0.482 0.283
## 272 RUNE 4 1.03 0.579 0.512 0.194
## 273 BAL 4 0.743 0.744 0.636 0.124
## 274 GLMR 4 8.58 0.958 0.222 0.0344
## 275 SAND 4 1.07 0.708 0.187 0.177
## 276 ANKR 4 1.18 0.968 0.296 0.0738
## 277 FTT 4 23.9 1.46 0.00780 0.255
## 278 BCH 4 0.995 0.665 0.212 0.260
## 279 GMT 4 1.36 0.819 0.256 0.179
## 280 SXP 4 1.09 0.625 0.445 0.302
## 281 LTC 4 2.75 0.900 0.585 0.337
## 282 ZRX 4 1.38 0.613 0.223 0.130
## 283 BNT 4 1.29 0.266 0.141 0.848
## 284 MXC 4 331. 263. 0.0285 0.0329
## 285 QTUM 4 1.21 0.615 0.316 0.782
## 286 IOTA 4 1.29 0.628 0.0777 0.0245
## 287 RIF 4 3.02 6.80 0.390 0.0742
## 288 ATOM 4 0.850 0.899 0.433 0.00386
## 289 KLAY 4 1.07 0.489 0.586 0.141
## 290 T 4 1.13 0.514 0.565 0.437
## 291 LAZIO 4 0.841 0.339 0.504 0.539
## 292 REN 4 1.87 0.488 0.317 0.899
## 293 ARDR 4 4.36 11.2 0.0000353 0.00682
## 294 DYDX 4 0.970 1.36 0.715 0.155
## 295 FEI 4 40.0 59.7 0.00000850 0.000863
## 296 ETHW 4 0.911 0.791 0.671 0.0574
## 297 PERP 4 1.82 1.07 0.510 0.140
## 298 AGIX 3 3.56 2.70 0.250 0.0188
## 299 GMTT 4 62.0 5.52 0.0841 0.00523
## 300 MTLX 4 2.14 0.870 0.475 0.832
## 301 APT 4 1.05 1.35 0.918 0.0000839
## 302 BRD 4 7.92 3.28 0.000191 0.868
## 303 SKL 4 1.74 0.749 0.695 0.673
## 304 TOMO 4 2.47 1.20 0.821 0.406
## 305 XRD 4 1.72 234. 0.0409 0.0180
## 306 OXT 5 NA 1.25 NA 0.166
## 307 INJ 4 2.57 1.40 0.488 0.000992
## 308 ARNM 4 6.27 1.08 0.291 0.661
## 309 GAS 5 NA 1.40 NA 0.0110
## 310 AGIX 4 9.58 2.70 0.402 0.0188
## 311 WAXP 5 NA 0.642 NA 0.220
## 312 XCH 5 NA 0.718 NA 0.335
## 313 VGX 5 NA 1.45 NA 0.000499
## 314 DODO 5 NA 1.33 NA 0.677
## 315 XDC 5 NA 0.451 NA 0.758
## 316 EOS 5 NA 0.732 NA 0.387
## 317 HT 5 NA 8.19 NA 0.637
## 318 XMR 5 NA 0.825 NA 0.0455
## 319 BTG 5 NA 0.812 NA 0.351
## 320 VIB 5 NA 7.36 NA 0.155
## 321 BNT 5 NA 0.266 NA 0.848
## 322 PLI 5 NA 2.54 NA 0.287
## 323 DASH 5 NA 1.19 NA 0.263
## 324 SXP 5 NA 0.625 NA 0.302
## 325 ETH 5 NA 0.644 NA 0.0837
## 326 BSV 5 NA 1.86 NA 0.00519
## 327 ADA 5 NA 0.978 NA 0.00427
## 328 TRX 5 NA 0.699 NA 0.212
## 329 ZEC 5 NA 0.860 NA 0.234
## 330 KNC 5 NA 0.622 NA 0.0251
## 331 BAT 5 NA 0.792 NA 0.121
## 332 MANA 5 NA 0.728 NA 0.203
## 333 ENJ 5 NA 0.848 NA 0.000545
## 334 XEM 5 NA 0.638 NA 0.201
## 335 KMD 5 NA 1.85 NA 0.396
## 336 ELF 5 NA 1.02 NA 0.128
## 337 NEXO 5 NA 0.894 NA 0.313
## 338 CHZ 5 NA 0.733 NA 0.0320
## 339 DCR 5 NA 1.62 NA 0.0798
## 340 AVA 5 NA 0.609 NA 0.589
## 341 1INCH 5 NA 0.608 NA 0.00934
## 342 BAKE 5 NA 0.755 NA 0.0460
## 343 DOT 5 NA 0.750 NA 0.0666
## 344 FIDA 5 NA 0.690 NA 0.145
## 345 NEAR 5 NA 0.869 NA 0.0363
## 346 ALICE 5 NA 0.747 NA 0.247
## 347 SNX 5 NA 2.14 NA 0.168
## 348 THETA 5 NA 0.589 NA 0.283
## 349 RUNE 5 NA 0.579 NA 0.194
## 350 BAL 5 NA 0.744 NA 0.124
## 351 REN 5 NA 0.488 NA 0.899
## 352 GLMR 5 NA 0.958 NA 0.0344
## 353 SAND 5 NA 0.708 NA 0.177
## 354 ANKR 5 NA 0.968 NA 0.0738
## 355 BCH 5 NA 0.665 NA 0.260
## 356 FTT 5 NA 1.46 NA 0.255
## 357 GMT 5 NA 0.819 NA 0.179
## 358 T 5 NA 0.514 NA 0.437
## 359 LTC 5 NA 0.900 NA 0.337
## 360 ZRX 5 NA 0.613 NA 0.130
## 361 ARDR 5 NA 11.2 NA 0.00682
## 362 MXC 5 NA 263. NA 0.0329
## 363 QTUM 5 NA 0.615 NA 0.782
## 364 IOTA 5 NA 0.628 NA 0.0245
## 365 RIF 5 NA 6.80 NA 0.0742
## 366 ATOM 5 NA 0.899 NA 0.00386
## 367 KLAY 5 NA 0.489 NA 0.141
## 368 LAZIO 5 NA 0.339 NA 0.539
## 369 BTC 5 NA 0.456 NA 0.257
## 370 DYDX 5 NA 1.36 NA 0.155
## 371 TOWN 5 NA 1.13 NA 0.435
## 372 LSK 5 NA 0.588 NA 0.201
## 373 FEI 5 NA 59.7 NA 0.000863
## 374 ETHW 5 NA 0.791 NA 0.0574
## 375 PERP 5 NA 1.07 NA 0.140
## 376 MTLX 5 NA 0.870 NA 0.832
## 377 APT 5 NA 1.35 NA 0.0000839
## 378 BRD 5 NA 3.28 NA 0.868
## 379 SKL 5 NA 0.749 NA 0.673
## 380 GMTT 5 NA 5.52 NA 0.00523
## 381 TOMO 5 NA 1.20 NA 0.406
## 382 XRD 5 NA 234. NA 0.0180
## 383 INJ 5 NA 1.40 NA 0.000992
## 384 ARNM 5 NA 1.08 NA 0.661
## 385 AGIX 5 NA 2.70 NA 0.0188
Out of 385 groups, 176 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:
## # A tibble: 385 x 6
## # Groups: symbol, split [385]
## symbol split lm_rmse_test lm_rsq_test lm_rmse_holdout lm_rsq_holdout
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 BTC 1 0.129 0.854 0.456 0.257
## 2 ETH 1 0.330 0.598 0.644 0.0837
## 3 EOS 1 0.823 0.168 0.732 0.387
## 4 LTC 1 0.749 0.563 0.900 0.337
## 5 BSV 1 2.07 0.0520 1.86 0.00519
## 6 ADA 1 0.402 0.722 0.978 0.00427
## 7 ZEC 1 0.540 0.494 0.860 0.234
## 8 HT 1 0.155 0.426 8.19 0.637
## 9 TRX 1 0.860 0.0659 0.699 0.212
## 10 XMR 1 1.24 0.111 0.825 0.0455
## # ... with 375 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:
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: 385 x 3
## # Groups: symbol [77]
## symbol model rmse
## <chr> <chr> <dbl>
## 1 BTC lm 0.558
## 2 ETH lm 0.501
## 3 EOS lm 1.02
## 4 LTC lm 1.14
## 5 BSV lm 1.10
## 6 ADA lm 0.634
## 7 ZEC lm 0.754
## 8 HT lm 1.52
## 9 TRX lm 1.03
## 10 XMR lm 0.913
## # ... with 375 more rows
Now tag the results as having been for the test set:
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: 385 x 3
## # Groups: symbol [77]
## symbol model rmse
## <chr> <chr> <dbl>
## 1 BTC lm 0.456
## 2 ETH lm 0.644
## 3 EOS lm 0.732
## 4 LTC lm 0.900
## 5 BSV lm 1.86
## 6 ADA lm 0.978
## 7 ZEC lm 0.860
## 8 HT lm 8.19
## 9 TRX lm 0.699
## 10 XMR lm 0.825
## # ... with 375 more rows
Now tag the results as having been for the holdout set:
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: 385 x 3
## # Groups: symbol [77]
## symbol model rsq
## <chr> <chr> <dbl>
## 1 BTC lm 0.731
## 2 ETH lm 0.701
## 3 EOS lm 0.403
## 4 LTC lm 0.502
## 5 BSV lm 0.271
## 6 ADA lm 0.491
## 7 ZEC lm 0.560
## 8 HT lm 0.256
## 9 TRX lm 0.217
## 10 XMR lm 0.162
## # ... with 375 more rows
Now tag the results as having been for the test set
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: 385 x 3
## # Groups: symbol [77]
## symbol model rsq
## <chr> <chr> <dbl>
## 1 BTC lm 0.257
## 2 ETH lm 0.0837
## 3 EOS lm 0.387
## 4 LTC lm 0.337
## 5 BSV lm 0.00519
## 6 ADA lm 0.00427
## 7 ZEC lm 0.234
## 8 HT lm 0.637
## 9 TRX lm 0.212
## 10 XMR lm 0.0455
## # ... with 375 more rows
Now tag the results as having been for the holdout set:
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.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:
8.7.2.2 Plot Results
Now we can plot the results on a chart:
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):
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).
8.9 Visualizations - Historical Metrics
We can pull the same data into this R session using the pin_get()
function:
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.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.