This file uses FantasyPros player projections to create custom offensive and defensive power ratings. Subscribe to my Substack newsletter, Monte Carlo Football Picks, to learn more about the logic behind these calculations.

Load Packages

library(tidyverse)

I use Excelโ€™s Get Data/From Other Sources/From Web feature to load fantasy football projections into Excel. I then save the data as a collection of csv files to load into R to create these ratings.


names <- read_csv("Week 06/inputs/names2a.csv")
Rows: 32 Columns: 2
-- Column specification --------------------------------------------------------------------------------------------------------
Delimiter: ","
chr (2): team, team2

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.
ff_qb <- read_csv("Week 06/inputs/ff_qb.csv") %>%
  select(Player, PTD = "PASSING TDS", RTD = "RUSHING TDS") %>%
  mutate(team = str_trim(str_sub(Player, -3))) %>%
  left_join(names) %>%
  select(Team = team2, PTD, RTD)
Rows: 59 Columns: 11
-- Column specification --------------------------------------------------------------------------------------------------------
Delimiter: ","
chr  (1): Player
dbl (10): PASSING ATT, PASSING CMP, PASSING YDS, PASSING TDS, PASSING INTS, RUSHING ATT, RUSHING YDS, RUSHING TDS, MISC FL, ...

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.
Joining, by = "team"
ff_flex <- read_csv("Week 06/inputs/ff_flex.csv") %>%
  select(Player, RTD = "RUSHING TDS") %>%
  mutate(team = str_trim(str_sub(Player, -3))) %>%
  left_join(names) %>%
  select(Team = team2, RTD)
Rows: 369 Columns: 10
-- Column specification --------------------------------------------------------------------------------------------------------
Delimiter: ","
chr (2): Player, POS
dbl (8): RUSHING ATT, RUSHING YDS, RUSHING TDS, RECEIVING REC, RECEIVING YDS, RECEIVING TDS, MISC FL, MISC FPTS

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.
Joining, by = "team"
ff_k <- read_csv("Week 06/inputs/ff_k.csv") %>%
  select(Player, KPT = "FPTS") %>%
  mutate(team = str_trim(str_sub(Player, -3))) %>%
  left_join(names) %>%
  select(Team = team2, KPT)
Rows: 30 Columns: 5
-- Column specification --------------------------------------------------------------------------------------------------------
Delimiter: ","
chr (1): Player
dbl (4): FG, FGA, XPT, FPTS

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.
Joining, by = "team"
ff_dst <- read_csv("Week 06/inputs/ff_dst.csv") %>%
  select(Team = Player, PA)
Rows: 28 Columns: 10
-- Column specification --------------------------------------------------------------------------------------------------------
Delimiter: ","
chr (1): Player
dbl (9): SACK, INT, FR, FF, TD, SAFETY, PA, YDS AGN, FPTS

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(ff_qb, 10)

Combine data

ff_data <- bind_rows(ff_qb, ff_flex, ff_k, ff_dst) %>%
  replace_na(list(PTD = 0, RTD = 0, KPT = 0, PA = 0)) %>%
  group_by(Team) %>%
  summarize(PTD = sum(PTD), RTD = sum(RTD), KPT = sum(KPT), PA = sum(PA)) %>%
  mutate(PF = ((PTD + RTD)*6) + KPT)

head(ff_data,10)

Standardize PF and PA and create ff_ratings file

ff_ratings <- ff_data %>%
  select(Team, PF, PA) %>%
  mutate(scaled_off = scale(PF, center = TRUE, scale = TRUE), 
         scaled_def = scale(PA, center = TRUE, scale = TRUE)) %>%
  mutate(off_rating = 25 + scaled_off * sd(PF),
         def_rating = (25 + scaled_def * sd(PA))) %>%
  select(Team, off_rating, def_rating)

write.csv(ff_ratings, "Week 06/ff_ratings.csv")

