HW02

Anonymous
python
02/27/2026 11:52 PM
13.5 KB
14
Indexable
"""
Name: Sebastian Brookes
Course: DS 2500
Assignment: Homework 2
Date: 2026-02-27
Filename: 02.py
"""

import csv
import math
import matplotlib.pyplot as plt

DATA_FILE = "wsl_goals_2324.csv"

ABBR_MAP = {
    "Arsenal": "ARS",
    "Aston Villa": "AVL",
    "Brighton and Hove Albion": "BHA",
    "Bristol City": "BCFC",
    "Chelsea": "CHEE",
    "Everton": "EFC",
    "Leicester City": "LCFC",
    "Liverpool": "LFC",
    "Manchester City": "MCFC",
    "Manchester United": "MUFC",
    "Tottenham Hotspur": "THFC",
    "West Ham United": "WHU"
}

SEPARATOR = "=" * 40

COLOR_MAP = {
    "Arsenal": "red",
    "Aston Villa": "lightskyblue",
    "Brighton and Hove Albion": "mediumblue",
    "Bristol City": "darkred",
    "Chelsea": "blue",
    "Everton": "darkblue",
    "Leicester City": "orange",
    "Liverpool": "orangered",
    "Manchester City": "steelblue",
    "Manchester United": "yellow",
    "Tottenham Hotspur": "navy",
    "West Ham United": "brown"
}

# PART 01

class Team:
    def __init__(self, name):
        self.name = name
        self.goals = []

    def add_goals(self, goals):
        """
        Parameters: an int of goals scored in a game
        Returns: nothing
        Does: appends the given goal count to the
              team's list of goals
        """
        self.goals.append(goals)

    def get_total_goals(self):
        """
        Parameters: nothing
        Returns: an int
        Does: computes and returns the total number
              of goals across all games
        """
        return sum(self.goals)

    def __str__(self):
        """
        Parameters: nothing
        Returns: a string
        Does: returns a string with the team name and
              their total number of goals
        """
        return f"{self.name} has scored {self.get_total_goals()} goals."

def read_data(file):
    """
    Parameters: a string filepath to a CSV file
    Returns: a list of Team objects
    Does: reads the CSV file and creates a Team object
          for each row, adding goal counts per game
    """
    with open(file, "r", encoding="utf-8") as f:
        reader = csv.reader(f)
        next(reader)
        teams = []
        for row in reader:
            team = Team(row[0])
            for goal in row[1:]:
                if goal == "":
                    team.add_goals(0)
                else:
                    team.add_goals(int(goal))
            teams.append(team)
        return teams

# PART 02

# 2.1
def get_team_with_most_goals(teams):
    """
    Parameters: a list of Team objects
    Returns: a tuple of (team_name, total_goals)
    Does: finds and returns the team with the highest
          total number of goals
    """
    most_goals = 0
    best_team_name = ""
    for team in teams:
        total = team.get_total_goals()
        if total > most_goals:
            most_goals = total
            best_team_name = team.name
    return (best_team_name, most_goals)

# 2.2
def get_mean_season_goals(teams):
    """
    Parameters: a list of Team objects
    Returns: a float
    Does: computes and returns the mean total goals
          scored across all teams in a session
    """
    total_goals = sum(team.get_total_goals() for team in teams)
    return total_goals / len(teams)

# 2.3
def get_variance_season_goals(teams):
    """
    Parameters: a list of Team objects
    Returns: a float
    Does: computes and returns the variance of total
          goals scored across all teams in a session
    """
    mean = get_mean_season_goals(teams)
    squared_diffs = [(team.get_total_goals() - mean) ** 2 for team in teams]
    return sum(squared_diffs) / len(squared_diffs)

# 2.4
def get_standard_deviation_season_goals(teams):
    """
    Parameters: a list of Team objects
    Returns: a float
    Does: computes and returns the standard deviation
          of total goals across all teams in a session
    """
    return math.sqrt(get_variance_season_goals(teams))

# 2.5
def get_median_for_team(name, teams):
    """
    Parameters: a string team name and a list of Team objects
    Returns: a float
    Does: finds the given team and computes and returns
          the median goals scored per game
    """
    for team in teams:
        if name == team.name:
            sorted_goals = sorted(team.goals)
            n = len(sorted_goals)
            mid = n // 2
            if n % 2 == 1:
                return sorted_goals[mid]
            else:
                return (sorted_goals[mid] + sorted_goals[mid - 1]) / 2

# 2.6
def get_mode_for_team(name, teams):
    """
    Parameters: a string team name and a list of Team objects
    Returns: an int
    Does: finds the given team and computes and returns
          the most frequently occurring goals per game
    """
    goal_freq = {}
    for team in teams:
        if name == team.name:
            for count in team.goals:
                goal_freq[count] = goal_freq.get(count, 0) + 1
            return max(goal_freq, key=lambda x: goal_freq[x])

