主要議題:預測股票的投資報酬
學習重點:
rm(list=ls(all=T))
Sys.setlocale("LC_ALL","C")
[1] "C"
options(digits=6, scipen=12)
library(dplyr)
library(caTools)
library(caret)
library(flexclust)
Load StocksCluster.csv into a data frame called “stocks”.
stocks = read.csv("data/StocksCluster.csv")
nrow(stocks)
[1] 11580
How many observations are in the dataset?
sum(stocks$PositiveDec==1) / nrow(stocks)
[1] 0.546114
What proportion of the observations have positive returns in December?
names(stocks)
[1] "ReturnJan" "ReturnFeb" "ReturnMar" "ReturnApr" "ReturnMay" "ReturnJune"
[7] "ReturnJuly" "ReturnAug" "ReturnSep" "ReturnOct" "ReturnNov" "PositiveDec"
cor(stocks[,1:11]) %>% sort
[1] -0.191351924 -0.191351924 -0.155983263 -0.155983263 -0.154658281 -0.154658281 -0.116489034
[8] -0.116489034 -0.095520920 -0.095520920 -0.090496798 -0.090496798 -0.087324267 -0.087324267
[15] -0.085905486 -0.085905486 -0.081429765 -0.081429765 -0.075594561 -0.075594561 -0.065270541
[22] -0.065270541 -0.061778509 -0.061778509 -0.058079236 -0.058079236 -0.054708909 -0.054708909
[29] -0.051756051 -0.051756051 -0.048373837 -0.048373837 -0.044411417 -0.044411417 -0.037678006
[36] -0.037678006 -0.033125658 -0.033125658 -0.029152600 -0.029152600 -0.028920972 -0.028920972
[43] -0.026437153 -0.026437153 -0.022792019 -0.022792019 -0.022635994 -0.022635994 -0.022005400
[50] -0.022005400 -0.021074539 -0.021074539 -0.019719800 -0.019719800 -0.011923758 -0.011923758
[57] -0.011027752 -0.011027752 -0.003892789 -0.003892789 0.000713756 0.000713756 0.000740714
[64] 0.000740714 0.003374160 0.003374160 0.009726288 0.009726288 0.010710526 0.010710526
[71] 0.017166728 0.017166728 0.021962862 0.021962862 0.031761837 0.031761837 0.037323535
[78] 0.037323535 0.043501771 0.043501771 0.044747269 0.044747269 0.048046590 0.048046590
[85] 0.048540025 0.048540025 0.063822504 0.063822504 0.066774583 0.066774583 0.067632333
[92] 0.067632333 0.068947804 0.068947804 0.076518327 0.076518327 0.080631932 0.080631932
[99] 0.090850264 0.090850264 0.092238307 0.092238307 0.131559786 0.131559786 0.142977229
[106] 0.142977229 0.169994483 0.169994483 0.191672786 0.191672786 1.000000000 1.000000000
[113] 1.000000000 1.000000000 1.000000000 1.000000000 1.000000000 1.000000000 1.000000000
[120] 1.000000000 1.000000000
# 0.191672786
cor(stocks[,1:11])
ReturnJan ReturnFeb ReturnMar ReturnApr ReturnMay ReturnJune ReturnJuly
ReturnJan 1.0000000 0.0667746 -0.09049680 -0.03767801 -0.04441142 0.0922383 -0.081429765
ReturnFeb 0.0667746 1.0000000 -0.15598326 -0.19135192 -0.09552092 0.1699945 -0.061778509
ReturnMar -0.0904968 -0.1559833 1.00000000 0.00972629 -0.00389279 -0.0859055 0.003374160
ReturnApr -0.0376780 -0.1913519 0.00972629 1.00000000 0.06382250 -0.0110278 0.080631932
ReturnMay -0.0444114 -0.0955209 -0.00389279 0.06382250 1.00000000 -0.0210745 0.090850264
ReturnJune 0.0922383 0.1699945 -0.08590549 -0.01102775 -0.02107454 1.0000000 -0.029152600
ReturnJuly -0.0814298 -0.0617785 0.00337416 0.08063193 0.09085026 -0.0291526 1.000000000
ReturnAug -0.0227920 0.1315598 -0.02200540 -0.05175605 -0.03312566 0.0107105 0.000713756
ReturnSep -0.0264372 0.0435018 0.07651833 -0.02892097 0.02196286 0.0447473 0.068947804
ReturnOct 0.1429772 -0.0873243 -0.01192376 0.04854003 0.01716673 -0.0226360 -0.054708909
ReturnNov 0.0676323 -0.1546583 0.03732353 0.03176184 0.04804659 -0.0652705 -0.048373837
ReturnAug ReturnSep ReturnOct ReturnNov
ReturnJan -0.022792019 -0.026437153 0.1429772 0.0676323
ReturnFeb 0.131559786 0.043501771 -0.0873243 -0.1546583
ReturnMar -0.022005400 0.076518327 -0.0119238 0.0373235
ReturnApr -0.051756051 -0.028920972 0.0485400 0.0317618
ReturnMay -0.033125658 0.021962862 0.0171667 0.0480466
ReturnJune 0.010710526 0.044747269 -0.0226360 -0.0652705
ReturnJuly 0.000713756 0.068947804 -0.0547089 -0.0483738
ReturnAug 1.000000000 0.000740714 -0.0755946 -0.1164890
ReturnSep 0.000740714 1.000000000 -0.0580792 -0.0197198
ReturnOct -0.075594561 -0.058079236 1.0000000 0.1916728
ReturnNov -0.116489034 -0.019719800 0.1916728 1.0000000
What is the maximum correlation between any two return variables in the dataset? You should look at the pairwise correlations between ReturnJan, ReturnFeb, ReturnMar, ReturnApr, ReturnMay, ReturnJune, ReturnJuly, ReturnAug, ReturnSep, ReturnOct, and ReturnNov.
colMeans(stocks[,1:11]) %>% sort()
ReturnSep ReturnFeb ReturnJuly ReturnOct ReturnJune ReturnNov ReturnJan ReturnAug
-0.01472077 -0.00760478 0.00305086 0.00565084 0.00593790 0.01138744 0.01263160 0.01619826
ReturnMar ReturnMay ReturnApr
0.01940234 0.02473659 0.02630815
Which month (from January through November) has the largest mean return across all observations in the dataset?
Which month (from January through November) has the smallest mean return across all observations in the dataset?
Run the following commands to split the data into a training set and testing set, putting 70% of the data in the training set and 30% of the data in the testing set:
set.seed(144)
spl = sample.split(stocks$PositiveDec, SplitRatio = 0.7)
stocksTrain = subset(stocks, spl == TRUE)
stocksTest = subset(stocks, spl == FALSE)
set.seed(144)
spl = sample.split(stocks$PositiveDec, SplitRatio = 0.7)
stocksTrain = subset(stocks, spl == TRUE)
stocksTest = subset(stocks, spl == FALSE)
StocksModel = glm(PositiveDec ~.,data=stocksTrain, family=binomial)
Then, use the stocksTrain data frame to train a logistic regression model (name it StocksModel) to predict PositiveDec using all the other variables as independent variables. Don’t forget to add the argument family=binomial to your glm command.
PredictTrain = predict(StocksModel, type="response")
A = table(stocksTrain$PositiveDec, PredictTrain > 0.5)
sum(diag(A))/sum(A)
[1] 0.571182
What is the overall accuracy on the training set, using a threshold of 0.5?
PredictTest = predict(StocksModel, newdata=stocksTest ,type="response")
B = table(stocksTest$PositiveDec, PredictTest > 0.5)
sum(diag(B))/sum(B)
[1] 0.56707
Now obtain test set predictions from StocksModel. What is the overall accuracy of the model on the test, again using a threshold of 0.5?
table(stocksTest$PositiveDec)
0 1
1577 1897
C = table(stocksTest$PositiveDec, stocksTest$PositiveDec==1)
1897/nrow(stocksTest)
[1] 0.546056
What is the accuracy on the test set of a baseline model that always predicts the most common outcome (PositiveDec = 1)?
Now, let’s cluster the stocks. The first step in this process is to remove the dependent variable using the following commands:
limitedTrain = stocksTrain
limitedTrain$PositiveDec = NULL
limitedTest = stocksTest
limitedTest$PositiveDec = NULL
Why do we need to remove the dependent variable in the clustering phase of the cluster-then-predict methodology?
In the market segmentation assignment in this week’s homework, you were introduced to the preProcess command from the caret package, which normalizes variables by subtracting by the mean and dividing by the standard deviation.
In cases where we have a training and testing set, we’ll want to normalize by the mean and standard deviation of the variables in the training set. We can do this by passing just the training set to the preProcess function:
library(caret)
preproc = preProcess(limitedTrain)
normTrain = predict(preproc, limitedTrain)
normTest = predict(preproc, limitedTest)
mean(normTrain$ReturnJan)
[1] 2.10059e-17
What is the mean of the ReturnJan variable in normTrain?
mean(normTest$ReturnJan)
[1] -0.000418589
What is the mean of the ReturnJan variable in normTest?
Why is the mean ReturnJan variable much closer to 0 in normTrain than in normTest?
Set the random seed to 144 (it is important to do this again, even though we did it earlier). Run k-means clustering with 3 clusters on normTrain, storing the result in an object called km.
set.seed(144)
km = kmeans(normTrain,centers = 3 )
table(km$cluster)
1 2 3
3157 4696 253
Which cluster has the largest number of observations?
Recall from the recitation that we can use the flexclust package to obtain training set and testing set cluster assignments for our observations (note that the call to as.kcca may take a while to complete):
km.kcca = flexclust::as.kcca(km, normTrain)
clusterTrain = predict(km.kcca)
clusterTest = predict(km.kcca, newdata=normTest)
table(clusterTest)
clusterTest
1 2 3
1298 2080 96
How many test-set observations were assigned to Cluster 2?
Using the subset function, build data frames stocksTrain1, stocksTrain2, and stocksTrain3, containing the elements in the stocksTrain data frame assigned to clusters 1, 2, and 3, respectively (be careful to take subsets of stocksTrain, not of normTrain). Similarly build stocksTest1, stocksTest2, and stocksTest3 from the stocksTest data frame.
stocksTrain1 = subset(stocksTrain,clusterTrain==1)
stocksTrain2 = subset(stocksTrain,clusterTrain==2)
stocksTrain3 = subset(stocksTrain,clusterTrain==3)
stocksTest1 = subset(stocksTest,clusterTest==1)
stocksTest2 = subset(stocksTest,clusterTest==2)
stocksTest3 = subset(stocksTest,clusterTest==3)
tapply(stocksTrain$PositiveDec, clusterTrain, mean)
1 2 3
0.602471 0.514055 0.438735
Which training set data frame has the highest average value of the dependent variable?
Build logistic regression models StocksModel1, StocksModel2, and StocksModel3, which predict PositiveDec using all the other variables as independent variables. StocksModel1 should be trained on stocksTrain1, StocksModel2 should be trained on stocksTrain2, and StocksModel3 should be trained on stocksTrain3.
StocksModel1 = glm(PositiveDec~., data = stocksTrain1,family = binomial)
StocksModel = lapply(split(stocksTrain,clusterTrain),function(x)
glm(PositiveDec~.,data=x,family = binomial))
sapply(StocksModel,coef)
1 2 3
(Intercept) 0.1722399 0.102932 -0.18189581
ReturnJan 0.0249836 0.884515 -0.00978934
ReturnFeb -0.3720737 0.317622 -0.04688326
ReturnMar 0.5955496 -0.379781 0.67417950
ReturnApr 1.1904775 0.492910 1.28146619
ReturnMay 0.3042091 0.896549 0.76251155
ReturnJune -0.0116538 1.500879 0.32943392
ReturnJuly 0.1976923 0.783149 0.77416437
ReturnAug 0.5127294 -0.244860 0.98260539
ReturnSep 0.5883269 0.736852 0.36380682
ReturnOct -1.0225351 -0.277563 0.78224209
ReturnNov -0.7484719 -0.787474 -0.87375214
Which variables have a positive sign for the coefficient in at least one model and a negative sign for the coefficient in at least one model? Select all that apply.
Using StocksModel1, make test-set predictions called PredictTest1 on the data frame stocksTest1. Using StocksModel2, make test-set predictions called PredictTest2 on the data frame stocksTest2. Using StocksModel3, make test-set predictions called PredictTest3 on the data frame stocksTest3.
StocksTestModel = lapply(split(stocksTest,clusterTest),function(x)
glm(PositiveDec~.,data=x,family = binomial))
Pred = lapply(1:3, function(i)
predict(StocksTestModel[[i]],stocksTest[clusterTest==i,],type="response"))
sapply(1:3, function(i)
table(stocksTest$PositiveDec[clusterTest==i], Pred[[i]] > 0.5) %>% {sum(diag(.))/sum(.)} )
[1] 0.618644 0.545192 0.635417
What is the overall accuracy of StocksModel1 on the test set stocksTest1, using a threshold of 0.5?
What is the overall accuracy of StocksModel2 on the test set stocksTest3, using a threshold of 0.5?
What is the overall accuracy of StocksModel3 on the test set stocksTest3, using a threshold of 0.5?
To compute the overall test-set accuracy of the cluster-then-predict approach, we can combine all the test-set predictions into a single vector and all the true outcomes into a single vector:
table( do.call(c, split(stocksTest$PositiveDec,clusterTest)), do.call(c, Pred) > 0.5 ) %>%
{sum(diag(.))/sum(.)}
[1] 0.57513
What is the overall test-set accuracy of the cluster-then-predict approach, again using a threshold of 0.5?
We see a modest improvement over the original logistic regression model. Since predicting stock returns is a notoriously hard problem, this is a good increase in accuracy. By investing in stocks for which we are more confident that they will have positive returns (by selecting the ones with higher predicted probabilities), this cluster-then-predict model can give us an edge over the original logistic regression model.