<jupyter_start><jupyter_text>Analyzing World Series Victories  Learning more about programming in pythonIf you want to learn more about programming in python on your own, I encourage you to go through the first 10-11 lectures of the free online introductory class here:https://github.com/agdelma/IntroCompPhysics.  Notebook BasicsWe can type command directly into the execution cells.  This can act like a simple calculator, or we can add more complex code.  To execute a cell, press the ▶ button on the tool bar, or hit `shift+Enter`. An Interactive Calculator<jupyter_code>1+1<jupyter_output><empty_output><jupyter_text>Your First ProgramWriting a `hello world` program is one line!<jupyter_code>print('Hello World')<jupyter_output><empty_output><jupyter_text>AssignmentThe assignment operator in Python is =. Python is a dynamically typed language, so we do not need to specify the type of a variable when we create one.<jupyter_code>answer = input('Enter some exciting text: ')
print(f'Your exciting text was: {answer}')<jupyter_output><empty_output><jupyter_text>Loops and ControlThese are the building blocks of any program in python. We can make the computer repeat things or perform actions based on the values of variables<jupyter_code>num_repeat = input('How many times should I repeat your exciting text? ')

if num_repeat.isnumeric():
    for n in range(int(num_repeat)):
        print(answer)
else:
    print("You didn't enter a valid number!")<jupyter_output><empty_output><jupyter_text>We are ready to begin our analysis of World Series Data!We need to import some libraries to perform our analysis.  Libraries are software written by someone else that you can build programs out of!<jupyter_code>import pandas as pd
from scipy.special import binom
import matplotlib.pyplot as plt
import numpy as np

red,lime,blue,green,purple = "#CF5970","#94B848","#878CC0","#5EBAA1","#BD5DC7"
%config InlineBackend.figure_formats = ['svg']<jupyter_output><empty_output><jupyter_text>Take a peak at the World Series Data<jupyter_code>ws = pd.read_csv('world_series_data.csv')
ws.tail(10)<jupyter_output><empty_output><jupyter_text>Distribution of wins by team: American League<jupyter_code>plt.figure(figsize=(10,5))
h = ws["American League Team"].value_counts().plot.bar(color=red)
h.set_ylabel('World Series Appearances')
h.set_title('American League')<jupyter_output><empty_output><jupyter_text>Distribution of wins by team: National League<jupyter_code>plt.figure(figsize=(10,5))
h = ws["National League Team"].value_counts().plot.bar(color=blue)
h.set_ylabel('World Series Appearances')
h.set_title('National League')<jupyter_output><empty_output><jupyter_text>Are teams from one league better than the other?The *probability of winning* based on the available data is given by:\begin{equation}P(\text{League Win}) = \frac{\text{Num. of Wins}}{\text{Total Num. Games}}\end{equation}<jupyter_code>total_number_games = ws["Total Games"].sum()
al_wins = ws["AL Wins"].sum()
nl_wins = ws["NL Wins"].sum()

print(f'AL Win Probability: {100*al_wins/total_number_games:.2f}%')
print(f'NL Win Probability: {100*nl_wins/total_number_games:.2f}%')<jupyter_output><empty_output><jupyter_text>Remember, probability is the likliehood **on average** of a given outcome occuring.  It seems that the American League is slightly better.  Any ideas why?  What is the probability of each World Series outcome?In a *best of seven* series there are 4 possible outcomes:* 4-0 (sweep)* 4-1* 4-2* 4-3These correspond to 4, 5, 6, and 7 games respectively.Each of these outcomes could happen in 2 distinct ways if we neglect ordering, *i.e.* either the American League or National Team could win the world series. We can quantify this by counting how may times each of the oucomes appears and dividing by the total number of series. The result is called a **probability histogram**<jupyter_code>plt.figure(figsize=(10,5))
h = ws['Total Games'].hist(range=[4,7],bins=[4,5,6,7,8],align='left',rwidth=0.9, density=True, color=green)
plt.xticks(np.arange(4,8), ('4-0', '4-1', '4-2', '4-3') )

# add a title to the current figure, our histogram
h.set_title("Outcome of World Series")
h.set_xlabel("Series Score")
h.set_ylabel("Probability")

# record the data for future plotting
ws_actual,binEdges = np.histogram(ws['Total Games'],bins=4, density=True)
ws_actual_prob = pd.Series(ws_actual, index=['4-0','4-1','4-2','4-3'], name='World Series')<jupyter_output><empty_output><jupyter_text>Questions * Can we undertsand this distribution? * Why do most series go to the full 7 games? * Why is 4-0 (a sweep) the most unlikely outcome?  Let's simplify this problem and analyze/collect our own data for a *model* system Activity: Odds-n-Evens 1. Get into groups of 3 (2 active players 1 umpire) 2. There are 3 possible *series* per group  3. For each series, decide who is American League (**EVENS**) and who is National League (**ODDS**) 3. Play **best 4 out of 7** games of O/E.  5. The umpire needs to record (1) the **winner (A or N)** and (2) the **length** of the series (4, 5, 6, or 7 games).<jupyter_code>oe_game_lengths = [4]
oe_game_winners = ['E']

data = {'Series Length':oe_game_lengths, 'Winners':oe_game_winners}
odds_evens = pd.DataFrame(data)
plt.figure(figsize=(10,5))
h = odds_evens['Series Length'].hist(range=[4,7],bins=[4,5,6,7,8],align='left',rwidth=0.9, density=True, color=green)
plt.xticks(np.arange(4,8), ('4-0', '4-1', '4-2', '4-3') )

h.set_title("Outcome of Odds/Evens Series")
h.set_xlabel("Series Score")
h.set_ylabel("Probability")

