library(knitr)
library(MASS)
dates <- c("2015-01-30","2015-02-06","2015-02-13","2015-02-20","2015-02-27","2015-03-06")
date.1 <- tail(dates,1)
forecast.matches <- read.csv(paste("forecasts_",date.1,".csv",sep=""),stringsAsFactors=F)
forecast.matches <- forecast.matches[is.na(forecast.matches$outcome)==F,]
This is the number 6 in a series of forecasts of football match outcomes, following on from my efforts last week and the previous weeks. The method of forecasts is unchanged from previous week. For next week I hope to add bookmaker prices in for reference purposes.
As with previous weeks, the dataset is all English matches recorded on http://www.soccerbase.com, which goes back to 1877 and the very first football matches.  Experimentation will take place with adjusting the estimation sample size, since it is not necessarily useful to have all matches back to 1877 when forecasting matches in 2015.  The Elo ranks have been calculated since the very first matches, and hence historical information is retained, to the extent that it is useful in determining a team’s current strength, back throughout footballing history.
The linear regression model is estimated here and reported:
res.eng <- read.csv(paste("historical_",date.1,".csv",sep=""))
model <- lm(outcome ~ E.1 + pts1 + pts.D + pts.D.2 + pld1 + pld.D + pld.D.2 + gs1 + gs.D + gs.D.2
+ gd1 + gd.D + gd.D.2
+ pos1 + pos.D + pos.D.2 + form1 + form.D + form.D.2 + tier1 + tier.D + tier.D.2 + season.d,
data=res.eng)
summary(model)
##
## Call:
## lm(formula = outcome ~ E.1 + pts1 + pts.D + pts.D.2 + pld1 +
## pld.D + pld.D.2 + gs1 + gs.D + gs.D.2 + gd1 + gd.D + gd.D.2 +
## pos1 + pos.D + pos.D.2 + form1 + form.D + form.D.2 + tier1 +
## tier.D + tier.D.2 + season.d, data = res.eng)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.0269 -0.3112 0.1153 0.3369 0.8964
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 3.310e-01 5.418e-03 61.093 < 2e-16 ***
## E.1 5.448e-01 6.053e-03 90.009 < 2e-16 ***
## pts1 9.700e-04 4.239e-04 2.288 0.02212 *
## pts.D -2.371e-03 3.077e-04 -7.704 1.32e-14 ***
## pts.D.2 -1.342e-05 6.471e-06 -2.074 0.03806 *
## pld1 -1.628e-03 6.094e-04 -2.671 0.00756 **
## pld.D 2.229e-03 7.108e-04 3.136 0.00171 **
## pld.D.2 -4.029e-05 3.080e-05 -1.308 0.19088
## gs1 4.764e-04 1.710e-04 2.786 0.00533 **
## gs.D -2.492e-04 1.526e-04 -1.633 0.10247
## gs.D.2 -6.539e-07 4.690e-06 -0.139 0.88910
## gd1 -5.913e-04 2.407e-04 -2.457 0.01401 *
## gd.D 2.451e-03 1.759e-04 13.934 < 2e-16 ***
## gd.D.2 -5.691e-06 2.343e-06 -2.429 0.01513 *
## pos1 7.637e-04 3.005e-04 2.541 0.01105 *
## pos.D -1.841e-03 2.514e-04 -7.324 2.42e-13 ***
## pos.D.2 3.745e-05 1.171e-05 3.199 0.00138 **
## form1 6.144e-04 3.519e-04 1.746 0.08079 .
## form.D 1.360e-03 2.960e-04 4.593 4.38e-06 ***
## form.D.2 -7.265e-05 2.996e-05 -2.425 0.01532 *
## tier1 1.946e-03 7.655e-04 2.542 0.01101 *
## tier.D -3.169e-02 2.862e-03 -11.074 < 2e-16 ***
## tier.D.2 -5.274e-03 1.258e-03 -4.194 2.75e-05 ***
## season.d -1.081e-03 3.082e-05 -35.062 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.3948 on 216015 degrees of freedom
## (38216 observations deleted due to missingness)
## Multiple R-squared: 0.08512, Adjusted R-squared: 0.08503
## F-statistic: 873.9 on 23 and 216015 DF, p-value: < 2.2e-16
The ordered logistic regression model is:
model.ord <- polr(as.factor(outcome) ~ E.1 + pts1 + pts.D + pts.D.2 + pld1 + pld.D + pld.D.2 +
gs1 + gs.D + gs.D.2 + gd1 + gd.D + gd.D.2 + pos1 + pos.D + pos.D.2 +
form1 + form.D + form.D.2 + tier1 + tier.D + tier.D.2 + season.d,
data=res.eng, method = "logistic")
summary(model.ord)
##
## Re-fitting to get Hessian
## Call:
## polr(formula = as.factor(outcome) ~ E.1 + pts1 + pts.D + pts.D.2 +
## pld1 + pld.D + pld.D.2 + gs1 + gs.D + gs.D.2 + gd1 + gd.D +
## gd.D.2 + pos1 + pos.D + pos.D.2 + form1 + form.D + form.D.2 +
## tier1 + tier.D + tier.D.2 + season.d, data = res.eng, method = "logistic")
##
## Coefficients:
## Value Std. Error t value
## E.1 2.636e+00 3.019e-02 87.32128
## pts1 4.169e-03 2.084e-03 2.00048
## pts.D -1.221e-02 1.522e-03 -8.02570
## pts.D.2 -7.482e-05 3.611e-05 -2.07207
## pld1 -9.114e-03 2.997e-03 -3.04071
## pld.D 1.268e-02 3.567e-03 3.55496
## pld.D.2 -2.313e-04 1.637e-04 -1.41305
## gs1 3.434e-03 8.512e-04 4.03401
## gs.D -1.586e-03 7.583e-04 -2.09125
## gs.D.2 -8.177e-07 2.557e-05 -0.03198
## gd1 -3.311e-03 1.193e-03 -2.77448
## gd.D 1.331e-02 8.795e-04 15.13210
## gd.D.2 7.265e-06 1.638e-05 0.44352
## pos1 3.048e-03 1.466e-03 2.07952
## pos.D -7.972e-03 1.240e-03 -6.42996
## pos.D.2 2.026e-04 5.945e-05 3.40719
## form1 2.794e-03 1.719e-03 1.62535
## form.D 7.078e-03 1.457e-03 4.85787
## form.D.2 -2.277e-04 1.495e-04 -1.52350
## tier1 9.442e-03 3.728e-03 2.53247
## tier.D -1.744e-01 1.506e-02 -11.58488
## tier.D.2 -6.727e-03 6.882e-03 -0.97761
## season.d -5.401e-03 1.527e-04 -35.37495
##
## Intercepts:
## Value Std. Error t value
## 0|0.5 0.2401 0.0264 9.0975
## 0.5|1 1.4290 0.0266 53.7856
##
## Residual Deviance: 428058.78
## AIC: 428108.78
## (38216 observations deleted due to missingness)
fac.matches <- forecast.matches[forecast.matches$division=="English FA Cup",]
fac.matches <- fac.matches[order(fac.matches$date),]
fac.matches$id <- 1:NROW(fac.matches)
par(mar=c(9,4,4,5)+.1)
plot(fac.matches$id,fac.matches$outcome,xaxt="n",xlab="",ylim=range(0,1),
main="Forecasts of Weekend facier League Matches",
ylab="Probability of Outcome")
lines(fac.matches$id,fac.matches$Ph,col=2,pch=15,type="p")
lines(fac.matches$id,fac.matches$Pd,col=3,pch=16,type="p")
lines(fac.matches$id,fac.matches$Pa,col=4,pch=17,type="p")
legend("topleft",ncol=4,pch=c(1,15,16,17),col=c(1:4),
legend=c("OLS","OL (home)","OL (draw)","OL (away)"),bty="n")
abline(h=0.5,lty=2)
abline(h=0.6,lty=3)
abline(h=0.7,lty=2)
abline(h=0.4,lty=3)
axis(1,at=fac.matches$id,labels=paste(fac.matches$team1,fac.matches$team2,sep=" v "),las=2,cex.axis=0.65)
for(i in 2:NROW(fac.matches)){
if(fac.matches$date[i]!=fac.matches$date[i-1]) {
lines(rep(c(i-0.5),2),c(0,1),lty=2)
}
}
Bookmaker prices return for our FA Cup matches:
clean.data <- function(data) {
require(zoo)
colnames(data)[1] <- "Date.Time"
colnames(data)[-1] <- gsub("^.*?\\d+_(\\w+)[.]href.*?$","\\1",colnames(data)[-1])
data <- data[,-NCOL(data)]
data$Date.Time <- as.Date(substring(data$Date.Time,2),"%Y-%m-%d")
data <- data[is.na(data$Date.Time)==F,]
data <- data[order(data$Date.Time),]
for(i in colnames(data)[nchar(colnames(data))==2]) {
data[,i] <- gsub("<div class=.*?>(.*?)</div>","\\1",data[,i])
data[,i] <- gsub("<div class=.*?>(.*?)\\s+$","\\1",data[,i])
data[,i] <- gsub(">","",data[,i])
data[,i] <- gsub("^\\s+|\\s+$","",data[,i])
#need to turn fractional odds into decimal odds
numerator <- gsub("(\\d+)/(\\d+)$","\\1",data[,i])
denominator <- gsub("(\\d+)/(\\d+)$","\\2",data[,i])
data[,i] <- as.numeric(numerator)+1
data[numerator!=denominator,i] <- as.numeric(numerator[numerator!=denominator])/as.numeric(denominator[numerator!=denominator])+1
data[,i] <- na.locf(data[,i],na.rm=F)
}
return(data)
}
fac.bk <- data.frame()
fac.loc <- paste("/home/readejj/Dropbox/Research/Data for Ideas/Betting/football/",date.1,"/fa-cup",sep="")
fac.bk.matches <- dir(fac.loc,pattern="*.csv")
for(i in fac.bk.matches) {
temp <- read.csv(paste(fac.loc,i,sep="/"),stringsAsFactors=F)
temp <- clean.data(temp)
temp$event <- gsub("[.]csv","",i)
fac.bk <- rbind(fac.bk,temp)
}
## Loading required package: zoo
##
## Attaching package: 'zoo'
##
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
## Warning: NAs introduced by coercion
fac.bk <- fac.bk[order(fac.bk$event,fac.bk$Date.Time),]
fac.bk$mean <- rowMeans(fac.bk[colnames(fac.bk)[nchar(colnames(fac.bk))==2]],na.rm=T)
fac.bk$event <- gsub("man-utd","man utd",fac.bk$event)
fac.bk$event <- gsub("man-city","man city",fac.bk$event)
fac.bk$event <- gsub("aston-villa","aston villa",fac.bk$event)
fac.bk$event <- gsub("west-brom","west brom",fac.bk$event)
fac.bk$event <- gsub("west-ham","west ham",fac.bk$event)
fac.bk$event <- gsub("crystal-palace","c palace",fac.bk$event)
fac.bk$team1 <- gsub("^(.*?)-v-(.*?)-(.*?)$","\\1",fac.bk$event)
fac.bk$team2 <- gsub("^(.*?)-v-(.*?)-(.*?)$","\\2",fac.bk$event)
fac.bk$event <- gsub("^(.*?)-v-(.*?)-(.*?)$","\\3",fac.bk$event)
fac.bk$event.1 <- c(fac.bk$event[-1],NA)
fac.bk.h <- fac.bk[(fac.bk$event!=fac.bk$event.1 | is.na(fac.bk$event.1)==T) & fac.bk$event==fac.bk$team1,]
fac.bk.d <- fac.bk[fac.bk$event!=fac.bk$event.1 & regexpr("draw",fac.bk$event)>-1,]
fac.bk.a <- fac.bk[fac.bk$event!=fac.bk$event.1 & fac.bk$event==fac.bk$team2,]
fac.matches$team1 <- tolower(fac.matches$team1)
fac.matches$team2 <- tolower(fac.matches$team2)
fac.matches <- merge(fac.matches,fac.bk.h[,c("team1","team2","mean")],by=c("team1","team2"),all.x=T)
colnames(fac.matches) <- gsub("mean","bk.h",colnames(fac.matches))
fac.matches <- merge(fac.matches,fac.bk.d[,c("team1","team2","mean")],by=c("team1","team2"),all.x=T)
colnames(fac.matches) <- gsub("mean","bk.d",colnames(fac.matches))
fac.matches <- merge(fac.matches,fac.bk.a[,c("team1","team2","mean")],by=c("team1","team2"),all.x=T)
colnames(fac.matches) <- gsub("mean","bk.a",colnames(fac.matches))
par(mar=c(9,4,4,5)+.1)
plot(fac.matches$id,fac.matches$Ph,xaxt="n",xlab="",ylim=range(0,1),col=2,pch=15,type="p",
main="Forecasts of Weekend facier League Matches",
ylab="Probability of Outcome")
lines(fac.matches$id,1/fac.matches$bk.h,col=2,pch=0,type="p")
lines(fac.matches$id,fac.matches$Pd,col=3,pch=16,type="p")
lines(fac.matches$id,1/fac.matches$bk.d,col=3,pch=1,type="p")
lines(fac.matches$id,fac.matches$Pa,col=4,pch=17,type="p")
lines(fac.matches$id,1/fac.matches$bk.a,col=4,pch=2,type="p")
legend("topleft",ncol=4,pch=c(1,15,16,17),col=c(1:4),
legend=c("OLS","OL (home)","OL (draw)","OL (away)"),bty="n")
abline(h=0.5,lty=2)
abline(h=0.6,lty=3)
abline(h=0.7,lty=2)
abline(h=0.4,lty=3)
axis(1,at=fac.matches$id,labels=paste(fac.matches$team1,fac.matches$team2,sep=" v "),las=2,cex.axis=0.65)
They suggest little difference between my model forecasts and average bookmaker prices.
prem.matches <- forecast.matches[forecast.matches$division=="English Premier",]
prem.matches <- prem.matches[order(prem.matches$date),]
prem.matches$id <- 1:NROW(prem.matches)
par(mar=c(9,4,4,5)+.1)
plot(prem.matches$id,prem.matches$outcome,xaxt="n",xlab="",ylim=range(0,1),
main="Forecasts of Weekend Premier League Matches",
ylab="Probability of Outcome")
lines(prem.matches$id,prem.matches$Ph,col=2,pch=15,type="p")
lines(prem.matches$id,prem.matches$Pd,col=3,pch=16,type="p")
lines(prem.matches$id,prem.matches$Pa,col=4,pch=17,type="p")
legend("topleft",ncol=4,pch=c(1,15,16,17),col=c(1:4),
legend=c("OLS","OL (home)","OL (draw)","OL (away)"),bty="n")
abline(h=0.5,lty=2)
abline(h=0.6,lty=3)
abline(h=0.7,lty=2)
abline(h=0.4,lty=3)
axis(1,at=prem.matches$id,labels=paste(prem.matches$team1,prem.matches$team2,sep=" v "),las=2,cex.axis=0.65)
if(NROW(prem.matches)>1) {
for(i in 2:NROW(prem.matches)){
if(prem.matches$date[i]!=prem.matches$date[i-1]) {
lines(rep(c(i-0.5),2),c(0,1),lty=2)
}
}
}
The coloured dots are forecasts from the ordered logistic regression model; the black circles are the forecasts from a simple OLS linear probability model. Â Hence the black circles are essentially a probability of a home win occurring (given the ordinal variable defined to capture all three outcomes), whereas the red squares are the probability of a home win, the green solid circles are the probability of a draw, and the blue triangles the probability of an away win. Â The home bias in football is notable in that the majority of red squares lie above blue triangles.
Unlike previous weeks, I am unable to provide bookmaker odds. Hopefully this will return next week.
Next, our Championship forecasts:
champ.matches <- forecast.matches[forecast.matches$division=="English Championship",]
champ.matches <- champ.matches[order(champ.matches$date),]
champ.matches$id <- 1:NROW(champ.matches)
par(mar=c(9,4,4,5)+.1)
plot(champ.matches$id,champ.matches$outcome,xaxt="n",xlab="",ylim=range(0,1),
main="Forecasts of Weekend Championship Matches",
ylab="Probability of Outcome")
lines(champ.matches$id,champ.matches$Ph,col=2,pch=15,type="p")
lines(champ.matches$id,champ.matches$Pd,col=3,pch=16,type="p")
lines(champ.matches$id,champ.matches$Pa,col=4,pch=17,type="p")
legend("topleft",ncol=4,pch=c(1,15,16,17),col=c(1:4),
legend=c("OLS","OL (home)","OL (draw)","OL (away)"),bty="n")
abline(h=0.5,lty=2)
abline(h=0.6,lty=3)
abline(h=0.7,lty=2)
axis(1,at=champ.matches$id,labels=paste(champ.matches$team1,champ.matches$team2,sep=" v "),las=2,cex.axis=0.65)
for(i in 2:NROW(champ.matches)){
if(champ.matches$date[i]!=champ.matches$date[i-1]) {
lines(rep(c(i-0.5),2),c(0,1),lty=2)
}
}
Next, our League One forecasts:
lg1.matches <- forecast.matches[forecast.matches$division=="English League One",]
lg1.matches <- lg1.matches[order(lg1.matches$date),]
lg1.matches$id <- 1:NROW(lg1.matches)
par(mar=c(9,4,4,5)+.1)
plot(lg1.matches$id,lg1.matches$outcome,xaxt="n",xlab="",ylim=range(0,1),
main="Forecasts of Weekend League One Matches",
ylab="Probability of Outcome")
lines(lg1.matches$id,lg1.matches$Ph,col=2,pch=15,type="p")
lines(lg1.matches$id,lg1.matches$Pd,col=3,pch=16,type="p")
lines(lg1.matches$id,lg1.matches$Pa,col=4,pch=17,type="p")
legend("topleft",ncol=4,pch=c(1,15,16,17),col=c(1:4),
legend=c("OLS","OL (home)","OL (draw)","OL (away)"),bty="n")
abline(h=0.5,lty=2)
abline(h=0.6,lty=3)
abline(h=0.7,lty=2)
axis(1,at=lg1.matches$id,labels=paste(lg1.matches$team1,lg1.matches$team2,sep=" v "),las=2,cex.axis=0.65)
for(i in 2:NROW(lg1.matches)){
if(lg1.matches$date[i]!=lg1.matches$date[i-1]) {
lines(rep(c(i-0.5),2),c(0,1),lty=2)
}
}
Next, our League Two forecasts:
lg2.matches <- forecast.matches[forecast.matches$division=="English League Two",]
lg2.matches <- lg2.matches[order(lg2.matches$date),]
lg2.matches$id <- 1:NROW(lg2.matches)
par(mar=c(9,4,4,5)+.1)
plot(lg2.matches$id,lg2.matches$outcome,xaxt="n",xlab="",ylim=range(0,1),
main="Forecasts of Weekend League Two Matches",
ylab="Probability of Outcome")
lines(lg2.matches$id,lg2.matches$Ph,col=2,pch=15,type="p")
lines(lg2.matches$id,lg2.matches$Pd,col=3,pch=16,type="p")
lines(lg2.matches$id,lg2.matches$Pa,col=4,pch=17,type="p")
legend("topleft",ncol=4,pch=c(1,15,16,17),col=c(1:4),
legend=c("OLS","OL (home)","OL (draw)","OL (away)"),bty="n")
abline(h=0.5,lty=2)
abline(h=0.6,lty=3)
abline(h=0.7,lty=2)
axis(1,at=lg2.matches$id,labels=paste(lg2.matches$team1,lg2.matches$team2,sep=" v "),las=2,cex.axis=0.65)
for(i in 2:NROW(lg2.matches)){
if(lg2.matches$date[i]!=lg2.matches$date[i-1]) {
lines(rep(c(i-0.5),2),c(0,1),lty=2)
}
}
Next, our Football Conference forecasts:
conf.matches <- forecast.matches[forecast.matches$division=="Football Conference",]
conf.matches$id <- 1:NROW(conf.matches)
par(mar=c(9,4,4,5)+.1)
plot(conf.matches$id,conf.matches$outcome,xaxt="n",xlab="",ylim=range(0,1),
main="Forecasts of Weekend Football Conference Matches",
ylab="Probability of Outcome")
lines(conf.matches$id,conf.matches$Ph,col=2,pch=15,type="p")
lines(conf.matches$id,conf.matches$Pd,col=3,pch=16,type="p")
lines(conf.matches$id,conf.matches$Pa,col=4,pch=17,type="p")
legend("topleft",ncol=4,pch=c(1,15,16,17),col=c(1:4),
legend=c("OLS","OL (home)","OL (draw)","OL (away)"),bty="n")
abline(h=0.5,lty=2)
abline(h=0.6,lty=3)
abline(h=0.7,lty=2)
axis(1,at=conf.matches$id,labels=paste(conf.matches$team1,conf.matches$team2,sep=" v "),las=2,cex.axis=0.65)
for(i in 2:NROW(conf.matches)){
if(conf.matches$date[i]!=conf.matches$date[i-1]) {
lines(rep(c(i-0.5),2),c(0,1),lty=2)
}
}
For transparency, all forecasts are also listed as a table:
kable(forecast.matches[order(forecast.matches$date,forecast.matches$division),
c("date","division","team1","outcome","team2","Ph","Pd","Pa")])
| date | division | team1 | outcome | team2 | Ph | Pd | Pa | |
|---|---|---|---|---|---|---|---|---|
| 1 | 2015-03-06 | English Championship | Fulham | 0.3823206 | Bournemouth | 0.2370610 | 0.2679382 | 0.4950009 |
| 59 | 2015-03-07 | Conference North | Hednesford | 0.6071943 | Boston Utd | 0.4699708 | 0.2743591 | 0.2556701 |
| 62 | 2015-03-07 | Conference North | Stalybridge | 0.4845479 | Barrow | 0.3321200 | 0.2880443 | 0.3798356 |
| 66 | 2015-03-07 | Conference North | Tamworth | 0.7304738 | Bradford PA | 0.6213436 | 0.2221048 | 0.1565516 |
| 46 | 2015-03-07 | Conference South | Sutton Utd | 0.6969378 | Hayes & Y | 0.5777477 | 0.2401833 | 0.1820690 |
| 3 | 2015-03-07 | English Championship | Cardiff | 0.5181393 | Charlton | 0.3643979 | 0.2886654 | 0.3469367 |
| 4 | 2015-03-07 | English Championship | Derby | 0.5760513 | Birmingham | 0.4554792 | 0.2776000 | 0.2669208 |
| 5 | 2015-03-07 | English Championship | Nottm Forest | 0.4360652 | Middlesbro | 0.2810444 | 0.2810265 | 0.4379291 |
| 6 | 2015-03-07 | English Championship | Huddersfield | 0.5403995 | Rotherham | 0.3896455 | 0.2873625 | 0.3229921 |
| 7 | 2015-03-07 | English Championship | Wigan | 0.5468318 | Leeds | 0.3976625 | 0.2866461 | 0.3156914 |
| 8 | 2015-03-07 | English Championship | Ipswich | 0.5851810 | Brentford | 0.4453436 | 0.2796474 | 0.2750090 |
| 9 | 2015-03-07 | English Championship | Millwall | 0.2957922 | Norwich | 0.1754919 | 0.2358705 | 0.5886375 |
| 10 | 2015-03-07 | English Championship | Blackpool | 0.3629449 | Sheff Wed | 0.2182727 | 0.2600153 | 0.5217120 |
| 11 | 2015-03-07 | English Championship | Wolves | 0.4344636 | Watford | 0.2761498 | 0.2799177 | 0.4439326 |
| 101 | 2015-03-07 | English FA Cup | Aston Villa | 0.4821551 | West Brom | 0.3255087 | 0.2875739 | 0.3869174 |
| 102 | 2015-03-07 | English FA Cup | Bradford | 0.6151810 | Reading | 0.4840204 | 0.2708794 | 0.2451002 |
| 12 | 2015-03-07 | English League One | Crawley | 0.2910730 | Bristol C | 0.1713633 | 0.2330432 | 0.5955935 |
| 13 | 2015-03-07 | English League One | Yeovil | 0.4450451 | Oldham | 0.2882923 | 0.2825199 | 0.4291878 |
| 14 | 2015-03-07 | English League One | Rochdale | 0.7550132 | Colchester | 0.6541294 | 0.2071669 | 0.1387037 |
| 15 | 2015-03-07 | English League One | Crewe | 0.5070725 | Scunthorpe | 0.3506702 | 0.2887304 | 0.3605995 |
| 16 | 2015-03-07 | English League One | Peterborough | 0.7061433 | Leyton Orient | 0.5851238 | 0.2372774 | 0.1775988 |
| 17 | 2015-03-07 | English League One | Sheff Utd | 0.6863388 | Fleetwood | 0.5676428 | 0.2440578 | 0.1882994 |
| 18 | 2015-03-07 | English League One | Barnsley | 0.6291734 | Walsall | 0.4930932 | 0.2684628 | 0.2384440 |
| 19 | 2015-03-07 | English League One | Gillingham | 0.5380465 | Doncaster | 0.3892466 | 0.2873944 | 0.3233589 |
| 20 | 2015-03-07 | English League One | Swindon | 0.6997759 | Notts Co | 0.5918036 | 0.2345906 | 0.1736058 |
| 21 | 2015-03-07 | English League One | Coventry | 0.5075364 | Port Vale | 0.3541445 | 0.2887587 | 0.3570968 |
| 22 | 2015-03-07 | English League One | MK Dons | 0.3359636 | Preston | 0.1965082 | 0.2488596 | 0.5546322 |
| 23 | 2015-03-07 | English League Two | Stevenage | 0.6130424 | Newport Co | 0.4783948 | 0.2723116 | 0.2492936 |
| 24 | 2015-03-07 | English League Two | Carlisle | 0.5796086 | Exeter | 0.4385611 | 0.2809129 | 0.2805259 |
| 25 | 2015-03-07 | English League Two | Hartlepool | 0.3695381 | Burton | 0.2282773 | 0.2644217 | 0.5073011 |
| 26 | 2015-03-07 | English League Two | Tranmere | 0.4740229 | Dag & Red | 0.3168038 | 0.2867653 | 0.3964310 |
| 27 | 2015-03-07 | English League Two | Luton | 0.7236880 | Morecambe | 0.6069295 | 0.2283177 | 0.1647528 |
| 28 | 2015-03-07 | English League Two | AFC W’bledon | 0.4609074 | York | 0.3023807 | 0.2849330 | 0.4126863 |
| 29 | 2015-03-07 | English League Two | Plymouth | 0.4631433 | Northampton | 0.3043457 | 0.2852198 | 0.4104345 |
| 30 | 2015-03-07 | English League Two | Bury | 0.7484961 | Oxford | 0.6359073 | 0.2156038 | 0.1484889 |
| 31 | 2015-03-07 | English League Two | Shrewsbury | 0.7411982 | Cambridge U | 0.6302982 | 0.2181336 | 0.1515682 |
| 32 | 2015-03-07 | English League Two | Accrington | 0.5239505 | Portsmouth | 0.3689970 | 0.2885397 | 0.3424634 |
| 33 | 2015-03-07 | English League Two | Cheltenham | 0.4174368 | Mansfield | 0.2596383 | 0.2755556 | 0.4648061 |
| 34 | 2015-03-07 | English League Two | Southend | 0.5802296 | Wycombe | 0.4331992 | 0.2818526 | 0.2849481 |
| 2 | 2015-03-07 | English Premier | QPR | 0.3537208 | Tottenham | 0.2098379 | 0.2559544 | 0.5342077 |
| 35 | 2015-03-07 | Football Conference | Braintree | 0.4903936 | Gateshead | 0.3345075 | 0.2881844 | 0.3773081 |
| 36 | 2015-03-07 | Football Conference | Wrexham | 0.5369709 | Chester | 0.3876262 | 0.2875206 | 0.3248532 |
| 37 | 2015-03-07 | Football Conference | Alfreton | 0.5228892 | Kidderminster | 0.3763636 | 0.2882330 | 0.3354034 |
| 38 | 2015-03-07 | Football Conference | Barnet | 0.6129419 | Forest Green | 0.4842797 | 0.2708121 | 0.2449081 |
| 39 | 2015-03-07 | Football Conference | Southport | 0.6582698 | Nuneaton | 0.5344089 | 0.2558872 | 0.2097038 |
| 40 | 2015-03-07 | Football Conference | Halifax | 0.7309076 | Dartford | 0.6328425 | 0.2169901 | 0.1501674 |
| 41 | 2015-03-07 | Football Conference | Macclesfield | 0.7346834 | Aldershot | 0.6246752 | 0.2206371 | 0.1546877 |
| 42 | 2015-03-07 | Football Conference | Dover | 0.6542938 | Lincoln | 0.5349289 | 0.2557134 | 0.2093577 |
| 43 | 2015-03-07 | Football Conference | Bristol R | 0.6919771 | Eastleigh | 0.5690529 | 0.2435246 | 0.1874224 |
| 44 | 2015-03-07 | Football Conference | Woking | 0.5534278 | Grimsby | 0.4048360 | 0.2858873 | 0.3092767 |
| 45 | 2015-03-07 | Football Conference | Torquay | 0.7537630 | Telford | 0.6564784 | 0.2060556 | 0.1374660 |
| 88 | 2015-03-07 | Ryman Premier | Lewes | 0.4202453 | Kingstonian | 0.2669972 | 0.2776205 | 0.4553823 |
| 105 | 2015-03-08 | English FA Cup | Liverpool | 0.8290591 | Blackburn | 0.7321966 | 0.1675718 | 0.1002316 |
| 103 | 2015-03-08 | Football Conference | Welling | 0.4162036 | Altrincham | 0.2627488 | 0.2764525 | 0.4607987 |
| 109 | 2015-03-09 | English FA Cup | Man Utd | 0.5597195 | Arsenal | 0.4131189 | 0.2848767 | 0.3020044 |
| 110 | 2015-03-10 | English Championship | Reading | 0.5264255 | Brighton | 0.3694896 | 0.2885232 | 0.3419872 |
| 111 | 2015-03-10 | English League One | Coventry | 0.4280955 | Bradford | 0.2715492 | 0.2787999 | 0.4496508 |
| 112 | 2015-03-10 | English League One | Yeovil | 0.2736797 | Bristol C | 0.1600024 | 0.2247672 | 0.6152304 |
| 113 | 2015-03-10 | Football Conference | Aldershot | 0.5355632 | Halifax | 0.3870669 | 0.2875627 | 0.3253704 |
| 114 | 2015-03-10 | Football Conference | Wrexham | 0.6400000 | Southport | 0.5134136 | 0.2625897 | 0.2239967 |
| 115 | 2015-03-10 | Football Conference | Gateshead | 0.8018084 | Welling | 0.7036170 | 0.1826777 | 0.1137053 |
| 116 | 2015-03-10 | Football Conference | Kidderminster | 0.5067374 | Eastleigh | 0.3517764 | 0.2887427 | 0.3594808 |
| 117 | 2015-03-10 | Football Conference | Torquay | 0.5586778 | Chester | 0.4211171 | 0.2837677 | 0.2951152 |
| 130 | 2015-03-10 | Football Conference | Macclesfield | 0.8096579 | Telford | 0.7155054 | 0.1764752 | 0.1080195 |
| 133 | 2015-03-11 | English Championship | Blackburn | 0.6667812 | Bolton | 0.5429120 | 0.2529987 | 0.2040893 |