This file is used to create custom offensive and defensive power ratings based on points scored and allowed. Subscribe to my Substack newsletter, Monte Carlo Football Picks, to learn more about the logic behind these calculations.
Load packages
library(tidyverse)
Load data
results <- read_csv("Week 07/inputs/game_results.csv")
Rows: 272 Columns: 7
-- Column specification -------------------------------------------------------------------------------------------------------
Delimiter: ","
chr (2): Home, Away
dbl (5): Season, Week, Hm, Aw, Past
i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(results, 5)
Rearrange data
res_a <- results %>%
select(Season, Week, Past, Team = Home, Opponent = Away, PF = Hm, PA = Aw)
res_b <- results %>%
select(Season, Week, Past, Team = Away, Opponent = Home, PF = Aw, PA = Hm)
results2 <- bind_rows(res_a, res_b) %>%
filter(Past == 1)
head(results2, 5)
Calculate number of games played
game_count <- results2 %>%
group_by(Team) %>%
summarise(n = n())
head(game_count, 5)
Calculate total PF and PA
team_points <- results2 %>%
group_by(Team) %>%
summarize(PF = sum(PF), PA = sum(PA)) %>%
left_join(game_count, by = "Team") %>%
mutate(PF_PG = PF/n, PA_PG = PA/n) %>%
select(Team, PF, PF_PG, PA, PA_PG, n)
head(team_points, 5)
Calculate average points per game
ppg <- (sum(team_points$PF) + sum(team_points$PA))/ (sum(team_points$n)*2)
ppg
[1] 23.90426
Opp’s PF, PA and games played
opp_pts <- results2 %>%
left_join(team_points, by = c("Opponent" = "Team")) %>%
rename("Opp_PF" = PF.y, "Opp_PA" = PA.y) %>%
select(Season:Opponent, Opp_PF, Opp_PA, n) %>%
group_by(Team) %>%
summarise(Opp_PF = sum(Opp_PF), Opp_PA = sum(Opp_PA), Opp_n = sum(n))
points_summary <- left_join(team_points, opp_pts, by = "Team") %>%
mutate(Opp_PF_oth = Opp_PF - PA, Opp_PA_oth = Opp_PA - PF, Opp_n_oth = Opp_n - n)
head(points_summary, 5)
Calculate Opp PG averages and summarize data
summary <- points_summary %>%
mutate(Opp_PF_PG_oth = Opp_PF_oth/Opp_n_oth, Opp_PA_PG_oth = Opp_PA_oth/Opp_n_oth) %>%
select(Team, PF_PG, PA_PG, Opp_PF_PG_oth, Opp_PA_PG_oth)
head(summary, 5)
Calculate simple ratings for Offense and Defense
simple_ratings <- summary %>%
mutate(rating1 = ppg + (PF_PG - ppg) + (ppg - Opp_PA_PG_oth),
rating2 = ppg - ((ppg - PA_PG) - (ppg - Opp_PF_PG_oth))) %>%
mutate(scaled1 = scale(rating1, center = TRUE, scale = TRUE)) %>%
mutate(off_rating = 25 + if_else(scaled1 * sd(rating1) > 3.5, 3.5,
if_else(scaled1 * sd(rating1) < -3.5, -3.5,
scaled1 * sd(rating1)))) %>%
mutate(scaled2 = scale(rating2, center = TRUE, scale = TRUE)) %>%
mutate(def_rating = 25 + if_else(scaled2 * sd(rating2) > 3.5, 3.5,
if_else(scaled2 * sd(rating2) < -3.5, -3.5,
scaled2 * sd(rating2)))) %>%
select(Team, off_rating, def_rating)
write.csv(simple_ratings, "Week 07/simple_ratings.csv")
simple_ratings
LS0tDQp0aXRsZTogIlNpbXBsZSBSYXRpbmdzOiAyMDIxIE5GTCBXZWVrIDA3Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQoNClRoaXMgZmlsZSBpcyB1c2VkIHRvIGNyZWF0ZSBjdXN0b20gb2ZmZW5zaXZlIGFuZCBkZWZlbnNpdmUgcG93ZXIgcmF0aW5ncyBiYXNlZCBvbiBwb2ludHMgc2NvcmVkIGFuZCBhbGxvd2VkLiAgU3Vic2NyaWJlIHRvIG15IFN1YnN0YWNrIG5ld3NsZXR0ZXIsIFtNb250ZSBDYXJsbyBGb290YmFsbCBQaWNrc10oaHR0cHM6Ly9tY2ZwLnN1YnN0YWNrLmNvbS8pLCB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBsb2dpYyBiZWhpbmQgdGhlc2UgY2FsY3VsYXRpb25zLg0KDQoNCg0KTG9hZCBwYWNrYWdlcw0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQoNCg0KTG9hZCBkYXRhDQpgYGB7cn0NCnJlc3VsdHMgPC0gcmVhZF9jc3YoIldlZWsgMDcvaW5wdXRzL2dhbWVfcmVzdWx0cy5jc3YiKQ0KDQpoZWFkKHJlc3VsdHMsIDUpDQpgYGANCg0KDQoNClJlYXJyYW5nZSBkYXRhDQpgYGB7cn0NCnJlc19hIDwtIHJlc3VsdHMgJT4lDQogIHNlbGVjdChTZWFzb24sIFdlZWssIFBhc3QsIFRlYW0gPSBIb21lLCBPcHBvbmVudCA9IEF3YXksIFBGID0gSG0sIFBBID0gQXcpDQoNCnJlc19iIDwtIHJlc3VsdHMgJT4lDQogIHNlbGVjdChTZWFzb24sIFdlZWssIFBhc3QsIFRlYW0gPSBBd2F5LCBPcHBvbmVudCA9IEhvbWUsIFBGID0gQXcsIFBBID0gSG0pDQoNCnJlc3VsdHMyIDwtIGJpbmRfcm93cyhyZXNfYSwgcmVzX2IpICU+JQ0KICBmaWx0ZXIoUGFzdCA9PSAxKQ0KDQpoZWFkKHJlc3VsdHMyLCA1KQ0KYGBgDQoNCg0KDQpDYWxjdWxhdGUgbnVtYmVyIG9mIGdhbWVzIHBsYXllZA0KYGBge3J9DQoNCmdhbWVfY291bnQgPC0gcmVzdWx0czIgJT4lDQogIGdyb3VwX2J5KFRlYW0pICU+JQ0KICBzdW1tYXJpc2UobiA9IG4oKSkNCiAgDQoNCmhlYWQoZ2FtZV9jb3VudCwgNSkNCmBgYA0KDQoNCg0KQ2FsY3VsYXRlIHRvdGFsIFBGIGFuZCBQQQ0KYGBge3J9DQp0ZWFtX3BvaW50cyA8LSByZXN1bHRzMiAlPiUNCiAgZ3JvdXBfYnkoVGVhbSkgJT4lDQogIHN1bW1hcml6ZShQRiA9IHN1bShQRiksIFBBID0gc3VtKFBBKSkgJT4lDQogIGxlZnRfam9pbihnYW1lX2NvdW50LCBieSA9ICJUZWFtIikgJT4lDQogIG11dGF0ZShQRl9QRyA9IFBGL24sIFBBX1BHID0gUEEvbikgJT4lDQogIHNlbGVjdChUZWFtLCBQRiwgUEZfUEcsIFBBLCBQQV9QRywgbikNCg0KDQpoZWFkKHRlYW1fcG9pbnRzLCA1KQ0KYGBgDQoNCg0KDQpDYWxjdWxhdGUgYXZlcmFnZSBwb2ludHMgcGVyIGdhbWUNCmBgYHtyfQ0KcHBnIDwtIChzdW0odGVhbV9wb2ludHMkUEYpICsgc3VtKHRlYW1fcG9pbnRzJFBBKSkvIChzdW0odGVhbV9wb2ludHMkbikqMikNCg0KcHBnDQpgYGANCg0KDQoNCk9wcCdzIFBGLCBQQSBhbmQgZ2FtZXMgcGxheWVkDQoNCmBgYHtyfQ0Kb3BwX3B0cyA8LSByZXN1bHRzMiAlPiUNCiAgbGVmdF9qb2luKHRlYW1fcG9pbnRzLCBieSA9IGMoIk9wcG9uZW50IiA9ICJUZWFtIikpICU+JQ0KICByZW5hbWUoIk9wcF9QRiIgPSBQRi55LCAiT3BwX1BBIiA9IFBBLnkpICU+JQ0KICBzZWxlY3QoU2Vhc29uOk9wcG9uZW50LCBPcHBfUEYsIE9wcF9QQSwgbikgJT4lDQogIGdyb3VwX2J5KFRlYW0pICU+JQ0KICBzdW1tYXJpc2UoT3BwX1BGID0gc3VtKE9wcF9QRiksIE9wcF9QQSA9IHN1bShPcHBfUEEpLCBPcHBfbiA9IHN1bShuKSkNCg0KcG9pbnRzX3N1bW1hcnkgPC0gbGVmdF9qb2luKHRlYW1fcG9pbnRzLCBvcHBfcHRzLCBieSA9ICJUZWFtIikgJT4lDQogIG11dGF0ZShPcHBfUEZfb3RoID0gT3BwX1BGIC0gUEEsIE9wcF9QQV9vdGggPSBPcHBfUEEgLSBQRiwgT3BwX25fb3RoID0gT3BwX24gLSBuKQ0KICANCmhlYWQocG9pbnRzX3N1bW1hcnksIDUpDQpgYGANCg0KDQoNCkNhbGN1bGF0ZSBPcHAgUEcgYXZlcmFnZXMgYW5kIHN1bW1hcml6ZSBkYXRhDQpgYGB7cn0NCnN1bW1hcnkgPC0gcG9pbnRzX3N1bW1hcnkgJT4lDQogIG11dGF0ZShPcHBfUEZfUEdfb3RoID0gT3BwX1BGX290aC9PcHBfbl9vdGgsIE9wcF9QQV9QR19vdGggPSBPcHBfUEFfb3RoL09wcF9uX290aCkgJT4lDQogIHNlbGVjdChUZWFtLCBQRl9QRywgUEFfUEcsIE9wcF9QRl9QR19vdGgsIE9wcF9QQV9QR19vdGgpDQoNCmhlYWQoc3VtbWFyeSwgNSkNCmBgYA0KDQoNCg0KQ2FsY3VsYXRlIHNpbXBsZSByYXRpbmdzIGZvciBPZmZlbnNlIGFuZCBEZWZlbnNlDQpgYGB7cn0NCnNpbXBsZV9yYXRpbmdzIDwtIHN1bW1hcnkgJT4lDQogIG11dGF0ZShyYXRpbmcxID0gcHBnICsgKFBGX1BHIC0gcHBnKSArIChwcGcgLSBPcHBfUEFfUEdfb3RoKSwNCiAgICAgICAgIHJhdGluZzIgPSBwcGcgLSAoKHBwZyAtIFBBX1BHKSAtIChwcGcgLSBPcHBfUEZfUEdfb3RoKSkpICU+JQ0KICBtdXRhdGUoc2NhbGVkMSA9IHNjYWxlKHJhdGluZzEsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gVFJVRSkpICU+JQ0KICBtdXRhdGUob2ZmX3JhdGluZyA9IDI1ICsgaWZfZWxzZShzY2FsZWQxICogc2QocmF0aW5nMSkgPiAzLjUsIDMuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShzY2FsZWQxICogc2QocmF0aW5nMSkgPCAtMy41LCAtMy41LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlZDEgKiBzZChyYXRpbmcxKSkpKSAlPiUNCiAgbXV0YXRlKHNjYWxlZDIgPSBzY2FsZShyYXRpbmcyLCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpKSAlPiUNCiAgbXV0YXRlKGRlZl9yYXRpbmcgPSAyNSArIGlmX2Vsc2Uoc2NhbGVkMiAqIHNkKHJhdGluZzIpID4gMy41LCAzLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2Uoc2NhbGVkMiAqIHNkKHJhdGluZzIpIDwgLTMuNSwgLTMuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZWQyICogc2QocmF0aW5nMikpKSkgJT4lDQogIHNlbGVjdChUZWFtLCBvZmZfcmF0aW5nLCBkZWZfcmF0aW5nKQ0KDQp3cml0ZS5jc3Yoc2ltcGxlX3JhdGluZ3MsICJXZWVrIDA3L3NpbXBsZV9yYXRpbmdzLmNzdiIpDQoNCnNpbXBsZV9yYXRpbmdzDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=