Created
February 7, 2011 22:24
-
-
Save JeffSackmann/815377 to your computer and use it in GitHub Desktop.
Find win expectancy and volatility given inning, out, base, run situation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Find win expectancy and volatility given inning, out, base, run situation. | |
## no. of runs that score with HR in diff. base situations | |
baseHr = {1: 1, | |
2: 2, | |
3: 2, | |
4: 3, | |
5: 2, | |
6: 3, | |
7: 3, | |
8: 4 | |
} | |
tangoRunExp = {'60': {1: 0.51400000000000001, 2: 0.19400000000000001, 3: 0.14999999999999999, 4: 0.076999999999999999, 5: 0.036999999999999998, 6: 0.017000000000000001, 7: 0.0060000000000000001, 8: 0.0030000000000000001, 9: 0.001, 10: 0.001, 'm': -0.216, 'b': 0.247}, '61': {1: 0.59599999999999997, 2: 0.17599999999999999, 3: 0.13200000000000001, 4: 0.057000000000000002, 5: 0.024, 6: 0.0089999999999999993, 7: 0.0040000000000000001, 8: 0.001, 9: 0.0, 10: 0.0, 'm': -0.11600000000000001, 'b': 0.40600000000000003}, '62': {1: 0.55900000000000005, 2: 0.20599999999999999, 3: 0.158, 4: 0.051999999999999998, 5: 0.017000000000000001, 6: 0.0050000000000000001, 7: 0.002, 8: 0.001, 9: 0.0, 10: 0.0, 'm': -0.19900000000000001, 'b': 0.82799999999999996}, '82': {1: 0.27300000000000002, 2: 0.35499999999999998, 3: 0.17000000000000001, 4: 0.13800000000000001, 5: 0.041000000000000002, 6: 0.014999999999999999, 7: 0.0050000000000000001, 8: 0.001, 9: 0.0, 10: 0.0, 'm': -0.20899999999999999, 'b': 0.78900000000000003}, '80': {1: 0.311, 2: 0.247, 3: 0.17000000000000001, 4: 0.14399999999999999, 5: 0.070999999999999994, 6: 0.031, 7: 0.012999999999999999, 8: 0.0080000000000000002, 9: 0.0030000000000000001, 10: 0.002, 'm': -0.127, 'b': 0.193}, '81': {1: 0.39700000000000002, 2: 0.24399999999999999, 3: 0.151, 4: 0.123, 5: 0.050999999999999997, 6: 0.021000000000000001, 7: 0.0080000000000000002, 8: 0.0030000000000000001, 9: 0.001, 10: 0.0, 'm': -0.14199999999999999, 'b': 0.40200000000000002}, '20': {1: 0.42399999999999999, 2: 0.29899999999999999, 3: 0.14999999999999999, 4: 0.071999999999999995, 5: 0.032000000000000001, 6: 0.012999999999999999, 7: 0.0050000000000000001, 8: 0.002, 9: 0.001, 10: 0.0, 'm': -0.27800000000000002, 'b': 0.71599999999999997}, '21': {1: 0.44400000000000001, 2: 0.32600000000000001, 3: 0.13700000000000001, 4: 0.056000000000000001, 5: 0.021999999999999999, 6: 0.0089999999999999993, 7: 0.0030000000000000001, 8: 0.001, 9: 0.0, 10: 0.0, 'm': -0.26800000000000002, 'b': 0.86299999999999999}, '22': {1: 0.45300000000000001, 2: 0.374, 3: 0.11600000000000001, 4: 0.039, 5: 0.012, 6: 0.0050000000000000001, 7: 0.001, 8: 0.0, 9: 0.0, 10: 0.0, 'm': -0.19400000000000001, 'b': 0.97399999999999998}, '42': {1: 0.49399999999999999, 2: 0.23699999999999999, 3: 0.18099999999999999, 4: 0.059999999999999998, 5: 0.017999999999999999, 6: 0.0070000000000000001, 7: 0.002, 8: 0.001, 9: 0.0, 10: 0.0, 'm': -0.14399999999999999, 'b': 0.84099999999999997}, '40': {1: 0.36199999999999999, 2: 0.25600000000000001, 3: 0.19400000000000001, 4: 0.104, 5: 0.048000000000000001, 6: 0.02, 7: 0.0089999999999999993, 8: 0.0040000000000000001, 9: 0.002, 10: 0.001, 'm': -0.16700000000000001, 'b': 0.45000000000000001}, '41': {1: 0.40100000000000002, 2: 0.25800000000000001, 3: 0.20300000000000001, 4: 0.083000000000000004, 5: 0.034000000000000002, 6: 0.012999999999999999, 7: 0.0050000000000000001, 8: 0.002, 9: 0.001, 10: 0.0, 'm': -0.17399999999999999, 'b': 0.66900000000000004}, '72': {1: 0.185, 2: 0.54800000000000004, 3: 0.16900000000000001, 4: 0.067000000000000004, 5: 0.023, 6: 0.0060000000000000001, 7: 0.002, 8: 0.0, 9: 0.0, 10: 0.0, 'm': -0.095000000000000001, 'b': 0.78500000000000003}, '71': {1: 0.41299999999999998, 2: 0.32800000000000001, 3: 0.13800000000000001, 4: 0.072999999999999995, 5: 0.029000000000000001, 6: 0.010999999999999999, 7: 0.0050000000000000001, 8: 0.002, 9: 0.001, 10: 0.0, 'm': -0.311, 'b': 0.47799999999999998}, '70': {1: 0.315, 2: 0.35599999999999998, 3: 0.16800000000000001, 4: 0.085999999999999993, 5: 0.043999999999999997, 6: 0.017999999999999999, 7: 0.0070000000000000001, 8: 0.0040000000000000001, 9: 0.002, 10: 0.0, 'm': -0.22900000000000001, 'b': 0.26100000000000001}, '11': {1: 0.59999999999999998, 2: 0.24299999999999999, 3: 0.097000000000000003, 4: 0.036999999999999998, 5: 0.014, 6: 0.0060000000000000001, 7: 0.002, 8: 0.001, 9: 0.0, 10: 0.0, 'm': -0.29599999999999999, 'b': 0.98799999999999999}, '12': {1: 0.67300000000000004, 2: 0.222, 3: 0.070999999999999994, 4: 0.023, 5: 0.0070000000000000001, 6: 0.002, 7: 0.001, 8: 0.0, 9: 0.0, 10: 0.0, 'm': -0.16300000000000001, 'b': 1.014}, '32': {1: 0.68600000000000005, 2: 0.20399999999999999, 3: 0.072999999999999995, 4: 0.025000000000000001, 5: 0.0080000000000000002, 6: 0.002, 7: 0.001, 8: 0.0, 9: 0.0, 10: 0.0, 'm': -0.107, 'b': 0.83199999999999996}, '31': {1: 0.59399999999999997, 2: 0.23400000000000001, 3: 0.104, 4: 0.042000000000000003, 5: 0.017000000000000001, 6: 0.0060000000000000001, 7: 0.0030000000000000001, 8: 0.001, 9: 0.0, 10: 0.0, 'm': -0.191, 'b': 0.69299999999999995}, '30': {1: 0.56599999999999995, 2: 0.22600000000000001, 3: 0.114, 4: 0.052999999999999999, 5: 0.023, 6: 0.01, 7: 0.0040000000000000001, 8: 0.002, 9: 0.001, 10: 0.0, 'm': -0.35799999999999998, 'b': 0.55900000000000005}, '51': {1: 0.73699999999999999, 2: 0.152, 3: 0.067000000000000004, 4: 0.027, 5: 0.010999999999999999, 6: 0.0040000000000000001, 7: 0.001, 8: 0.001, 9: 0.0, 10: 0.0, 'm': -0.27000000000000002, 'b': 0.47699999999999998}, '50': {1: 0.65400000000000003, 2: 0.185, 3: 0.088999999999999996, 4: 0.041000000000000002, 5: 0.017999999999999999, 6: 0.0080000000000000002, 7: 0.0030000000000000001, 8: 0.001, 9: 0.001, 10: 0.0, 'm': -0.37, 'b': 0.35499999999999998}, '52': {1: 0.73199999999999998, 2: 0.17699999999999999, 3: 0.059999999999999998, 4: 0.021000000000000001, 5: 0.0070000000000000001, 6: 0.002, 7: 0.001, 8: 0.0, 9: 0.0, 10: 0.0, 'm': -0.047, 'b': 0.76300000000000001}} | |
def getRunsInn(rpinn): | |
runsinn = {0: 1/((rpinn*.761)+1), | |
1: (rpinn*(0.761**2))/(((rpinn*.761)+1)**2) | |
} | |
for i in range(2, 11): | |
v = (rpinn*(0.761**2)*(((rpinn*.761) - 0.761 + 1)**(i-1)))/(((rpinn*.761)+1)**(i+1)) | |
runsinn[i] = v | |
return runsinn | |
def getRunExp(rpinn, runsinn): | |
runExp = {'10': runsinn | |
} | |
for i in range(0, 3): | |
for j in range(1, 9): | |
k = str(j) + str(i) | |
if k == '10': continue | |
runExp[k] = {0: ((tangoRunExp[k]['m']*rpinn) + tangoRunExp[k]['b']) | |
} | |
for r in range(1, 11): | |
runExp[k][r] = ((1 - runExp[k][0])*tangoRunExp[k][r]) | |
return runExp | |
def getInnWinexp(runExp): | |
## Chance of home team winning with zero | |
## outs at the beg. of each inning | |
innWinexp = {'101': {0: 0.5 | |
} | |
} | |
for i in range(-25, 0): | |
innWinexp['101'][i] = 0 | |
for i in range(1, 26): | |
innWinexp['101'][i] = 1 | |
for i in range(9, 0, -1): | |
for j in range(2, 0, -1): | |
if j == 2: next = str(i+1) + '1' | |
else: next = str(i) + '2' | |
this = str(i) + str(j) | |
innWinexp[this] = {} | |
if j == 2: | |
for k in range(-25, 26): | |
p = 0 | |
if i == 9 and k > 0: | |
innWinexp[this][k] = 1 | |
continue | |
else: pass | |
for m in range(0, 11): | |
if k+m > 25: iw = 1 | |
else: iw = innWinexp[next][k+m] | |
p += runExp['10'][m]*iw | |
innWinexp[this][k] = p | |
else: | |
for k in range(-25, 26): | |
p = 0 | |
for m in range(0, 11): | |
if k-m < -25: iw = 0 | |
else: iw = innWinexp[next][k-m] | |
p += runExp['10'][m]*iw | |
innWinexp[this][k] = p | |
return innWinexp | |
def getWinexp(innWinexp, runExp, inn, half, base, outs, rdiff): | |
if inn > 9: inn = 9 | |
innkey = str(inn) + str(half) | |
if outs > 2: outs = 2 | |
sitkey = str(base) + str(outs) | |
if half == 2: next = str(inn+1) + '1' | |
else: next = str(inn) + '2' | |
if sitkey == '10': ## beginning of half inning | |
if rdiff > 25: rdiff = 25 | |
elif rdiff < -25: rdiff = -25 | |
else: pass | |
Winexp = innWinexp[innkey][rdiff] | |
elif half == 1: | |
Winexp = 0 | |
for i in range(10, -1, -1): | |
if rdiff-i < -25: iw = 0 | |
elif rdiff-i > 25: iw = 1 | |
else: iw = innWinexp[next][rdiff-i] | |
Winexp += runExp[sitkey][i]*iw | |
else: | |
Winexp = 0 | |
for i in range(0, 11): | |
if rdiff-i < -25: iw = 0 | |
elif rdiff+i > 25: iw = 1 | |
else: iw = innWinexp[next][rdiff+i] | |
Winexp += runExp[sitkey][i]*iw | |
return Winexp | |
def getVol(innWinexp, runExp, inn, half, base, outs, rdiff): | |
## changes if strikeout: | |
if outs == 2: | |
outsK = 0 | |
baseK = 1 | |
if half == 1: | |
halfK = 2 | |
innK = inn | |
else: | |
halfK = 1 | |
innK = inn + 1 | |
else: | |
outsK = outs + 1 | |
baseK, halfK, innK = base, half, inn | |
WinexpK = getWinexp(innWinexp, runExp, innK, halfK, baseK, outsK, rdiff) | |
## changes if homerun | |
if half == 1: | |
rdiff -= baseHr[base] | |
else: | |
rdiff += baseHr[base] | |
base = 1 | |
WinexpHr = getWinexp(innWinexp, runExp, inn, half, base, outs, rdiff) | |
return (abs(WinexpHr - WinexpK))/0.133 | |
def rpgToInnWinexp(rpg): | |
rpinn = float(rpg)/9 ## r/inn | |
runsinn = getRunsInn(rpinn) | |
runExp = getRunExp(rpinn, runsinn) | |
innWinexp = getInnWinexp(runExp) | |
return innWinexp, runExp | |
####sample usage | |
##inn = 2 | |
##half = 1 ## 1 = top, 2 = bottom | |
##base = 1 ## base situation: 1 through 8 | |
##outs = 2 | |
##rdiff = 2 ## run differential from home team perspective | |
## | |
##rpg = 4.5 ## runs per team-game | |
##innWinexp, runExp = rpgToInnWinexp(rpg) | |
## | |
##winexp = getWinexp(innWinexp, runExp, inn, half, base, outs, rdiff) | |
## | |
##vol = getVol(innWinexp, runExp, inn, half, base, outs, rdiff) | |
## | |
##print winexp, lev |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In the very unlikely event anything further happens with this, it lives in this repo:
https://github.com/JeffSackmann/baseball_misc