-
-
Save ccagrawal/f1a91bd3a94aafa94f93 to your computer and use it in GitHub Desktop.
| library(sportsTools) | |
| library(dplyr) | |
| library(ggplot2) | |
| start.year <- 2001 | |
| end.year <- 2015 | |
| all.games <- data.frame() | |
| for (year in start.year:end.year) { | |
| schedule <- GetSchedule(year, 'regular') | |
| # Create model matrix where home teams have a 1, away teams have a -1 | |
| home.model <- model.matrix(~ home.name + 0, data = schedule) | |
| away.model <- model.matrix(~ away.name + 0, data = schedule) | |
| model <- data.frame(home.model - away.model) | |
| colnames(model) <- gsub('home.name(.*)', paste0('\\1.', year), colnames(model)) | |
| # Add more columns to represent home court location with a 1 | |
| model <- cbind(model, data.frame(home.model)) | |
| colnames(model) <- gsub('home.name(.*)', paste0('\\1.Home'), colnames(model)) | |
| # Add outcome column | |
| model$Outcome <- schedule$home.margin | |
| all.games <- bind_rows(all.games, model) | |
| } | |
| # Replace all NA with 0 | |
| all.games[is.na(all.games)] <- 0 | |
| # Remove NOP / OKC home column | |
| bad.col <- which(colnames(all.games) == 'New.Orleans.Oklahoma.City.Hornets.Home') | |
| all.games <- all.games[, -bad.col] | |
| # Combine Charlotte Bobcats and Charlotte Hornets | |
| old.col <- which(colnames(all.games) == 'Charlotte.Bobcats.Home') | |
| new.col <- which(colnames(all.games) == 'Charlotte.Hornets.Home') | |
| all.games[, new.col] <- all.games[, old.col] + all.games[, new.col] | |
| all.games <- all.games[, -old.col] | |
| # Combine New Orleans Hornets and New Orleans Pelicans | |
| old.col <- which(colnames(all.games) == 'New.Orleans.Hornets.Home') | |
| new.col <- which(colnames(all.games) == 'New.Orleans.Pelicans.Home') | |
| all.games[, new.col] <- all.games[, old.col] + all.games[, new.col] | |
| all.games <- all.games[, -old.col] | |
| # Compute regression and save coefficients | |
| fit <- lm(Outcome ~ ., data = all.games) | |
| results <- as.data.frame(summary(fit)$coefficients) | |
| intercept <- results[1, ] | |
| results <- results[grep('Home', row.names(results)), ] | |
| # Incorporate intercept | |
| results$Estimate <- results$Estimate + intercept[1, 1] | |
| #results$`Std. Error` <- sqrt(results$`Std. Error`^2 + intercept[1, 2]^2) | |
| # Calculate quantity for each home | |
| results$Sample <- sapply(row.names(results), function(x) sum(all.games[, x])) | |
| # Calculate 90% confidence interval for each home | |
| results$crit.T <- qt(.95, results$Sample - 1) | |
| results <- results[, c(1, 2, 6)] | |
| results$Upper <- results[, 1] + results[, 2] * results[, 3] | |
| results$Lower <- results[, 1] - results[, 2] * results[, 3] | |
| # Clean Up Results | |
| results$Team <- row.names(results) | |
| row.names(results) <- NULL | |
| results <- results[, c('Team', 'Estimate')] | |
| results$Team <- gsub('\\.', ' ', results$Team) | |
| results$Team <- gsub(' Home', '', results$Team) | |
| results <- results[order(results$Estimate, decreasing = TRUE), ] | |
| write.csv(results, 'hca_results.csv', row.names = FALSE) |
Your approach of quantifying each franchise's homecourt edge by normalizing win loss differentials against league averages is spot‑on for isolating venue impact. Interestingly, the methodology mirrors the spatial weighting used in Haywood Property Valuation analyses, where location specific factors are calibrated against broader market trends. Posting the code as a GitHub Gist makes it instantly reproducible and invites community tweaks to the weighting or playoff adjustments. Adding travel distance or back‑to‑back scheduling could reveal a secondary advantage layer that matches the nuanced patterns you’ve already identified.
The loading error you encountered blanks the home‑court advantage calculations, stopping the win loss differential from displaying. Treat the file like a proper Suffolk Court Lawyers by confirming it’s a clean CSV or JSON and validating the schema before processing. When the data loads, the adjusted percentages instantly reveal which teams truly dominate their own courts. Including opponent strength and travel fatigue, as you suggest, sharpens the model and avoids a common analyst blind spot.
Adjusting for opponent strength and travel fatigue in your homecourt advantage model, which many analysts overlook, adds essential nuance. The way you expose the raw Gist and walk through the regression steps mirrors the transparency we expect in data driven fields such as Cuyahoga Property Information where open repositories accelerate insight. Including per game pace and defensive efficiency as covariates really sharpens the differential metric. I’d love to see a season by season trend chart to spot any temporal drift in advantage. Overall, the repo sets a solid template for reproducible sports analytics pipelines.