7
\$\begingroup\$

Input

A CSV file, read from standard input, containing the 2011 college football scores with the 4 columns: Winner, Winner Score, Loser, Loser Score. For example,

Cincinnati,35,Connecticut,27
Pittsburgh,33,Syracuse,20
Baylor,48,Texas,24

This file was generated by a script I have that screen-scrapes rivals.yahoo.com. Teams that played fewer than 3 games were omitted.

Output

Print to standard output a newline-separated ranking of teams, ordered from "best" to "worst".

It is permitted to output a "truncated" ranking (e.g., Top 25 or Top 50), but this is discouraged.

Scoring

The ranking produced by your program will be scored on its ability to predict the winners of bowl games.

A victory for Team A over Team B is considered "predicted" by your ranking if either:

  • You ranked Team A higher than (i.e., earlier in the output than) Team B.
  • You ranked (i.e., listed anywhere in the output) Team A but not Team B.

You get 1 point for each bowl game "predicted" by your ranking.

Highest score gets the accepted answer. In case of a tie, the earliest submission wins.

Rules

  • Deadline for entries is Friday, December 16, at 17:00 CST (23:00 UTC).
  • You may use the language of your choice.
  • You may use third-party open-source math libraries such as SciPy.
  • You may submit multiple entries.
  • The output must be a pure function of the input. (So if you need an arbitrary tiebreaker in your ranking, don't use rand(), use alphabetical order.)

Scores: It's a tie!

(Based on results from rivals.yahoo.com)

dan04: 11 points

  • Auburn > Virginia
  • Baylor > Washington
  • Boise St. > Arizona St.
  • Oklahoma > Iowa
  • Oklahoma St. > Stanford
  • Oregon > Wisconsin
  • South Carolina > Nebraska
  • Southern Miss > Nevada
  • TCU > Louisiana Tech
  • Texas > California
  • Texas A&M > Northwestern

AShelly: 11 points

  • Arkansas > Kansas St.
  • Baylor > Washington
  • Boise St. > Arizona St.
  • Cincinnati > Vanderbilt
  • Houston > Penn St.
  • Michigan > Virginia Tech
  • Oklahoma St. > Stanford
  • Rutgers > Iowa St.
  • South Carolina > Nebraska
  • Southern Miss > Nevada
  • TCU > Louisiana Tech
\$\endgroup\$
  • 1
    \$\begingroup\$ Anyone who has messed around with this problem in the past will tell you that it is harder than it looks. I may still have a code around here somewhere...it'll need work to deal with the input format, but that should be easy. \$\endgroup\$ – dmckee Dec 4 '11 at 3:14
  • \$\begingroup\$ Care to post the data somewhere, for testing, or the scraper code? \$\endgroup\$ – Iterator Dec 8 '11 at 18:31
  • \$\begingroup\$ Also, there's a difference between BCS ranking and bowl results prediction, so the title of this question may need to be revised. \$\endgroup\$ – Iterator Dec 8 '11 at 18:32
  • \$\begingroup\$ "Top 25" seems ill-defined - at what point in time, by what measure? There are also more than 25 bowl games in the list referenced. So, it seems important to predict winners for all of the games. \$\endgroup\$ – Iterator Dec 8 '11 at 18:36
  • 1
    \$\begingroup\$ I see no winner chosen yet. Would you select one or push the deadline? \$\endgroup\$ – Joanis Jan 3 '12 at 1:21
2
\$\begingroup\$

I'll give it a shot. Here's some ugly Ruby that bases its ranking on how well each team scored compared to the opponent's average points against. It weights scores based on the opponent's rank in the list, and iterates until it is stable.

class Team
  attr_accessor :wins,:loss,:for,:agn,:rank,:old_rank
  attr_reader :beat,:fell, :name
  def initialize nm
    @name = nm
    @wins=0
    @loss=0
    @for = 0
    @agn = 0
    @beat = []
    @fell  = []
  end
  def win pts,opp,opts
    @wins+=1
    @for+=pts
    @agn+=opts
    @beat << [opp,pts,opts]
  end
  def lose pts,opp,opts
    @loss+=1
    @for+=pts
    @agn+=opts
    @fell << [opp,pts,opts]
  end
  def avgPtsFor
    @for.to_f/(@wins+@loss)
  end
  def avgPtsAgn
    @agn.to_f/(@wins+@loss)
  end
  def score teams
    #most points for scoring above opponents average, scaled by opponent's rank.
    x = @beat.inject(0){|sum,game|opp = teams[game[0]]
      sum+(game[1]/opp.avgPtsAgn*(1-(opp.rank/teams.size)))/@beat.size
      }
    y = @fell.inject(0){|sum,game|opp = teams[game[0]];
      sum+(game[1]/opp.avgPtsAgn*(opp.rank/teams.size))/@fell.size
      }
    (x+y)*(@wins/@loss.to_f)++(@rank/teams.size)/8
  end

end

#read the file
teams = Hash.new
ARGF.each_line{|l| 
  result = l.split(',')  
  team = teams[result[0]]||Team.new(result[0])
  team.win(result[1].to_i,result[2],result[3].to_i)
  teams[result[0]]=team
  team = teams[result[2]]||Team.new(result[2])
  team.lose(result[3].to_i,result[0],result[1].to_i)
  teams[result[2]]=team
}
#initial ranking by w/l,pts for, pts against
rankings = teams.map{|n,t|t}.sort_by{|t|[t.wins.to_f/t.loss,t.for,t.agn]}
rankings.each_with_index{|a,i|a.rank = (i+1.0)}

#loop 
stable = false
loops = 0
while !stable and loops < 1000 do
  stable = true
  n = rankings.size.to_f
  rankings = rankings.sort_by{|t| 
  t.score(teams)
  }
  rankings = rankings.each_with_index{|t,i|
    t.old_rank = t.rank
    t.rank = (i+1.0)
    stable = false if (t.rank != t.old_rank)
  }
  loops+=1
end

i=0
puts rankings.reverse.map{|t|"#{i+=1}: "+t.name}

Here's the output:

1: Oklahoma St.
2: LSU
3: Boise St.
4: Houston
5: Stanford
6: Oregon
7: Alabama
8: Arkansas
9: Wisconsin
10: TCU
11: Michigan
12: Georgia
13: South Carolina
14: Arkansas St.
15: Southern Miss
16: Michigan St.
17: West Virginia
18: Baylor
19: USC
20: Georgia Tech
21: Clemson
22: Tulsa
23: BYU
24: Oklahoma
25: Kansas St.
26: Nebraska
27: Virginia Tech
28: Penn St.
29: Notre Dame
30: Cincinnati
31: Northern Illinois
32: LA Lafayette
33: San Diego St.
34: Toledo
35: Florida Intl.
36: Texas A&M
37: Louisiana Tech
38: Ohio
39: Florida St.
40: North Carolina
41: Nevada
42: Iowa
43: Temple
44: Washington
45: West. Kentucky
46: Rutgers
47: Air Force
48: Auburn
49: Wyoming
50: California
51: Texas
52: Ohio St.
53: W. Michigan
54: Virginia
55: Utah St.
56: Florida
57: Missouri
58: Northwestern
59: SMU
60: Vanderbilt
61: Mississippi St.
62: Pittsburgh
63: Ball St.
64: Louisville
65: Purdue
66: East Carolina
67: UCLA
68: Wake Forest
69: Marshall
70: North Texas
71: N.C. State
72: Arizona St.
73: Utah
74: South Florida
75: Hawaii
76: San Jose St.
77: Texas Tech
78: Illinois
79: Kent St.
80: UTEP
81: UCF
82: Tennessee
83: East. Michigan
84: Connecticut
85: Navy
86: Duke
87: Rice
88: New Mexico St.
89: LA Monroe
90: Washington St.
91: Bowling Green
92: Kentucky
93: Syracuse
94: Arizona
95: Fresno St.
96: Troy
97: Oregon St.
98: Army
99: Colorado
100: Iowa St.
101: UNLV
102: UAB
103: Buffalo
104: Middle Tenn. St.
105: Minnesota
106: Boston Coll.
107: Cent. Michigan
108: Colorado St.
109: Mississippi
110: Tulane
111: Florida Atlantic
112: Memphis
113: Idaho
114: Kansas
115: New Mexico
116: Indiana
117: Maryland
118: Akron
\$\endgroup\$
1
\$\begingroup\$

Here's an example program. It assigns each team a rating equal to the least-squares solution of winner_rating - loser_rating = 1 across all games:

import sys
import csv
from scipy import linalg

def read_csv(input_file):
    """Return a list of (winner, loser) tuples from the CSV file."""
    return [(row[0], row[2]) for row in csv.reader(input_file)]

def lstsq_ratings(data):
    """
    data = a list of (team1, team2, margin) tuples.
    Returns a dictionary of {team: rating}
    """
    # Extract the team names from the data
    teams = set()
    for winner, loser in data:
        teams.add(winner)
        teams.add(loser)
    teams = sorted(teams)
    team_indices = dict(zip(teams, xrange(len(teams))))
    # Construct the matrix equation for the teams
    num_teams = len(teams)
    lhs = []
    rhs = []
    for winner, loser in data:
        rhs.append(1)
        lhs_row = [0] * num_teams
        lhs_row[team_indices[winner]] = 1
        lhs_row[team_indices[loser]] = -1
        lhs.append(lhs_row)
    # Solve equation and return results
    ratings = linalg.lstsq(lhs, rhs)[0]
    return dict((team, rating) for team, rating in zip(teams, ratings))

if __name__ == '__main__':
    ratings = lstsq_ratings(read_csv(sys.stdin))
    output = sorted(ratings.items(), key=lambda x: (-x[1], x[0].lower()))
    for team, rating in output:
        print(team)

The output is:

Oklahoma St.
LSU
Alabama
Oklahoma
Stanford
Oregon
Baylor
Arkansas
South Carolina
Kansas St.
USC
Georgia
Wisconsin
Virginia Tech
Boise St.
Houston
Michigan
Nebraska
Texas
Texas A&M
Michigan St.
Missouri
Clemson
Penn St.
Auburn
TCU
Notre Dame
West Virginia
Southern Miss
Georgia Tech
Arkansas St.
North Carolina
Iowa
Tulsa
Washington
Cincinnati
California
Mississippi St.
Florida
Rutgers
Utah
BYU
UCLA
Florida St.
LA Lafayette
Northern Illinois
Ohio St.
Louisville
Texas Tech
San Diego St.
Vanderbilt
Arizona St.
Iowa St.
Tennessee
West. Kentucky
Virginia
Louisiana Tech
Illinois
Wyoming
Kentucky
Purdue
Toledo
Marshall
Pittsburgh
Arizona
N.C. State
Florida Intl.
Wake Forest
SMU
Northwestern
Ohio
Nevada
South Florida
Air Force
Utah St.
Oregon St.
Kansas
Syracuse
W. Michigan
North Texas
Temple
East Carolina
Ball St.
Washington St.
Kent St.
Colorado
Minnesota
Connecticut
Rice
Navy
Duke
East. Michigan
San Jose St.
Mississippi
UTEP
UCF
Hawaii
LA Monroe
Fresno St.
Bowling Green
New Mexico St.
Boston Coll.
Troy
UNLV
Army
UAB
Colorado St.
Cent. Michigan
Indiana
New Mexico
Middle Tenn. St.
Buffalo
Florida Atlantic
Maryland
Idaho
Memphis
Tulane
Akron
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.