here is the code. Print "UPSET" after each ()-> print line if it was an upset

import argparse
import csv
import os
from collections import Counter
from typing import List

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

PARENT\_DIR = os.path.dirname(os.path.realpath(\_\_file\_\_))

HISTORICAL\_SEED\_WIN\_RATES = {
 (1, 16): 1.000,
 (2, 15): 0.917,
 (3, 14): 0.833,
 (4, 13): 0.750,
 (5, 12): 0.667,
 (6, 11): 0.583,
 (7, 10): 0.583,
 (8, 9): 0.500,
}
@dataclasses.dataclass
class Team:
 team\_name: str
 team\_seed: int
 ken\_pom\_score: float

 @classmethod
 def extract\_teams(cls, file\_path: str):
 with open(os.path.join(PARENT\_DIR, file\_path), "r", newline="") as csvfile:
 return [cls(
 team\_name=row["team"],
 team\_seed=int(row["seed"]),
 ken\_pom\_score=float(row["score"])
 ) for row in (csv.DictReader(csvfile))]
class Tournament:

 def \_\_init\_\_(self, teams: List[Team], num\_simulations: int):
 self.teams: List[Team] = teams
 self.num\_simulations = num\_simulations
 self.k = self.find\_best\_k()
 self.adj\_matrix = self.calculate\_adj\_matrix()

 @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)}")
 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.num\_simulations)
 updated\_tournament.run()

 @staticmethod
 def historical\_upset\_rate(seed1, seed2):
 if seed1 < seed2:
 lower\_seed, higher\_seed = seed1, seed2
 else:
 lower\_seed, higher\_seed = seed2, seed1
 return 1 - HISTORICAL\_SEED\_WIN\_RATES.get((lower\_seed, higher\_seed), 0.5)

 def calculate\_upset\_rates(self):
 upset\_rates = {}
 for i in range(0, len(self.teams), 2):
 seed1, seed2 = self.teams[i].team\_seed, self.teams[i + 1].team\_seed
 historical\_upset\_rate = self.historical\_upset\_rate(seed1, seed2)
 upset\_rates[(i, i + 1)] = historical\_upset\_rate
 return upset\_rates

 def select\_winner(self, i, round\_win\_counts, upset\_rates):
 # Calculate the probability of an upset based on historical seed win rates
 prob\_upset = upset\_rates[(i, i + 1)]

 # If a random number is less than the probability of an upset, return the underdog
 if np.random.rand() < prob\_upset:
 i\_is\_better\_team = self.teams[i].ken\_pom\_score > self.teams[i + 1].ken\_pom\_score
 index = i if i\_is\_better\_team else i + 1
 # Otherwise, return the favorite
 else:
 index = i if round\_win\_counts[i] > round\_win\_counts[i + 1] else i + 1

 return index

 def play\_round(self):
 round\_win\_counts = Counter()
 num\_upsets = 0
 num\_games = 0

 # Calculate upset rates for each matchup based on historical seed win rates
 upset\_rates = self.calculate\_upset\_rates()

 for \_ in range(int(self.num\_simulations \* 64 / len(self.teams))):
 for i in range(0, len(self.teams), 2):
 win\_i = np.random.rand() < self.adj\_matrix[i, i + 1]
 winning\_team\_index = i if win\_i else i + 1
 round\_win\_counts[winning\_team\_index] += 1

 i\_underdog = self.teams[i].team\_seed < self.teams[i + 1].team\_seed
 j\_underdog = self.teams[i].team\_seed > self.teams[i + 1].team\_seed

 underdog\_i\_won = i\_underdog and win\_i
 underdog\_j\_won = j\_underdog and not win\_i

 num\_upsets += 1 if underdog\_i\_won or underdog\_j\_won else 0
 num\_games += 1

 winning\_team\_indices = []

 for i in range(0, len(self.teams), 2):
 index = self.select\_winner(i, round\_win\_counts, upset\_rates)
 print(f"({self.teams[i].team\_name}, {self.teams[i + 1].team\_name})->{self.teams[index].team\_name}")
 winning\_team\_indices.append(index)

 winning\_teams: list[Team] = [self.teams[winner\_index] for winner\_index in winning\_team\_indices]

 # Print upset rate for the current round
 print(f"Upset rate for this round: {num\_upsets / num\_games:.2%}")

 return winning\_teams

 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\_difference):
 error = 0
 for matchup, historical\_probability in HISTORICAL\_SEED\_WIN\_RATES.items():
 difference = average\_kenpom\_difference[matchup]
 probability = 1 / (1 + exp(-k \* difference))
 error += (probability - historical\_probability) \*\* 2
 return error

 @staticmethod
 def average\_kenpom\_difference(max\_seed=16, kenpom\_range=(0, 40)):
 min\_kenpom, max\_kenpom = kenpom\_range
 kenpom\_increment = (max\_kenpom - min\_kenpom) / max\_seed
 average\_difference = {}

 for higher\_seed in range(1, max\_seed + 1):
 for lower\_seed in range(higher\_seed + 1, max\_seed + 1):
 higher\_seed\_kenpom = max\_kenpom - (higher\_seed - 1) \* kenpom\_increment
 lower\_seed\_kenpom = max\_kenpom - (lower\_seed - 1) \* kenpom\_increment
 average\_difference[(higher\_seed, lower\_seed)] = higher\_seed\_kenpom - lower\_seed\_kenpom

 return average\_difference

 @staticmethod
 def find\_best\_k():
 average\_difference = Tournament.average\_kenpom\_difference()
 result = minimize\_scalar(Tournament.error\_function, args=(average\_difference,))
 x = result.x
 print("Best k:", x)
 return 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(
 '-n', '--num-simulations',
 type=int,
 default=1000,
 help="Number of simulations to run (default: 100000)"
 )
 args = parser.parse\_args()
 teams\_ = Team.extract\_teams(args.file)
 tournament = Tournament(teams\_, args.num\_simulations)
 tournament.run()
