how can this code be cleaned? just tell me the changes one at a time
import argparse
import math
from collections import defaultdict
from typing import List

import numpy as np
from numpy.ma import exp
from scipy.optimize import minimize\_scalar

from team import Team

HISTORICAL\_SEED\_WIN\_RATES = {
 15: 1.000,
 14: 0.9585,
 13: 0.917,
 12: 0.875,
 11: 0.833,
 10: 0.7915,
 9: 0.750,
 8: 0.7085,
 7: 0.667,
 6: 0.625,
 5: 0.583,
 4: 0.5615,
 3: 0.54,
 2: 0.52,
 1: 0.500,
 0: 0.500,
}
class Tournament:

 def \_\_init\_\_(self, teams: List[Team], noise: float, k: float = None):
 self.teams: List[Team] = teams
 self.k = k if k is not None else self.find\_best\_k()
 self.adj\_matrix = self.calculate\_adj\_matrix()
 self.noise = noise

 @staticmethod
 def get\_opponent\_index(team\_index):
 return team\_index + 1 if team\_index % 2 == 0 else team\_index - 1

 def calculate\_adj\_matrix(self):
 num\_teams = len(self.teams)
 adj\_matrix = np.zeros((num\_teams, num\_teams))

 for i, team\_i in enumerate(self.teams):
 for j, team\_j in enumerate(self.teams):
 if i != j:
 p\_win = self.calculate\_win\_probability(team\_i, team\_j)
 adj\_matrix[i, j] = p\_win
 adj\_matrix[j, i] = 1 - p\_win

 return adj\_matrix

 def run(self):
 print(f"\nRound of {len(self.teams)}")
 print("teams in round: ", [
 f"{x.team\_name} ({x.team\_seed})"
 for x in self.teams
 ])
 if len(self.teams) == 0:
 print("No teams in the tournament. Exiting.")
 return

 if len(self.teams) == 1:
 print(f"Winner: {self.teams[0].team\_name}")
 return

 winners = self.play\_round()
 updated\_tournament = Tournament(winners, self.noise, self.k)
 updated\_tournament.run()

 @staticmethod
 def historical\_upset\_rate(seed1, seed2):
 return 1 - Tournament.get\_midpoint\_win\_rate(seed1, seed2)

 @staticmethod
 def get\_midpoint\_win\_rate(seed1, seed2):
 lower, higher = sorted((seed1, seed2))
 return HISTORICAL\_SEED\_WIN\_RATES.get(higher - lower)

 def select\_winner\_simple(self, home: Team, away: Team):
 home\_seed, away\_seed = home.team\_seed, away.team\_seed
 historical\_upset\_rate = self.historical\_upset\_rate(home\_seed, away\_seed)
 home\_is\_better = home.would\_upset(away)
 better\_team = home if home\_is\_better else away
 worse\_team = away if home\_is\_better else home

 statistical = self.calculate\_win\_probability(worse\_team, better\_team)

 # Noise is added by using historical upset rates rather than team specific KenPom scores
 probability = (1 - self.noise) \* statistical + self.noise \* historical\_upset\_rate

 # If a random number is less than the probability of an upset, return the underdog
 if np.random.rand() < probability:
 return worse\_team
 # Otherwise, return the favorite
 else:
 return better\_team

 def play\_round(self):
 winners = []
 realized\_upsets = 0

 for i in range(0, len(self.teams), 2):
 home = self.teams[i]
 away = self.teams[i + 1]
 winner = self.select\_winner\_simple(home, away)
 loser = home if winner == away else away
 is\_upset = winner.would\_upset(loser)
 realized\_upsets += 1 if is\_upset else 0
 winners += [winner]

 if is\_upset:
 expected\_edge = winner.is\_better\_kenpom(loser)
 print(f"{winner.team\_name}({winner.team\_seed}) "
 f"over {loser.team\_name}({loser.team\_seed}) "
 f"{'' if expected\_edge else 'UNEXPECTED'}")

 print(f"Upset rate for this round: {realized\_upsets / len(winners):.2%}")

 return winners

 def get\_team\_by\_name(self, team\_name: str):
 for team in self.teams:
 if team.team\_name == team\_name:
 return team
 return None

 def calculate\_win\_probability(self, team\_i: Team, team\_j: Team):
 ken\_pom\_diff = team\_i.ken\_pom\_score - team\_j.ken\_pom\_score
 probability = 1 / (1 + math.exp(-self.k \* ken\_pom\_diff))
 return probability

 @staticmethod
 def error\_function(k, average\_kenpom\_differences):
 error = 0
 for seed\_difference, historical\_probability in HISTORICAL\_SEED\_WIN\_RATES.items():
 # Get the historical probability based on the seed difference
 historical\_probability = HISTORICAL\_SEED\_WIN\_RATES[seed\_difference]

 # Calculate the probability based on the KenPom difference and the given k
 difference = average\_kenpom\_differences[seed\_difference]
 probability = 1 / (1 + exp(-k \* difference))

 # Add the squared error between the calculated probability and historical probability
 error += (probability - historical\_probability) \*\* 2

 return error

 from collections import defaultdict

 def average\_kenpom\_difference(self):
 # Initialize a dictionary to store the sum of KenPom differences and counts for each seed difference
 kenpom\_diff\_sum = defaultdict(float)
 kenpom\_diff\_count = defaultdict(int)

 # Loop through all possible matchups between teams
 for i, home in enumerate(self.teams):
 for away in self.teams[i + 1:]:
 seed\_diff = abs(home.team\_seed - away.team\_seed)
 kenpom\_diff = abs(home.ken\_pom\_score - away.ken\_pom\_score)

 # Update the sum of KenPom differences and counts for the seed difference
 kenpom\_diff\_sum[seed\_diff] += kenpom\_diff
 kenpom\_diff\_count[seed\_diff] += 1

 # Calculate the average KenPom difference for each seed difference
 average\_difference = {}
 for seed\_diff in kenpom\_diff\_sum:
 average\_difference[seed\_diff] = kenpom\_diff\_sum[seed\_diff] / kenpom\_diff\_count[seed\_diff]

 return average\_difference

 def find\_best\_k(self):
 average\_difference = self.average\_kenpom\_difference()
 result = minimize\_scalar(Tournament.error\_function, args=(average\_difference,))
 return result.x
