This problem involves writing a program to analyze historical win-loss data for a single season of Division I NCAA women’s basketball teams and compute from this the win ratio for each team as well as the conference(s) with the highest average win ratio.

Background

The NCAA basketball season is well under way, and as loyal UA fans we are, of course, cheering on the Wildcats and sizing up the competition. An important indicator of the quality of a team is its win ratio over a season, which is defined to be the fraction of wins in the total number of games that team played. Thus, if a team plays N games and wins M of them, then its win ratio is M/N. Assuming---as we will do for this problem---there are no tied games, a team with M wins and N losses has a win ratio of M/(M+N). The average win ratio for a conference in a given season is the average of the win ratios of all of the teams in that conference in that season.

Expected Behavior

Write a program, in a file bball.py, that behaves as follows.
  1. Use input() (without arguments) to read the name of an input data file.
  2. Read and process the file specified (see 'Input' below for the file format). For each non-comment line in the file:
    • create a Team object from the line (see Program Structure below);
    • parse the line to extract the appropriate data values;
    • use these values to compute the team's win ratio;
    • update initialize the corresponding Team object appropriately.
    • add the team to the list of teams in the appropriate conference, creating a conference if necessary.
  3. Compute the average win ratio for each conference and find the conference(s) that have the highest win ratio.
  4. Print out the results of your analysis in the format given below under Output. Some examples are shown here.

Input

Any line in the input file that begins with "#" is a comment and should be ignored. Each non-coment line gives win-loss data for a team and has the following format: team name (one or more words), followed by conference name in parentheses (one or more words), followed by the number of wins, followed by the number of losses. For example:
# Division I Women's Basketball: 2015-16 Season
# School (Conference)	Wins	Losses
UConn (AAC)		       38     0
UC Riverside (Big West)	23     9
St. John's (NY) (Big East)	23     10
Arizona (Pac-12)	13	19
If more than one conference has the highest average win ratio, they can be printed out in any order.

Note that the team name may consist of multiple words, some of which may be parenthesized, e.g., "St. John’s (NY)". The conference name is given by the rightmost parenthesized group of words in the line.

Output

The output should be the conference name and win-loss average for all of the conferences with the best win ratio average, one conference per line, in the following format (the simplest way to get this into your code without any mistakes is to copy-paste it into your program and then editing the variable names appropriately):
"{} : {}".format(conf_name, conf_win_ratio_avg)
where conf_name is the name of a conference and conf_win_ratio_avg is the average win ratio for that conference. Some examples are shown here.

Assertions

Your program should use assert statements to check the following (however, see below for replacements for asserts in situations where asserts are difficult to state).
  • For each method, any pre-conditions on the arguments for that method. The assert should be placed at or very soon after the beginning of the method.
  • For each loop that either (i) computes a value; or (ii) transforms some data, at least one assert that holds in the body of the loop. You can choose what the invariant is, but it must be something that:
    • reflects the computation of the loop; and
    • is not simply a statement of the iteration condition (or some expression whose value follows directly from the iteration condition).

Asserts are not necessary for loops that neither compute values nor transform data, e.g., loops that simply read in data or print out results.

This level of assertion-checking is almost certainly overkill, but we'll do this for a little while in order to get more experience with pre-conditions and loop invariants and to practise working with assert statements.

Try to make your asserts as precise and specific as you can.

Replacements for asserts

In some situations, it may be difficult or impossible to write an assert that captures what you want to capture. In such situations, in place of an assert you can write a comment giving the invariant or assumption you want to state. Such a comment should be written as follows:
### INVARIANT: ...your invariant in English and/or Python
or
### ASSUMPTION: ...your assumption in English and/or Python

Programming Requirements

Your program should implement (at least) the following classes along with the methods specified.
class Team
An object of this class represents information about a team: namely, the team name, the conference it belongs to, and win-loss data. This class should implement the following methods:
  • __init__(self, line) : line is a line read from the input file. Initializes the object with information extracted from line. The information stored as attributes for each team should be sufficient to implement the other methods for the team specified below.
  • name(self): returns the name of the team.
  • conf(self): returns the conference the team belongs to.
  • win_ratio(self): returns the win ratio for the team.
  • __str__(self): returns a string with information about the team, in the following format:
    "{} : {}".format(name, win_ratio_str)
    where name is the name of the team and win_ratio_str is its win ratio (as a string).
class Conference
An object of this class represents information about a set of teams, namely, the teams belonging to that conference. This class should implement the following methods:
  • __init__(self, conf) : conf is a string giving the name of a conference. Initializes a conference object with name conf. The list of teams for the conference is initialized to be empty.
  • __contains__(self, team) : team is a Team object. Returns True if team is in the list of teams for this conference; False otherwise.
  • name(self): returns the name of the conference object;
  • add(self, team): Takes a team object team as argument and adds team to the list of teams associated with the object;
  • win_ratio_avg(self): returns the average win ratio for the conference (a floating-point value).
  • __str__(self): returns a string with information about the conference, in the following format:
    "{} : {}".format(name, win_ratio_str)
    where name is the name of the conference and win_ratio_str is its average win ratio (as a string).
class ConferenceSet
An object of this class represents a set of conferences. This class should implement the following methods:
  • __init__(self) : creates and returns the object. Initially the set of conferences is empty. Initializes the set of conferences to be empty.
  • add(self, team) : team is a Team object. Adds team to the appropriate conference in the list of conferences, if necessary creating a Conference object for the conference of this team.
  • best(self) : returns a list of conferences that have the highest average win ratio.

I found it convenient to also implement __repr__() methods for some of these classes for debugging purposes. This is optional.

Examples

Several examples of this data analysis are given here.

Testing

You may want to consider the following situations for black-box testing:
  • empty input file;
  • input with only comment lines
  • just a single team
  • several teams, but just a single conference
  • several conferences, just a single team each
  • several conferences, each with several teams
  • several conferences with tied win ratio
  • a team with 0 losses
  • a team with 0 losses
  • a team with many parenthesized words in the name