코드는 다음과 같습니다. 업셋인 경우 각 ()-> 줄 뒤에 "UPSET"을 인쇄합니다.import argparseimport csvimport os컬렉션에서 가져 오기 카운터입력에서 임포트 목록데이터 클래스 임포트numpy를 np로 임포트numpy.ma에서 exp 임포트scipy.optimize에서 가져 오기 최소화\_스칼라import mathPARENT\_DIR = os.path.dirname(os.path.realpath(\_\_file\_\_))historical\_seed\_win\_rates = { (1, 16): 1.000, (2, 15): 0.917, (3, 14): 0.833, (4, 13): 0.750, (5, 12): 0.667, (6, 11): 0.583, (7, 10): 0.583, (8, 9): 0.500,}@dataclasses.dataclass팀 클래스: team\_name: str 팀\_씨앗: int ken\_pom\_score: float @classmethod def extract\_teams(cls, file\_path: str): with open(os.path.join(PARENT\_DIR, file\_path), "r", newline="") as csvfile: return [cls( team\_name=row["team"], team\_seed=int(row["seed"]), ken\_pom\_score=float(row["score"]) ) for row in (csv.DictReader(csvfile))]토너먼트 클래스: def \_\_init\_\_(self, teams: List[Team], num\_simulations: int): self.teams: List[Team] = teams self.num\_simulations = num\_simulations self.k = self.find\_best\_k() self.adj\_matrix = self.calculate\_adj\_matrix() @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"\n{len(self.teams)}의 라운드") len(self.teams) == 0: print("토너먼트에 참가팀이 없습니다. 종료합니다.") 반환 len(self.teams) == 1: print(f"우승자: {self.teams[0].team\_name}") 반환 winners = self.play\_round() updated\_tournament = 토너먼트(우승자, self.num\_simulations) updated\_tournament.run() 정적 메서드 def historical\_upset\_rate(seed1, seed2): if seed1 < seed2: lower\_seed, higher\_seed = seed1, seed2 else: LOWER\_SEED, HIGHER\_SEED = SEED2, SEED1 반환 1 - HISTORICAL\_SEED\_WIN\_RATES.get((lower\_seed, higher\_seed), 0.5) def calculate\_upset\_rates(self): upset\_rates = {} for i in range(0, len(self.teams), 2): seed1, seed2 = self.teams[i].team\_seed, self.teams[i + 1].team\_seed historical\_upset\_rate = self.historical\_upset\_rate(seed1, seed2) upset\_rates[(i, i + 1)] = historical\_upset\_rate 반환 업셋\_율 def select\_winner(self, i, round\_win\_counts, upset\_rates): # 과거 시드 승률을 기반으로 업셋 확률을 계산합니다. prob\_upset = upset\_rates[(i, i + 1)] # 난수가 역전 확률보다 작으면 약자를 반환합니다. if np.random.rand() < prob\_upset: i\_is\_better\_team = self.teams[i].ken\_pom\_score > self.teams[i + 1].ken\_pom\_score index = i if i\_is\_better\_team else i + 1 # 그렇지 않으면 즐겨찾기를 반환합니다. else: index = i if round\_win\_counts[i] > round\_win\_counts[i + 1] else i + 1 인덱스 반환 def play\_round(self): round\_win\_counts = Counter() num\_upsets = 0 num\_games = 0 # 과거 시드 승률을 기반으로 각 매치업에 대한 업셋 비율을 계산합니다. upset\_rates = self.calculate\_upset\_rates() for \_ in range(int(self.num\_simulations \* 64 / len(self.teams))): for i in range(0, len(self.teams), 2):: win\_i = np.random.rand() < self.adj\_matrix[i, i + 1] winning\_team\_index = i if win\_i else i + 1 round\_win\_counts[winning\_team\_index] += 1 i\_언더독 = self.teams[i].team\_seed < self.teams[i + 1].team\_seed j\_underdog = self.teams[i].team\_seed > self.teams[i + 1].team\_seed underdog\_i\_원 = i\_언더독 및 승리\_i underdog\_j\_원 = j\_언더독이고 승리하지 않음\_i 언더독\_i\_원 또는 언더독\_j\_원일 경우 num\_upsets += 1 그렇지 않으면 0 num\_games += 1 winning\_team\_indices = [] for i in range(0, len(self.teams), 2): index = self.select\_winner(i, round\_win\_counts, upset\_rates) print(f"({self.teams[i].team\_name}, {self.teams[i + 1].team\_name})->{self.teams[index].team\_name}") winning\_team\_indices.append(index) winning\_teams: list[Team] = [self.teams[winner\_index] for winner\_index in winning\_team\_indices] # 현재 라운드의 업셋 비율을 인쇄합니다. print(f"이번 라운드의 역전율: {num\_upsets / num\_games:.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\_difference): error = 0 매치업의 경우, HISTORICAL\_SEED\_WIN\_RATES.items()의 historical\_probability: 차이 = 평균\_켄폼\_차이[매치업] 확률 = 1 / (1 + exp(-)