if \_\_name\_\_ == "\_\_main\_\_":
 parser = argparse.ArgumentParser(
 description="NCAA Tournament Simulation"
 )
 parser.add\_argument(
 '-f', '--file',
 default='2023ncaab.csv',
 help="Path to the data file (default: '2023ncaab.csv')"
 )
 parser.add\_argument(
 '-z', '--noise',
 type=int,
 default=0.5
 )
 args = parser.parse\_args()
 teams\_ = Team.extract\_teams(args.file)
 tournament = Tournament(teams\_, args.noise)
 tournament.run()
이 코드를 어떻게 정리할 수 있습니까? 한 번에 하나씩 변경 사항을 알려주세요.import argparseimport math컬렉션에서 기본 딕셔너리 가져 오기입력에서 임포트 목록numpy를 np로 임포트numpy.ma에서 exp 임포트scipy.optimize에서 가져 오기 최소화\_스칼라팀에서 팀 가져 오기historical\_seed\_win\_rates = { 15: 1.000, 14: 0.9585, 13: 0.917, 12: 0.875, 11: 0.833, 10: 0.7915, 9: 0.750, 8: 0.7085, 7: 0.667, 6: 0.625, 5: 0.583, 4: 0.5615, 3: 0.54, 2: 0.52, 1: 0.500, 0: 0.500,}토너먼트 클래스를 만듭니다: def \_\_init\_\_(self, teams: List[Team], noise: float, k: float = None): self.teams: List[Team] = teams self.k = k k가 None이 아닌 경우 self.find\_best\_k() self.adj\_matrix = self.calculate\_adj\_matrix() self.noise = noise @staticmethod def get\_opponent\_index(team\_index): return team\_index + 1 if team\_index % 2 == 0 else team\_index - 1 def calculate\_adj\_matrix(self): num\_teams = len(self.teams) adj\_matrix = np.zeros((num\_teams, num\_teams)) for i, team\_i in enumerate(self.teams): for j, team\_j in enumerate(self.teams): IF I != J: p\_win = self.calculate\_win\_probability(team\_i, team\_j) adj\_matrix[i, j] = p\_win adj\_matrix[j, i] = 1 - p\_win 반환 adj\_matrix def run(self): print(f"\nRound of {len(self.teams)}") print("라운드에 들어간 팀들: ", [ f"{x.team\_name} ({x.team\_seed})" for x in self.teams ]) len(self.teams) == 0: print("토너먼트에 참가할 팀이 없습니다. 종료합니다.") 반환 len(self.teams) == 1: print(f"우승자: {self.teams[0].team\_name}") 반환 winners = self.play\_round() updated\_tournament = 토너먼트(winners, self.noise, self.k) updated\_tournament.run() 정적 메서드 def historical\_upset\_rate(seed1, seed2): 반환 1 - Tournament.get\_midpoint\_win\_rate(seed1, seed2) 정적 메서드 def get\_midpoint\_win\_rate(seed1, seed2): lower, higher = sorted((seed1, seed2)) return HISTORICAL\_SEED\_WIN\_RATES.get(higher - lower) def select\_winner\_simple(self, 홈: 팀, 원정: 팀): HOME\_SEED, AWAY\_SEED = HOME.TEAM\_SEED, AWAY.TEAM\_SEED historical\_upset\_rate = self.historical\_upset\_rate(home\_seed, away\_seed) home\_is\_better = home.would\_upset(away) 더 나은\_팀 = 홈\_이\_더 나은 경우 홈, 그렇지 않으면 원정 worse\_team = 홈\_이\_더 나은 경우 원정 그렇지 않으면 홈 statistical = self.calculate\_win\_probability(worse\_team, better\_team) # 팀별 켄폼 점수가 아닌 과거 역전율을 사용하여 노이즈가 추가됩니다. 확률 = (1 - self.noise) \* 통계 + self.noise \* historical\_upset\_rate # 난수가 역전 확률보다 작으면 언더독을 반환합니다. np.random.rand() < probability: 반환 더 나쁜\_팀 # 그렇지 않으면, 우세를 반환합니다. else: 반환 더 나은\_팀 def play\_round(self): winners = [] realized\_upsets = 0 for i in range(0, len(self.teams), 2): home = self.teams[i] away = self.teams[i + 1] 승자 = self.select\_winner\_simple(홈, 어웨이) 패자 = 승자가 원정팀이면 홈, 그렇지 않으면 원정팀 is\_upset = winner.would\_upset(loser) 실현된\_upset += 1 is\_upset이면 1 그렇지 않으면 0 승자 += [승자] if is\_upset: expected\_edge = winner.is\_better\_kenpom(loser) print(f"{승자.팀\_이름}({승자.팀\_시드}) " f"over {loser.team\_name}({loser.team\_seed}) " f"{'' if expected\_edge else 'UNEXPECTED'}") print(f"이번 라운드의 업셋 비율: {realized\_upsets / len(winners):.2%}") 승자 반환 def get\_team\_by\_name(self, team\_name: str): for team in self.teams: if team.team\_name == team\_name: 반환 팀 반환 없음 def calculate\_win\_probability(self, team\_i: 팀, team\_j: 팀): ken\_pom\_diff = team\_i.ken\_pom\_score - team\_j.ken\_pom\_score probability = 1 / (1 + math.exp(-self.k \* ken\_pom\_diff)) 확률 반환 정적 메서드 def error\_function(k, average\_kenpom\_differences): error = 0 HISTORICAL\_SEED\_WIN\_RATES.items()의 seed\_difference, historical\_probability에 대해: # 시드 차이에 따라 과거 확률을 구합니다. historical\_probability = HISTORICAL\_SEED\_WIN\_RATES[seed\_difference] # 켄폼 차이와 주어진 k를 기반으로 확률을 계산합니다. difference = average\_kenpom\_differences[seed\_difference] 확률 = 1 / (1 + exp(-k \* 차이)) # 계산된 확률과 과거 확률 사이의 제곱 오차를 더합니다. error += (확률 - 과거\_확률) \*\* 2 반환 오류 컬렉션에서 기본 딕셔너리 가져 오기 def average\_kenpom\_difference(self): # 각 시드 차이에 대한 KenPom 차이와 카운트의 합계를 저장하기 위해 딕셔너리를 초기화합니다. kenpom\_diff\_sum = defaultdict(float) kenpom\_diff\_count = defaultdict(int) # 팀 간의 모든 가능한 매치업을 반복합니다. for i, home in enumerate(self.teams): for away in self.teams[i + 1:]: seed\_diff = abs(home.team\_seed - away.team\_seed) kenpom\_diff = abs(home.ken\_pom\_score - away.ken\_pom\_score) # KenPom 차이의 합계를 업데이트하고 시드 차이에 대한 카운트를 업데이트합니다. kenpom\_diff\_sum[seed\_di