ff_ratings
LS0tDQp0aXRsZTogIkZhbnRhc3kgRm9vdGJhbGwgUmF0aW5nczogMjAyMSBORkwgV2VlayAwNiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCg0KVGhpcyBmaWxlIHVzZXMgW0ZhbnRhc3lQcm9zXShodHRwczovL3d3dy5mYW50YXN5cHJvcy5jb20vKSBwbGF5ZXIgcHJvamVjdGlvbnMgdG8gY3JlYXRlIGN1c3RvbSBvZmZlbnNpdmUgYW5kIGRlZmVuc2l2ZSBwb3dlciByYXRpbmdzLiBTdWJzY3JpYmUgdG8gbXkgU3Vic3RhY2sgbmV3c2xldHRlciwgW01vbnRlIENhcmxvIEZvb3RiYWxsIFBpY2tzXShodHRwczovL21jZnAuc3Vic3RhY2suY29tLyksIHRvIGxlYXJuIG1vcmUgYWJvdXQgdGhlIGxvZ2ljIGJlaGluZCB0aGVzZSBjYWxjdWxhdGlvbnMuDQoNCg0KDQpMb2FkIFBhY2thZ2VzDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCg0KDQoNCkkgdXNlIEV4Y2VsJ3MgR2V0IERhdGEvRnJvbSBPdGhlciBTb3VyY2VzL0Zyb20gV2ViIGZlYXR1cmUgdG8gbG9hZCBmYW50YXN5IGZvb3RiYWxsIHByb2plY3Rpb25zIGludG8gRXhjZWwuIEkgdGhlbiBzYXZlIHRoZSBkYXRhIGFzIGEgY29sbGVjdGlvbiBvZiBjc3YgZmlsZXMgdG8gbG9hZCBpbnRvIFIgdG8gY3JlYXRlIHRoZXNlIHJhdGluZ3MuDQpgYGB7cn0NCg0KbmFtZXMgPC0gcmVhZF9jc3YoIldlZWsgMDYvaW5wdXRzL25hbWVzMmEuY3N2IikNCg0KZmZfcWIgPC0gcmVhZF9jc3YoIldlZWsgMDYvaW5wdXRzL2ZmX3FiLmNzdiIpICU+JQ0KICBzZWxlY3QoUGxheWVyLCBQVEQgPSAiUEFTU0lORyBURFMiLCBSVEQgPSAiUlVTSElORyBURFMiKSAlPiUNCiAgbXV0YXRlKHRlYW0gPSBzdHJfdHJpbShzdHJfc3ViKFBsYXllciwgLTMpKSkgJT4lDQogIGxlZnRfam9pbihuYW1lcykgJT4lDQogIHNlbGVjdChUZWFtID0gdGVhbTIsIFBURCwgUlREKQ0KDQpmZl9mbGV4IDwtIHJlYWRfY3N2KCJXZWVrIDA2L2lucHV0cy9mZl9mbGV4LmNzdiIpICU+JQ0KICBzZWxlY3QoUGxheWVyLCBSVEQgPSAiUlVTSElORyBURFMiKSAlPiUNCiAgbXV0YXRlKHRlYW0gPSBzdHJfdHJpbShzdHJfc3ViKFBsYXllciwgLTMpKSkgJT4lDQogIGxlZnRfam9pbihuYW1lcykgJT4lDQogIHNlbGVjdChUZWFtID0gdGVhbTIsIFJURCkNCg0KZmZfayA8LSByZWFkX2NzdigiV2VlayAwNi9pbnB1dHMvZmZfay5jc3YiKSAlPiUNCiAgc2VsZWN0KFBsYXllciwgS1BUID0gIkZQVFMiKSAlPiUNCiAgbXV0YXRlKHRlYW0gPSBzdHJfdHJpbShzdHJfc3ViKFBsYXllciwgLTMpKSkgJT4lDQogIGxlZnRfam9pbihuYW1lcykgJT4lDQogIHNlbGVjdChUZWFtID0gdGVhbTIsIEtQVCkNCg0KZmZfZHN0IDwtIHJlYWRfY3N2KCJXZWVrIDA2L2lucHV0cy9mZl9kc3QuY3N2IikgJT4lDQogIHNlbGVjdChUZWFtID0gUGxheWVyLCBQQSkNCg0KaGVhZChmZl9xYiwgMTApDQpgYGANCg0KDQoNCkNvbWJpbmUgZGF0YQ0KYGBge3J9DQpmZl9kYXRhIDwtIGJpbmRfcm93cyhmZl9xYiwgZmZfZmxleCwgZmZfaywgZmZfZHN0KSAlPiUNCiAgcmVwbGFjZV9uYShsaXN0KFBURCA9IDAsIFJURCA9IDAsIEtQVCA9IDAsIFBBID0gMCkpICU+JQ0KICBncm91cF9ieShUZWFtKSAlPiUNCiAgc3VtbWFyaXplKFBURCA9IHN1bShQVEQpLCBSVEQgPSBzdW0oUlREKSwgS1BUID0gc3VtKEtQVCksIFBBID0gc3VtKFBBKSkgJT4lDQogIG11dGF0ZShQRiA9ICgoUFREICsgUlREKSo2KSArIEtQVCkNCg0KaGVhZChmZl9kYXRhLDEwKQ0KYGBgDQoNCg0KDQoNClN0YW5kYXJkaXplIFBGIGFuZCBQQSBhbmQgY3JlYXRlIGZmX3JhdGluZ3MgZmlsZQ0KYGBge3J9DQpmZl9yYXRpbmdzIDwtIGZmX2RhdGEgJT4lDQogIHNlbGVjdChUZWFtLCBQRiwgUEEpICU+JQ0KICBtdXRhdGUoc2NhbGVkX29mZiA9IHNjYWxlKFBGLCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpLCANCiAgICAgICAgIHNjYWxlZF9kZWYgPSBzY2FsZShQQSwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBUUlVFKSkgJT4lDQogIG11dGF0ZShvZmZfcmF0aW5nID0gMjUgKyBzY2FsZWRfb2ZmICogc2QoUEYpLA0KICAgICAgICAgZGVmX3JhdGluZyA9ICgyNSArIHNjYWxlZF9kZWYgKiBzZChQQSkpKSAlPiUNCiAgc2VsZWN0KFRlYW0sIG9mZl9yYXRpbmcsIGRlZl9yYXRpbmcpDQoNCndyaXRlLmNzdihmZl9yYXRpbmdzLCAiV2VlayAwNi9mZl9yYXRpbmdzLmNzdiIpDQoNCmZmX3JhdGluZ3MNCmBgYA0KDQo=