write a loop in main that runs 1000 iterations counting the frequency of win for each return value for tourney winner

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, temperature: float = None):
 self.teams: List[Team] = teams
 self.temperature = temperature if temperature is not None else self.find\_temperature\_using\_least\_squares()
 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 None

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

 winners = self.play\_round()
 updated\_tournament = Tournament(winners, self.noise, self.temperature)
 return 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.temperature \* ken\_pom\_diff))
 return probability

 def average\_kenpom\_differences(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\_temperature\_using\_least\_squares(self):
 average\_difference = self.average\_kenpom\_differences()
 result = minimize\_scalar(Tournament.error\_function, args=average\_difference)
 return result.x

 @staticmethod
 def error\_function(temperature, 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(-temperature \* difference))

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

 return error
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()
메인에서 토너먼트 우승자에 대한 각 반환 값에 대해 승리 빈도를 계산하는 1000번의 반복을 실행하는 루프를 작성합니다.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, temperature: float = None): self.teams: List[Team] = teams self.temperature = temperature가 None이 아닌 경우 온도 그 외에는 self.find\_temperature\_using\_least\_squares() 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: winner = self.teams[0] print(f"우승자: {winner.team\_name}") 우승자 반환 winners = self.play\_round() updated\_tournament = 토너먼트(우승자, self.noise, self.temperature) 반환 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.temperature \* ken\_pom\_diff)) 확률 반환 def average\_kenpom\_differences(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\_diff] += kenpom\_diff kenpom\_diff\_count[seed\_diff] += 1 # 각 시드 차이에 대한 평균 KenPom 차이를 계산합니다. average\_difference = {} for seed\_diff in kenpom\_diff\_sum: average\_difference[seed\_diff] = kenpom\_diff\_sum[seed\_diff] / kenpom\_diff\_count[seed\_diff] 반환 평균\_차이 def find\_temperature\_using\_least\_squares(self): average\_difference = self.average\_kenpom\_differences() 결과 = 최소화\_스칼라(Tournament.error\_함수, args=평균\_difference) re