A Data-Driven Approach to Determining Conference Supremacy
This repository implements a novel method for evaluating the best college football conferences using a cross-country scoring mechanism. This method ranks conferences based on the positions of their top teams in the Associated Press's Top 25 rankings before, throughout, and at the end of each college football season. This provides an objective, measurable comparison of conference strength for every week of the season.
Inspired by the team scoring in cross-country racing, this method sums the "finishing positions," or the ranking, of the top five teams within each conference. The conference with the lowest total score is deemed the best, emphasizing overall depth and strength rather than just top-tier performance (like whichever conference produced the most recent national champion, or bogus math like detailed in the below tweet).
Take a look at the average AP Ranking by Power Five Conference 📊
— FOX College Football (@CFBONFOX) January 18, 2023
Which conference surprises you the most? 👀 pic.twitter.com/qJ0AWkQkOm
This approach was first introduced in 2015 and updated in 2019 and 2024. You can read more about the method and its evolution in the following blog posts:
- 2015: The Race for Supremacy
- 2019: AP XC - An Update
- 2024: Updating the race for conference realignments | Medium
The results of this work across every season on record, tracking each conference's Final AP ranking score by year:

This code is built upon the ESPN College Football API, shown by Akshay Easwaran to have hidden endpoints with reliable AP ranking information back to 2014. Thus, this code is dependent upon the quality and stability of ESPN's API data structure.
data: Input/output data, organized asdata/<year>/<4_team|5_team>/. Each subdirectory holds one CSV per week (<year>_week_<N>.csv) plus one running<year>_<4_team|5_team>_summary_statistics.csvthat accumulates every week's conference scores for that season.images: Auto-generated graph PNGs referenced by this README. These are overwritten in place each time the graphing scripts run (see Graphing below) - don't hand-edit them.espn_api.py: Script for fetching data from the ESPN API.store_data.py: Script for storing data fetched from external sources.graph_data.py: Generates the styled graphs shown in this README, both for a single season and across every season on record (see "Graphing" under Specialized Uses below).weekly_update.py: One-shot entry point that pulls the latest rankings and regenerates the current-week graphs in a single call - see Automating Weekly Updates.counterfactual_conferences_2023.py: Standalone "what-if" script remapping 2023 results onto the 2024 realigned conferences.
Make sure all required packages are installed. You can do this in a few ways:
Using your command line or bash terminal:
pip install -r requirements.inThis will install the package's dependencies to your base python interpreter. This is not recommended as other python projects or repositories may require different versions of these packages.
requirements.in lists the packages this project actually needs, with any version pins kept deliberate
and minimal (e.g. a security-floor minimum version). requirements.txt, by contrast,
is a full pip freeze snapshot of a known-working environment - useful as a reference if you hit a
dependency conflict, but not what you should install from directly.
Setting up a virtual python environment (venv) is recommended to ensure no dependency conflicts across your personal projects or with other developers on this project. See below for executing this on windows operating systems.
To execute a full run that pulls the latest AP rankings from ESPN and scores them as a cross-country meet,
execute and run the store_data.py file.
weekly_update.py is a single entry point that does the whole week's work in one
call: it fetches and stores the latest AP rankings for both 4-team and 5-team scoring, then regenerates
images/current_week_4team.png and images/current_week_5team.png - the same filenames this README
links to above, so a new week's results show up here automatically with no README edits needed.
Run it directly with your venv's interpreter:
python weekly_update.pyFor unattended/scheduled runs, use the wrapper script for your OS instead of calling weekly_update.py
directly. Both scripts locate the project's venv relative to their own location - no machine-specific
paths to edit - and exit with a clear error if no venv is found rather than silently falling back to a
different interpreter:
- Windows:
weekly_update.bat- point a Task Scheduler (schtasks) job at this file. - Linux/macOS/WSL/Git Bash:
weekly_update.sh- point acronjob at this file. It also works against a Windows-created venv when run from WSL or Git Bash.
Neither script pauses for input by default, so they're safe to run unattended; each has a commented-out
pause (Windows) / read (Linux) line you can uncomment if you'd rather the window stay open when
running it manually by double-click.
-
Fetch Data:
-
Use
espn_api.pyto fetch the latest college football data from ESPN. -
Critical Functions:
-
full_ap_xc_run(year: int = None, week=None, four_team_score: bool = False) -> dictPurpose: Fetches the full AP cross-country run data for a given year and week, with an option for four-team scoring.
Inputs:
year: The year for which to fetch data (optional).week: The week for which to fetch data (optional).four_team_score: Boolean indicating whether to use four-team scoring (default isFalse).
Outputs:
- A dictionary containing the fetched data, including conference team data and conference scores.
-
-
-
Store Data:
-
Use
store_data.pyto store the fetched data into a suitable format for analysis. -
Critical Functions:
-
summarize_data(week, conference_score_tuple: list, n_teams_str: str = pent, existing_summary_df: pd.DataFrame = None)Purpose: Summarizes data for a given week and conference score tuple. It standardizes the week formatting, handles potential errors, and writes the summary data to a file.
Inputs:
week: The week to summarize.conference_score_tuple: List of conference scores.n_teams_str: String indicating the number of teams (default ispent).existing_summary_df: Existing summary DataFrame (optional).
Outputs:
- The summary data as a DataFrame.
-
store_weekly_results(year: int = None, week=None, four_team_score: bool = False)Purpose: Stores weekly results by calling various functions to fetch, prepare, and write data.
Inputs:
year: The year to store results for (optional).week: The week to store results for (optional).four_team_score: Boolean indicating whether to use four-team scoring (default isFalse).
Outputs:
- The results of the storage operation.
-
store_all_data_2014_to_present()Purpose: Stores all data from 2014 to the present year by iterating through each year and week, calling
store_weekly_resultsfor both four-team and five-team scoring.Inputs: None
Outputs: None
-
-
-
Counterfactual Conference Analysis:
-
Use
counterfactual_conferences_2023.pyto impose the 2024 conference membership schema onto the 2023 season results, previewing how the realigned conferences could perform in 2024. -
Critical Functions:
-
realign_teams(df: pd.DataFrame, n_teams_score: int = 5)Purpose: Realigns teams based on the 2024 conference membership schema and recalculates their standings using the 2023 season results. This function previews the future strength of each conference under the upcoming realignments.
Inputs:
df: DataFrame containing the 2023 season results.n_teams_score: The number of top team scores to sum for each conference (default is 5).
Outputs:
- A DataFrame with teams realigned to their new conferences and the recalculated conference standings.
-
-
-
Graphing:
-
Use
graph_data.pyto turn a season's (or every season's) stored summary statistics into the styled, halogen-glow graphs shown at the top of this README. -
Critical Functions:
-
graph_year(year: int, num_scoring_teams: int = 5, show: bool = True)Purpose: Plots one season's weekly conference scores, Preseason through Final.
Inputs:
year: The season to graph.num_scoring_teams:4or5; which scoring mode's data to plot (default5).show: Whether to pop open a plot window (defaultTrue; setFalsefor unattended runs).
Outputs:
- The
matplotlib.pyplotmodule, with the generated figure as its current figure.
-
graph_final_rankings_by_year(num_scoring_teams: int = 5, show: bool = True)Purpose: Plots each conference's Final-week score across every season on record - one point per year - so you can see a conference's ranking trend over time rather than within one season.
Inputs: Same as
graph_year, minusyear(it covers every season found underdata/).Outputs:
- The
matplotlib.pyplotmodule, as above.
- The
-
save_graph(plot, file_name: str = None) -> strPurpose: Saves a graph produced by the functions above to
images/, creating that directory if needed. Iffile_nameis omitted, it's derived from the graph's own title.Inputs:
plot: The return value ofgraph_year()/graph_final_rankings_by_year().file_name: Output filename (optional).
Outputs:
- The full path the image was saved to.
-
-
Conference line colors follow a fixed, deliberate convention (defined in
CONFERENCE_COLORSingraph_data.py): SEC is blue, Big Ten is yellow, ACC is red, Big 12 is purple, the American (AAC) is orange - reserved even though it rarely fields enough ranked teams to score - and the defunct Pac-12 is light blue, since it still appears throughout the historical data. Any other conference that shows up (Mountain West, Sun Belt, MAC, etc.) is colored from a rotating fallback palette, since no fixed color has ever been established for them.
-
Contributions are welcome! Please fork the repository and create a pull request with your changes.
Thanks to John-Lee-Cooper, seanreid5454, & akeaswaran for their thought partnership and good ideas over the years on this project.
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.