# 2.7
def get_most_consistent_team(teams):
    """
    Parameters: a list of Team objects
    Returns: a tuple of (team_name, coefficient_of_variation)
    Does: finds and returns the team with the lowest
          coefficient of variation (std dev / mean)
    """
    cv_by_team = {}
    for team in teams:
        team_mean = team.get_total_goals() / len(team.goals)
        squared_diffs = [(game - team_mean) ** 2 for game in team.goals]
        team_stdev = math.sqrt(sum(squared_diffs) / len(squared_diffs))
        cv = team_stdev / team_mean
        cv_by_team[team.name] = cv
    consistent_team_name = min(cv_by_team, key=lambda x: cv_by_team[x])

    return (consistent_team_name, cv_by_team[consistent_team_name])


# 2.8
def get_longest_streak_team(teams):
    """
    Parameters: a list of Team objects
    Returns: a tuple of (team_name, streak_length)
    Does: finds and returns the team with the longest
          streak of games scoring 2 or more goals
    """
    best_team_streak = 0
    best_team_name = ""
    for team in teams:
        streak = 0
        team_streak = 0
        for game in team.goals:
            if game >= 2:
                streak += 1
                if streak > team_streak:
                    team_streak = streak
            else:
                streak = 0
        if team_streak > best_team_streak:
            best_team_streak = team_streak
            best_team_name = team.name
    return (best_team_name, best_team_streak)

# 2.9
def get_most_improved_mean_goals_team(teams):
    """
    Parameters: a list of Team objects
    Returns: a tuple of (team_name, improvement)
    Does: finds and returns the team that improved their
          mean goals per game the most from the first
          half to the second half of the season
    """
    best_name = ""
    best_improvement = float("-inf")
    for team in teams:
        mid = len(team.goals) // 2
        first_half_mean = sum(team.goals[:mid]) / mid
        second_half_mean = sum(team.goals[mid:]) / (len(team.goals) - mid)
        improvement = second_half_mean - first_half_mean
        if improvement > best_improvement:
            best_improvement = improvement
            best_name = team.name
    return (best_name, best_improvement)

# 2.10
def plot_teams(teams):
    """
    Parameters: a list of Team objects
    Returns: nothing
    Does: generates an animated scatter plot of each
          team's cumulative goal progression throughout
          the season
    """
    for game_num in range(len(teams[0].goals)):
        plt.clf()
        for i, team in enumerate(teams):
            cumulative = sum(team.goals[:game_num + 1])
            plt.scatter(cumulative, i,
                        color=COLOR_MAP[team.name],
                        label=team.name)
        plt.legend(bbox_to_anchor=(1.05, 1),
                   loc="upper left")
        plt.tight_layout(rect=[0.05, 0, 1, 0.95])
        max_goals = max(t.get_total_goals() for t in teams)
        plt.xlim(0, max_goals)
        labels = [ABBR_MAP[t.name] for t in teams]
        plt.yticks(range(len(teams)), labels)
        plt.title("Progression of Teams")
        plt.xlabel("Goals")
        plt.ylabel("Team")
        plt.pause(0.75)
    plt.show()


def print_section(label, result, separator):
    """
    Parameters: a string label, a string result, and
                a string separator
    Returns: nothing
    Does: prints a labeled section wrapped in separator
          lines
    """
    print(separator)
    print(label)
    print(separator)
    print(result, "\n")


def main():
    """
    Parameters: nothing
    Returns: nothing
    Does: reads data, prints analysis results, and
          generates a progression plot
    """
    teams = read_data(DATA_FILE)
    sep = SEPARATOR
    print_section("Problem 2.1",
                  f"Team with most goals: "
                  f"{get_team_with_most_goals(teams)}",
                  sep)
    print_section("Problem 2.2",
                  f"Mean: "
                  f"{get_mean_season_goals(teams)}",
                  sep)
    print_section("Problem 2.3",
                  f"Variance: "
                  f"{get_variance_season_goals(teams)}",
                  sep)
    print_section("Problem 2.4",
                  f"Std dev: "
                  f"{get_standard_deviation_season_goals(teams)}",
                  sep)
    arsenal_median = get_median_for_team("Arsenal", teams)
    print_section("Problem 2.5",
                  f"Arsenal median goals: "
                  f"{arsenal_median}",
                  sep)
    spurs_mode = get_mode_for_team("Tottenham Hotspur",
                                   teams)
    print_section("Problem 2.6",
                  f"Tottenham mode goals: "
                  f"{spurs_mode}",
                  sep)
    print_section("Problem 2.7",
                  f"Most consistent team: "
                  f"{get_most_consistent_team(teams)}",
                  sep)
    print_section("Problem 2.8",
                  f"Longest streak overall: "
                  f"{get_longest_streak_team(teams)}",
                  sep)
    most_improved = get_most_improved_mean_goals_team(teams)
    print_section("Problem 2.9",
                  f"Team with best improvement: "
                  f"{most_improved}",
                  sep)
    plot_teams(teams)


if __name__ == "__main__":
    main()
Editor is loading...
Leave a Comment