# Record the probabilities
oe_actual,binEdges = np.histogram(odds_evens['Series Length'],bins=4, density=True)
oe_actual_prob = pd.Series(oe_actual, index=['4-0','4-1','4-2','4-3'], name='Odds & Evens')
print(oe_actual_prob,'\n')
print(ws_actual_prob)<jupyter_output><empty_output><jupyter_text>Is there anything special about the American vs. National League in our Data?<jupyter_code>h = odds_evens['Winners'].value_counts().plot.bar(color=blue,xlabel='League', ylabel='Number of Wins')<jupyter_output><empty_output><jupyter_text>Question: How can we compute the probability and see if it agrees with our experiment?<!--**Answer**: First enumerate the number of outcomes in each case:  * 4-0: AAAA, NNNN  * 4-1: NAAAA, ANAAA, AANAA, AAANA -- One party needs to win **at most** 1 out of the first 4 games-->  We can write code to enumerate these for us!<jupyter_code>import itertools as it
outcome,prob,combos = {},{},{}<jupyter_output><empty_output><jupyter_text>4-0 Sweep<jupyter_code>outcome['4-0'] = ["AAAA","NNNN"]<jupyter_output><empty_output><jupyter_text>4-1<jupyter_code># AL wins 4-1
score = set(it.permutations("AAAN"))
outcome['4-1'] = [ "".join(c) + "A" for c in score]

# NL wins 4-1
score = set(it.permutations("NNNA"))
outcome['4-1'] += [ "".join(c) + "N" for c in score]<jupyter_output><empty_output><jupyter_text>4-2<jupyter_code># AL wins 4-2
score = set(it.permutations("AAANN"))
outcome['4-2'] = [ "".join(c) + "A" for c in score]

# NL wins 4-2
score = set(it.permutations("NNNAA"))
outcome['4-2'] += [ "".join(c) + "N" for c in score]<jupyter_output><empty_output><jupyter_text>4-3<jupyter_code># AL wins 4-3
score = set(it.permutations("AAANNN"))
outcome['4-3'] = [ "".join(c) + "A" for c in score]

# NL wins 4-3
score = set(it.permutations("NNNAAA"))
outcome['4-3'] += [ "".join(c) + "N" for c in score]<jupyter_output><empty_output><jupyter_text>Now let's determine the probabilities * We have to remember that the probability to win a single game is equal to 1/2 * Therefore, the probability of given outcome is equal to the number of ways for that score to happen, divided by: $2^{\text{number of games}}$  List all the posibile combinations<jupyter_code>for score,out in outcome.items():
    combos[score] = pd.DataFrame({score:out})
combos['4-0']
combos['4-1']
combos['4-2']
combos['4-3']<jupyter_output><empty_output><jupyter_text>To determine the probability:$$ \text{Prob(outcome)} = \frac{\text{number outcomes}}{2^{\text{number games}}} $$<jupyter_code>for score,out in outcome.items():
    prob[score] = 1.0*len(out)/(2**(len(out[0])))
exact_prob = pd.Series(prob, name='Exact Prob.')<jupyter_output><empty_output><jupyter_text>Let's compare the exact probability we have measured, with the observed<jupyter_code>exact_prob
ws_actual_prob
oe_actual_prob<jupyter_output><empty_output><jupyter_text>These look pretty close, let's compare on a plot<jupyter_code>plt.figure(figsize=(10,5))
exact_prob.plot.bar(alpha=1, color=red, label='Exact', width=0.25, position=0.5)
ws_actual_prob.plot.bar(alpha=0.75, color=green, label='World Series', width=0.25, position=0)
oe_actual_prob.plot.bar(alpha=0.75, color=blue, label='Odds/Evens',width=0.25, position=1)

plt.title("Outcome of World Series")
plt.xlabel("Game Score")
plt.ylabel("Probability")
plt.legend(loc='upper left', fontsize=15)<jupyter_output><empty_output><jupyter_text>Success!  It turns out, this is an example of the binomial probability distribution for the special case where each of the two outcomes is equally likely.The binomial distribution is the discrete probability distribution of the number of successes in a sequence of n independent yes/no experiments, each of which yields success with probability p.See more: http://en.wikipedia.org/wiki/Binomial_distribution $$P_b(n,k) = \binom{n}{k} \frac{1}{2^n}$$ Let's try to reproduce our previous calculation.  In our case, we will use the binomial probability for the first $n-1$ games of a $n$ game series, then multiply by $1/2$ the probability of our team winning the final game and finally, multiply the whole thing by $2$ as we don't care whether the AL or NL wins.  Thus, our probability is:$$P(a,0) = P_b(a,0) \times 2 $$$$P(a,b>0) = P_b(a+b-1,3) \times \frac{1}{2} \times 2 $$<jupyter_code>def P(a,b):
    ''' The probability for a series outcome a-b. '''
    if b == 0:
        return (binom(a,b)/2**a) * 2
    else:
        return binom(a+b-1,3)/2**(a+b-1)
exact_prob
for b in range(4):
    print (f'P(4-{b}) = {P(4,b)}')
import scipy,matplotlib
print(np.__version__)
print(scipy.__version__)
print(pd.__version__)
print(matplotlib.__version__)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13689072327044025, 'fraction_numerical': 0.023781446540880505, 'mean_word_length': 6.151791988756149, 'pattern_counts': {'":': 0, '<': 90, '<?xml version=': 0, '>': 91, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '22', 'dir': 'jupyter-structured-clean-dedup', 'id': '450377', 'license': 'MIT', 'n_tokens_mistral': 3618, 'n_tokens_neox': 3447, 'n_words': 1040, 'path': 'World_Series_Analysis.ipynb', 'repo_name': 'agdelma/worldseries', 'size': '10176'}