'Calculating probability of a Fantasy League in Python

Inspired by some projects, I have decided to work on a calculator project based on Python.

Essentially, I have 5 teams in a fantasy league, with points assigned to these teams based on their current standings. Teams A-E.

Assuming the league has 10 more matches to be played, my main aim is to calculate the probability that a team makes it to the top 3 in their league given the matches have a 33.3% of either going: A win to the team (which adds 2 points to the winning team) A lose to the team (which awards 0 points to the losing team) A draw (which awards 1 point to both teams)

This also in turn means there will be 3^10 outcomes for the 10 matches to be played.

For each of these 3^10 scenarios, I will also compute how the final points table will look, and from there, I will be able to sort and figure out which are the top 3 teams in the fantasy league.

I've worked halfway through the project, as shown:

Points = { "A":12, "B":14, "C":8, "D":12, "E":6}  #The current standings
RemainingMatches = [
A:B
B:D
C:E
A:E
D:C
B:D
C:D
A:E
C:E
D:C
]

n=len(RemainingMatches) # Number of matches left
combinations = pow(3,n) # Number of possible scenarios left assumes each game has 3 outcomes
print( "Number of remaining matches = %d" % n )
print( "Number of possible scenarios = %d" % combinations )

for i in range(0,combinations)
    ... 
    for i in range(0,n)

I am currently wondering how do I get these possible combinations to match a certain scenario? For example, when i = 0, it points to the first matchup where A wins, B losses. Hence, Points[A] = Points[A] + 2 etc. I know there will be a nested loop since I have to consider the other matches too. But how do I exactly map each scenario, and then nest it? Apologies if I am being unclear here, struggling with this part at the moment.

Thinking Process:

3 outcomes per game.

for i to combinations:
    #When i =1,  means A win/B lost?
    #When i =2, means B win/A lost?
    #When i =3, means A/B drew?
    for i to n:
        #Go to next match?

Not exactly sure what is the logic flow for this kind of scenario. Thank you.



Solution 1:[1]

Here is a different way to write the code. If you knew in advance the outcome of each of the ten remaining games, you could compute which teams would finish in top three. This is done with the play_out function, which takes current standings, remaining games, and the known outcomes of future games.

Now all that remains is to loop over all possible future outcomes and tally the winning probabilities. This is done in the simulate_league function that takes in current standings and remaining games, and returns a probability that a given team finishes in top 3.

There may be situations where two teams are tied for the third place. In cases like this, the code below allows for four teams or more to be in "top 3". To use a different rule, you can change the top3scores = nlargest(3, set(pts.values())) line.

In terms of useful Python functions, itertools.product is used to generate all possible win/draw/loss outcomes, and heapq.nlargest is used to find the largest 3 scores out of a bunch. The collections.Counter class is used to count the number of possibilities in which a given team finishes in top 3.

from itertools import product
from heapq import nlargest
from collections import Counter

Points = {"A":12, "B":14, "C":8, "D":12, "E":6} # The current standings

RemainingMatches = ["A:B","B:D","C:E","A:E","D:C","B:D","C:D","A:E","C:E","D:C"]
# reformat remaining matches
RemainingMatches = [tuple(s.split(":")) for s in RemainingMatches]

def play_out(points, remaining_matches, winloss_schedule):
  pts = points.copy()
  # simulate remaining games given predetermine win/loss/draw outcomes 
  # given by winloss_schedule
  for (team_a, team_b), outcome in zip(remaining_matches, winloss_schedule):
    if outcome == -1:
      pts[team_a] += 2
    elif outcome == 0:
      pts[team_a] += 1
      pts[team_b] += 1
    else:
      pts[team_b] += 2
  # top 3 scores (allows for ties)
  top3scores = nlargest(3, set(pts.values()))
  return {team: score in top3scores for team, score in pts.items()}

def simulate_league(points, remaining_matches):
  top3counter = Counter()
  for winloss_schedule in product([-1, 0, 1], repeat=len(remaining_matches)):
    top3counter += play_out(points, remaining_matches, winloss_schedule)
  total_possibilities = 3 ** len(remaining_matches)
  return {team: top3count / total_possibilities
          for team, top3count in top3counter.items()}

# simulate_league(Points, RemainingMatches)
# {'A': 0.9293637487510372,
#  'B': 0.9962573455943369,
#  'C': 0.5461057765584515,
#  'D': 0.975088485833799,
#  'E': 0.15439719554945894}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 hilberts_drinking_problem