Add files via upload

This commit is contained in:
Peter Norvig 2018-04-14 20:50:02 -07:00 committed by GitHub
parent 698d8b9ba9
commit f35adc518a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -8,15 +8,16 @@
"\n",
"## 12 April, 2018\n",
"\n",
"\"It's tough to make predictions, especially [about](https://en.wikiquote.org/wiki/Yogi_Berra) [the](https://en.wikiquote.org/wiki/Niels_Bohr) future.\" That's true for the **NBA basketball playoffs**, where there is a difference of opinion over the probable fates of the Warriors and Cavs, the teams that met in the finals each of the last three years. The Las Vegas oddsmakers have the Warriors as co-favorites at 35% chance to win the title, while [538](https://fivethirtyeight.com/features/the-nba-playoffs-sleepers-favorites-and-best-first-round-matchups/), using their ELO rating, give the Warriors only a 4% chance. That 9-fold difference underscores that rational people can use different models with different assumptions and come to different conclusions. Here are some models you might choose:\n",
"\"It's tough to make predictions, especially [about](https://en.wikiquote.org/wiki/Yogi_Berra) [the](https://en.wikiquote.org/wiki/Niels_Bohr) future.\" That's true for the **NBA basketball playoffs**, where there is a wide range of opinion about the Warriors and Cavs, the teams that met in the finals each of the last three years. The Las Vegas oddsmakers have the Warriors as co-favorites at 35% chance to win the title, while [538](https://fivethirtyeight.com/features/the-nba-playoffs-sleepers-favorites-and-best-first-round-matchups/), using their ELO rating, give the Warriors only a 4% chance. That 9-fold difference underscores that rational people can use different models with different assumptions and come to different conclusions. Here are some models you might choose:\n",
"\n",
"1. **Holistic**: \"I just feel that the Rockets have about a 1/3 chance of winning it all.\"\n",
"2. **Game by Game**: \"I think the Rockets have an 75% chance of winning each game in the first round, then 70% in the next round, then 60%; from that I'll calculate their overall chance.\"\n",
"3. **Point by Point**: \"The Rockets have a per-game average point differential of 8.2; I'll compare that to the other teams and caclulate their overall chance.\"\n",
"1. **Holistic**: I just feel that the Warriors have about a 1/3 chance of winning it all.\n",
"2. **Game by Game**: I think the Warriors have an 75% chance of winning each game in the first round, then 65% for each game in the second round, but only 45% against the Rockets, then 55% if they make it to the finals. From that I'll calculate their overall chance.\n",
"3. **Point by Point**: The Warriors have a per-game average point differential of +5.8; I'll compare that to the other teams and caclulate their overall chance.\n",
"4. **Play by Play**: Use [detailed statistics](https://www.basketball-reference.com/play-index/plus/shot_finder.cgi) to model the game shot-by-shot, or even pass-by-pass.\n",
"\n",
"# Point by Point Model\n",
"\n",
"The [Simple Rating System](https://www.sportingcharts.com/dictionary/nba/simple-rating-system-statistics.aspx) (SRS) records the average point differential of a team over the season, with a slight adjustment for strength of schedule. We can look up the winning percentage and SRS of the top contending teams [here](https://www.basketball-reference.com/leagues/NBA_2018.html): \n",
"The [Simple Rating System](https://www.sportingcharts.com/dictionary/nba/simple-rating-system-statistics.aspx) (SRS) records the average point differential of a team over the season, with a slight adjustment for strength of schedule. We can look up the winning percentage and SRS of the top contending teams [here](https://www.basketball-reference.com/leagues/NBA_2018.html) (and other sites have slightly different ways of making similar calculations): \n",
"\n",
" TEAM PCT SRS\n",
" Rockets .793 8.2\n",
@ -25,15 +26,15 @@
" Sixers .634 4.3\n",
" \n",
"\n",
"The Point-by-Point model of a game is: a game is decided by a random sample from the distribution of point differentials, which is a normal (Gaussian) distribution centered around the recorded differences of the two teams. So, if the Raptors play the Sixers, then the\n",
"mean of this distribution is 7.3 - 4.3 = 3.0. We also need to know the standard deviation; [Betlabs](https://www.betlabssports.com/blog/a-look-at-nba-team-totals/) says it\n",
"is 10.5 points (and they confirm that scores roughly follow a normal distribution). \n",
"The function `simulate` does the calculation:"
"The Point-by-Point model says: a game is decided by a random sample from the distribution of point differentials, which is a normal (Gaussian) distribution centered around the difference of SRS scores of the two teams. So, if the Raptors play the Sixers, then the\n",
"mean of this distribution is 7.3 - 4.3 = 3.0. We also need to know the standard deviation of the distribution; [Betlabs](https://www.betlabssports.com/blog/a-look-at-nba-team-totals/) says it\n",
"is 10.5 points across the NBA (and they confirm that scores roughly follow a normal distribution). \n",
"The function `simulate` does the calculation by Monte Carlo simulation:"
]
},
{
"cell_type": "code",
"execution_count": 28,
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@ -41,28 +42,28 @@
"from random import gauss\n",
"\n",
"def simulate(diff, 𝝈=10.5, n=100000):\n",
" \"Given SRS point differential of two teams, return favored team's win probability.\"\n",
" \"Given SRS point differential of a team against another, return game win probability.\"\n",
" return mean(gauss(diff, 𝝈) > 0 for game in range(n))"
]
},
{
"cell_type": "code",
"execution_count": 29,
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.61204"
"0.61166"
]
},
"execution_count": 29,
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"simulate(7.3 - 4.3) # Raptors win probability vs Sixers"
"simulate(7.3 - 4.3) # Raptors game win probability vs Sixers"
]
},
{
@ -74,7 +75,7 @@
},
{
"cell_type": "code",
"execution_count": 30,
"execution_count": 3,
"metadata": {},
"outputs": [
{
@ -109,14 +110,14 @@
"\n",
"# Game by Game Model\n",
"\n",
"The next model says that a playoff series is a sequence of independent and identically distributed game results (where the probability of a single-game win is specified either using point differential, or holistically). The idea here is to be consistent: if you believe that a team's win percentage is 60%, and you believe that games are independent, then you must believe that the team's chance of wining 4 in a row is 0.6<sup>4</sup> = 0.1296.\n",
"The next model says that a playoff series is a sequence of independent and identically distributed game results (where the probability of a single-game win could be specified using point differential, or holistically, or some other model). The idea here is to be consistent: if you believe that a team's win percentage is 60%, and you believe that games are independent, then you must believe that the team's chance of wining 4 in a row is 0.6<sup>4</sup> = 0.1296. This model ignores the fact that games aren't strictly independent, and ignores home court advantage. Why? Because these factors would change the final winning estimate by only a few percentage points, and I already have more uncertainty than that.\n",
"\n",
"The function `win_series` calculates the probability of winning a series, given the probability of winning a game:"
]
},
{
"cell_type": "code",
"execution_count": 31,
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
@ -125,7 +126,8 @@
" The optional arguments say how many Wins and Losses the team has in the series so far.\"\"\"\n",
" return (1 if W == 4 else\n",
" 0 if L == 4 else\n",
" p * win_series(p, W + 1, L) + (1 - p) * win_series(p, W, L + 1))"
" p * win_series(p, W + 1, L) + \n",
" (1 - p) * win_series(p, W, L + 1))"
]
},
{
@ -137,7 +139,7 @@
},
{
"cell_type": "code",
"execution_count": 33,
"execution_count": 5,
"metadata": {},
"outputs": [
{
@ -147,7 +149,7 @@
"0 point differential = 50% win game = 50% win series\n",
"1 point differential = 54% win game = 58% win series\n",
"2 point differential = 58% win game = 66% win series\n",
"3 point differential = 61% win game = 73% win series\n",
"3 point differential = 61% win game = 74% win series\n",
"4 point differential = 65% win game = 80% win series\n",
"5 point differential = 68% win game = 85% win series\n",
"6 point differential = 72% win game = 89% win series\n",
@ -168,7 +170,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"For example, a team with a 3 point differential advantage has a 61% chance of winning each game in a series, and a 73% chance of winning the series. This model ignores the fact that games aren't strictly independent, and ignores home court advantage. Why? Because these factors would change the final winning estimate by only a few percentage points, and I already have more uncertainty than that. \n",
"For example, a team with a 3 point net differential has a 61% chance of winning each game in a series, and a 73% chance of winning the series. \n",
"\n",
"What happens if some games in a series have already been played? The following function prints a table where each row tells a team's current win-loss record, each column is the game win percentage, and each entry in the table is the series win percentage."
]
@ -183,32 +185,32 @@
"output_type": "stream",
"text": [
"W-L | Game Win Percentage\n",
" | 20% 25% 30% 35% 40% 45% 50% 55% 60% 65% 70% 75% 80%\n",
"----+-----------------------------------------------------------------\n",
"0-0 | 3% 7% 13% 20% 29% 39% 50% 61% 71% 80% 87% 93% 97%\n",
"0-1 | 2% 4% 7% 12% 18% 26% 34% 44% 54% 65% 74% 83% 90%\n",
"0-2 | 1% 2% 3% 5% 9% 13% 19% 26% 34% 43% 53% 63% 74%\n",
"0-3 | 0% 0% 1% 2% 3% 4% 6% 9% 13% 18% 24% 32% 41%\n",
"----+-----------------------------------------------------------------\n",
"1-0 | 10% 17% 26% 35% 46% 56% 66% 74% 82% 88% 93% 96% 98%\n",
"1-1 | 6% 10% 16% 24% 32% 41% 50% 59% 68% 76% 84% 90% 94%\n",
"1-2 | 3% 5% 8% 13% 18% 24% 31% 39% 48% 56% 65% 74% 82%\n",
"1-3 | 1% 2% 3% 4% 6% 9% 12% 17% 22% 27% 34% 42% 51%\n",
"----+-----------------------------------------------------------------\n",
"2-0 | 26% 37% 47% 57% 66% 74% 81% 87% 91% 95% 97% 98% 99%\n",
"2-1 | 18% 26% 35% 44% 52% 61% 69% 76% 82% 87% 92% 95% 97%\n",
"2-2 | 10% 16% 22% 28% 35% 43% 50% 57% 65% 72% 78% 84% 90%\n",
"2-3 | 4% 6% 9% 12% 16% 20% 25% 30% 36% 42% 49% 56% 64%\n",
"----+-----------------------------------------------------------------\n",
"3-0 | 59% 68% 76% 82% 87% 91% 94% 96% 97% 98% 99% 100% 100%\n",
"3-1 | 49% 58% 66% 73% 78% 83% 88% 91% 94% 96% 97% 98% 99%\n",
"3-2 | 36% 44% 51% 58% 64% 70% 75% 80% 84% 88% 91% 94% 96%\n",
"3-3 | 20% 25% 30% 35% 40% 45% 50% 55% 60% 65% 70% 75% 80%\n"
" | 15% 20% 25% 30% 35% 40% 45% 50% 55% 60% 65% 70% 75% 80% 85%\n",
"----+---------------------------------------------------------------------------\n",
"0-0 | 1% 3% 7% 13% 20% 29% 39% 50% 61% 71% 80% 87% 93% 97% 99%\n",
"0-1 | 1% 2% 4% 7% 12% 18% 26% 34% 44% 54% 65% 74% 83% 90% 95%\n",
"0-2 | 0% 1% 2% 3% 5% 9% 13% 19% 26% 34% 43% 53% 63% 74% 84%\n",
"0-3 | 0% 0% 0% 1% 2% 3% 4% 6% 9% 13% 18% 24% 32% 41% 52%\n",
"----+---------------------------------------------------------------------------\n",
"1-0 | 5% 10% 17% 26% 35% 46% 56% 66% 74% 82% 88% 93% 96% 98% 99%\n",
"1-1 | 3% 6% 10% 16% 24% 32% 41% 50% 59% 68% 76% 84% 90% 94% 97%\n",
"1-2 | 1% 3% 5% 8% 13% 18% 24% 31% 39% 48% 56% 65% 74% 82% 89%\n",
"1-3 | 0% 1% 2% 3% 4% 6% 9% 12% 17% 22% 27% 34% 42% 51% 61%\n",
"----+---------------------------------------------------------------------------\n",
"2-0 | 16% 26% 37% 47% 57% 66% 74% 81% 87% 91% 95% 97% 98% 99% 100%\n",
"2-1 | 11% 18% 26% 35% 44% 52% 61% 69% 76% 82% 87% 92% 95% 97% 99%\n",
"2-2 | 6% 10% 16% 22% 28% 35% 43% 50% 57% 65% 72% 78% 84% 90% 94%\n",
"2-3 | 2% 4% 6% 9% 12% 16% 20% 25% 30% 36% 42% 49% 56% 64% 72%\n",
"----+---------------------------------------------------------------------------\n",
"3-0 | 48% 59% 68% 76% 82% 87% 91% 94% 96% 97% 98% 99% 100% 100% 100%\n",
"3-1 | 39% 49% 58% 66% 73% 78% 83% 88% 91% 94% 96% 97% 98% 99% 100%\n",
"3-2 | 28% 36% 44% 51% 58% 64% 70% 75% 80% 84% 88% 91% 94% 96% 98%\n",
"3-3 | 15% 20% 25% 30% 35% 40% 45% 50% 55% 60% 65% 70% 75% 80% 85%\n"
]
}
],
"source": [
"def series_table(pcts=[p/100 for p in range(20, 85, 5)]):\n",
"def series_table(pcts=[p/100 for p in range(15, 90, 5)]):\n",
" print('W-L | Game Win Percentage')\n",
" print(' | ' + ' '.join(map(pct, pcts)))\n",
" for W in range(4):\n",
@ -224,7 +226,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"For example, if our team with a 60% win percentage loses the first game of the series, then check the \"0-1\" row and the \"60%\" column to see that it still has a 54% chance of winning the series. You also have the option of updating your prior probability. Suppose after seeing our 60% team lose the first game you decide \"perhaps I was wrong about them; perhaps they're actually a 55% team\". Then you would look at the 55% column of the 0-1 row and find that their chances have slipped to 44%."
"For example, if our team with a 60% win percentage loses the first game of the series, then check the \"0-1\" row and the \"60%\" column to see that it still has a 54% chance of winning the series. "
]
},
{
@ -233,7 +235,7 @@
"source": [
"# Series by Series Model\n",
"\n",
"Here's a function to tabulate a team's chance of winning each series on the way to a title. The first argument is the team's name, and the second, `rounds` is a list of playoff round entries, where each entry is a tuple of the (expected) opponent team name, the game win percentage against this team, and optionally the wins and losses in the series so far:"
"Here's a function to tabulate a team's chance of winning each series on the way to a title. The first argument is the team's name, and each following argument is a playoff round entry, consisting of the opponent team name, the game win percentage against this opponent, and optionally the wins and losses in the series so far:"
]
},
{
@ -242,7 +244,7 @@
"metadata": {},
"outputs": [],
"source": [
"def playoffs(name, rounds):\n",
"def playoffs(name, *rounds):\n",
" \"Print probability for team winning each series.\"\n",
" overall = 1.0 \n",
" for (opponent, p, *WL) in rounds:\n",
@ -270,26 +272,17 @@
"text": [
"Rockets vs Wolves 93%; through here: 93%\n",
"Rockets vs Jazz 87%; through here: 81%\n",
"Rockets vs Warriors 71%; through here: 58%\n",
"Rockets vs Raptors 71%; through here: 41%\n"
"Rockets vs Warriors 61%; through here: 49%\n",
"Rockets vs Raptors 71%; through here: 35%\n"
]
}
],
"source": [
"playoffs('Rockets',\n",
" [('Wolves', 0.75),\n",
" ('Jazz', 0.70),\n",
" ('Warriors', 0.60),\n",
" ('Raptors', 0.60)])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So I'm in good agreement with [538](https://fivethirtyeight.com/features/the-nba-playoffs-sleepers-favorites-and-best-first-round-matchups/): I have the Rockets at 58% winning the conference and 41% winning the title, while 538 had them at 57% and 44%.\n",
"\n",
"What if I went by point differential?"
" ('Wolves', 0.75),\n",
" ('Jazz', 0.70),\n",
" ('Warriors', 0.55),\n",
" ('Raptors', 0.60))"
]
},
{
@ -301,28 +294,28 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Rockets vs Wolves 88%; through here: 88%\n",
"Rockets vs Jazz 79%; through here: 69%\n",
"Rockets vs Warriors 70%; through here: 49%\n",
"Rockets vs Raptors 58%; through here: 28%\n"
"Warriors vs Spurs 93%; through here: 93%\n",
"Warriors vs Blazers 80%; through here: 74%\n",
"Warriors vs Rockets 39%; through here: 29%\n",
"Warriors vs Raptors 61%; through here: 18%\n"
]
}
],
"source": [
"playoffs('Rockets',\n",
" [('Wolves', simulate(8.2 - 2.3)),\n",
" ('Jazz', simulate(8.2 - 4.5)),\n",
" ('Warriors', simulate(8.2 - 5.8)),\n",
" ('Raptors', simulate(8.2 - 7.3))])"
"playoffs('Warriors',\n",
" ('Spurs', 0.75),\n",
" ('Blazers', 0.65),\n",
" ('Rockets', 0.45),\n",
" ('Raptors', 0.55))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This puts the Rockets at 48% and 28%, which is closer to the Vegas odds of 44% and 35%. How do I reconcile this discrepancy? I guess I would say that I don't have much faith in the point differential model, for several reasons: it counts games from the distant past, when some teams had very different lineups than they have now (due to injuries and trades); different teams have different approaches to how they handle games whose outcome is already decided; the metric puts too much emphasis on blowouts, for example, in the Warriors' final game, it was to their strategic advantage to lose, and they did it very convincingly&mdash;by 40 points, which dropped their average point differential for the entire year by 0.5 points.\n",
"So I'm in good agreement with the Vegas oddsmakers about the Rockets: I have the Rockets at 49% winning the conference and 35% winning the title, while Vegas had them at 44% and 35%. For the Warriors I'm splitting the difference between 538's low estimate (8% win conference, 4% win title) and Vegas's high estimate (44% and 35%, tied with the Rockets).\n",
"\n",
"Here are my projections for the hometown Warriors:"
"What if I went by point differential?"
]
},
{
@ -334,34 +327,120 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Warriors vs Spurs 87%; through here: 87%\n",
"Warriors vs Portland 80%; through here: 70%\n",
"Warriors vs Rockets 29%; through here: 20%\n",
"Warriors vs Raptors 61%; through here: 12%\n"
"Rockets vs Wolves 89%; through here: 89%\n",
"Rockets vs Jazz 78%; through here: 70%\n",
"Rockets vs Warriors 69%; through here: 48%\n",
"Rockets vs Raptors 58%; through here: 28%\n"
]
}
],
"source": [
"playoffs('Rockets',\n",
" ('Wolves', simulate(8.2 - 2.3)),\n",
" ('Jazz', simulate(8.2 - 4.5)),\n",
" ('Warriors', simulate(8.2 - 5.8)),\n",
" ('Raptors', simulate(8.2 - 7.3)))"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Warriors vs Spurs 73%; through here: 73%\n",
"Warriors vs Blazers 76%; through here: 55%\n",
"Warriors vs Rockets 31%; through here: 17%\n",
"Warriors vs Raptors 38%; through here: 6%\n"
]
}
],
"source": [
"playoffs('Warriors',\n",
" [('Spurs', 0.70),\n",
" ('Portland', 0.65),\n",
" ('Rockets', 0.40),\n",
" ('Raptors', 0.55)])"
" ('Spurs', simulate(5.8 - 2.9)),\n",
" ('Blazers', simulate(5.8 - 2.6)),\n",
" ('Rockets', simulate(5.8 - 8.2)),\n",
" ('Raptors', simulate(5.8 - 7.3)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So I'm splitting the difference between 538's low estimate (8% win conference, 4% win title) and Vegas's high estimate (44% win conference, 35% win title).\n",
"This model gives cuts the Rockets' chances a bit (mainly because the Raptors have a strong 7.3 score), and cuts the Warriors' chances a lot, bringing them more in line with the 538 prediction. How do I reconcile the discrepancy between my subjective probabilities and these numbers? I guess I would say that I have less faith in the point differential model, for several reasons: it counts games from the distant past, when some teams had very different lineups than they have now (due to injuries and trades); different teams have different approaches to how they handle games whose outcome is already decided; the metric puts too much emphasis on blowouts, for example, in the Warriors' final game, it was to their strategic advantage to lose, and they did it very convincingly&mdash;by 40 points, which dropped their average point differential for the entire year by 0.5 points.\n",
"\n",
"---"
"# Series Length\n",
"\n",
"Given a team's game win percentage, how many games should we expect a series to run? That is, with a game win percentage of 60%, how likely is it to sweep all 4 games? To go to 7 games? Here's a chart:"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"W-L | Game Win Percentage\n",
" | 10% 15% 20% 25% 30% 35% 40% 45% 50% 55% 60% 65% 70% 75% 80% 85% 90%\n",
"----+-------------------------------------------------------------------------------------\n",
"4-0 | 0% 0% 0% 0% 1% 2% 3% 4% 6% 9% 13% 18% 24% 32% 41% 52% 66%\n",
"4-1 | 0% 0% 1% 1% 2% 4% 6% 9% 12% 16% 21% 25% 29% 32% 33% 31% 26%\n",
"4-2 | 0% 0% 1% 2% 4% 6% 9% 12% 16% 19% 21% 22% 22% 20% 16% 12% 7%\n",
"4-3 | 0% 1% 2% 3% 6% 8% 11% 14% 16% 17% 17% 15% 13% 10% 7% 4% 1%\n",
"3-4 | 1% 4% 7% 10% 13% 15% 17% 17% 16% 14% 11% 8% 6% 3% 2% 1% 0%\n",
"2-4 | 7% 12% 16% 20% 22% 22% 21% 19% 16% 12% 9% 6% 4% 2% 1% 0% 0%\n",
"1-4 | 26% 31% 33% 32% 29% 25% 21% 16% 12% 9% 6% 4% 2% 1% 1% 0% 0%\n",
"0-4 | 66% 52% 41% 32% 24% 18% 13% 9% 6% 4% 3% 2% 1% 0% 0% 0% 0%\n"
]
}
],
"source": [
"from collections import Counter\n",
"\n",
"def series_results(p, W=0, L=0):\n",
" \"\"\"Return {(win, loss): probability} for all possible outcomes of the series.\"\"\"\n",
" return MCounter({(W, L): 1} if W == 4 or L == 4 else\n",
" (p * series_results(p, W + 1, L) + \n",
" (1 - p) * series_results(p, W, L + 1)))\n",
" \n",
"def series_results_table(pcts=[p/100 for p in range(10, 95, 5)]):\n",
" outcomes = [(4, 0), (4, 1), (4, 2), (4, 3), (3, 4), (2, 4), (1, 4), (0, 4)]\n",
" print('W-L | Game Win Percentage')\n",
" print(' | ' + ' '.join(map(pct, pcts)))\n",
" print('----+' + '-' * 5 * len(pcts))\n",
" for (W, L) in outcomes:\n",
" results = [series_results(p)[W, L] for p in pcts]\n",
" print('{}-{} | {}'.format(W, L, ' '.join(map(pct, results))))\n",
" \n",
"class MCounter(Counter):\n",
" \"A multipliable Counter.\"\n",
" def __mul__(self, p): return MCounter({k: p * self[k] for k in self})\n",
" __rmul__ = __mul__\n",
" \n",
"series_results_table()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So we see that our 60% team has a 13% chance of sweeping, and 13+21+21 = 55% chance of winning in 6 games or less.\n",
"\n",
"**Note:** I think `collections.Counter` should support multiplication. If `C` is a `Counter`, then shouldn't `C + C` be the same as `2 * C`?"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"# Historical Relic: 2016 NBA Playoffs\n",
"\n",
"\n",
@ -382,7 +461,7 @@
},
{
"cell_type": "code",
"execution_count": 11,
"execution_count": 13,
"metadata": {
"scrolled": true
},
@ -400,15 +479,15 @@
],
"source": [
"playoffs('Warriors',\n",
" [('Rockets', 0.83),\n",
" ('Clippers', 0.73),\n",
" ('Spurs', 0.58),\n",
" ('Cavs', 0.67)])"
" ('Rockets', 0.83),\n",
" ('Clippers', 0.73),\n",
" ('Spurs', 0.58),\n",
" ('Cavs', 0.67))"
]
},
{
"cell_type": "code",
"execution_count": 12,
"execution_count": 14,
"metadata": {},
"outputs": [
{
@ -424,15 +503,15 @@
],
"source": [
"playoffs('Spurs',\n",
" [('Memphis', 0.83),\n",
" ('Thunder', 0.62),\n",
" ('Warriors', 0.42),\n",
" ('Cavs', 0.67)])"
" ('Memphis', 0.83),\n",
" ('Thunder', 0.62),\n",
" ('Warriors', 0.42),\n",
" ('Cavs', 0.67))"
]
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": 15,
"metadata": {},
"outputs": [
{
@ -448,10 +527,10 @@
],
"source": [
"playoffs('Cavs',\n",
" [('Pistons', 0.83),\n",
" ('Hawks', 0.60),\n",
" ('Raptors', 0.55),\n",
" ('GSW/SAS', 0.33)])"
" ('Pistons', 0.83),\n",
" ('Hawks', 0.60),\n",
" ('Raptors', 0.55),\n",
" ('GSW/SAS', 0.33))"
]
},
{
@ -476,7 +555,7 @@
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": 16,
"metadata": {},
"outputs": [
{
@ -492,10 +571,10 @@
],
"source": [
"playoffs('Warriors',\n",
" [('Rockets', 0.70, 3, 1),\n",
" ('Blazers', 0.55),\n",
" ('Spurs', 0.55),\n",
" ('Cavs', 0.60)])"
" ('Rockets', 0.70, 3, 1),\n",
" ('Blazers', 0.55),\n",
" ('Spurs', 0.55),\n",
" ('Cavs', 0.60))"
]
},
{
@ -507,7 +586,7 @@
},
{
"cell_type": "code",
"execution_count": 15,
"execution_count": 17,
"metadata": {},
"outputs": [
{
@ -523,15 +602,15 @@
],
"source": [
"playoffs('Spurs',\n",
" [('Memphis', 0.83, 4, 0),\n",
" ('Thunder', 0.62),\n",
" ('Warriors', 0.45),\n",
" ('Cavs', 0.67)])"
" ('Memphis', 0.83, 4, 0),\n",
" ('Thunder', 0.62),\n",
" ('Warriors', 0.45),\n",
" ('Cavs', 0.67))"
]
},
{
"cell_type": "code",
"execution_count": 16,
"execution_count": 18,
"metadata": {},
"outputs": [
{
@ -547,10 +626,10 @@
],
"source": [
"playoffs('Cavs',\n",
" [('Pistons', 0.83, 4, 0),\n",
" ('Hawks', 0.60),\n",
" ('Raptors', 0.55),\n",
" ('GSW/SAS', 0.40)])"
" ('Pistons', 0.83, 4, 0),\n",
" ('Hawks', 0.60),\n",
" ('Raptors', 0.55),\n",
" ('GSW/SAS', 0.40))"
]
},
{
@ -574,7 +653,7 @@
},
{
"cell_type": "code",
"execution_count": 17,
"execution_count": 19,
"metadata": {},
"outputs": [
{
@ -590,15 +669,15 @@
],
"source": [
"playoffs('Warriors',\n",
" [('Rockets', 0.70, 4, 1),\n",
" ('Blazers', 0.67, 3, 1),\n",
" ('Spurs', 0.60),\n",
" ('Cavs', 0.55)])"
" ('Rockets', 0.70, 4, 1),\n",
" ('Blazers', 0.67, 3, 1),\n",
" ('Spurs', 0.60),\n",
" ('Cavs', 0.55))"
]
},
{
"cell_type": "code",
"execution_count": 18,
"execution_count": 20,
"metadata": {},
"outputs": [
{
@ -614,15 +693,15 @@
],
"source": [
"playoffs('Spurs',\n",
" [('Memphis', 0.83, 4, 0),\n",
" ('Thunder', 0.60, 2, 3),\n",
" ('Warriors', 0.40),\n",
" ('Cavs', 0.50)])"
" ('Memphis', 0.83, 4, 0),\n",
" ('Thunder', 0.60, 2, 3),\n",
" ('Warriors', 0.40),\n",
" ('Cavs', 0.50))"
]
},
{
"cell_type": "code",
"execution_count": 19,
"execution_count": 21,
"metadata": {},
"outputs": [
{
@ -638,15 +717,15 @@
],
"source": [
"playoffs('Thunder',\n",
" [('Dallas', 0.83, 4, 1),\n",
" ('Spurs', 0.40, 3, 2),\n",
" ('Warriors', 0.40),\n",
" ('Cavs', 0.45)])"
" ('Dallas', 0.83, 4, 1),\n",
" ('Spurs', 0.40, 3, 2),\n",
" ('Warriors', 0.40),\n",
" ('Cavs', 0.45))"
]
},
{
"cell_type": "code",
"execution_count": 20,
"execution_count": 22,
"metadata": {},
"outputs": [
{
@ -662,10 +741,10 @@
],
"source": [
"playoffs('Cavs',\n",
" [('Pistons', 0.83, 4, 0),\n",
" ('Hawks', 0.60, 4, 0),\n",
" ('Raptors', 0.65),\n",
" ('GS/SA/OK', 0.45)])"
" ('Pistons', 0.83, 4, 0),\n",
" ('Hawks', 0.60, 4, 0),\n",
" ('Raptors', 0.65),\n",
" ('GS/SA/OK', 0.45))"
]
},
{
@ -690,7 +769,7 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 23,
"metadata": {},
"outputs": [
{
@ -706,10 +785,10 @@
],
"source": [
"playoffs('Warriors',\n",
" [('Rockets', 0.70, 4, 1),\n",
" ('Blazers', 0.67, 4, 1),\n",
" ('Thunder', 0.63, 0, 1),\n",
" ('Cavs', 0.55)])"
" ('Rockets', 0.70, 4, 1),\n",
" ('Blazers', 0.67, 4, 1),\n",
" ('Thunder', 0.63, 0, 1),\n",
" ('Cavs', 0.55))"
]
},
{
@ -725,7 +804,7 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 24,
"metadata": {},
"outputs": [
{
@ -741,15 +820,15 @@
],
"source": [
"playoffs('Warriors',\n",
" [('Rockets', 0.70, 4, 1),\n",
" ('Blazers', 0.67, 4, 1),\n",
" ('Thunder', 0.55, 1, 3),\n",
" ('Cavs', 0.55)])"
" ('Rockets', 0.70, 4, 1),\n",
" ('Blazers', 0.67, 4, 1),\n",
" ('Thunder', 0.55, 1, 3),\n",
" ('Cavs', 0.55))"
]
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 25,
"metadata": {},
"outputs": [
{
@ -765,15 +844,15 @@
],
"source": [
"playoffs('Cavs',\n",
" [('Pistons', 0.83, 4, 0),\n",
" ('Hawks', 0.60, 4, 0),\n",
" ('Raptors', 0.55, 2, 2),\n",
" ('Thunder', 0.45)])"
" ('Pistons', 0.83, 4, 0),\n",
" ('Hawks', 0.60, 4, 0),\n",
" ('Raptors', 0.55, 2, 2),\n",
" ('Thunder', 0.45))"
]
},
{
"cell_type": "code",
"execution_count": 24,
"execution_count": 26,
"metadata": {},
"outputs": [
{
@ -789,10 +868,10 @@
],
"source": [
"playoffs('Thunder',\n",
" [('Dallas', 0.83, 4, 1),\n",
" ('Spurs', 0.40, 4, 2),\n",
" ('Warriors', 0.45, 3, 1),\n",
" ('Cavs', 0.55)])"
" ('Dallas', 0.83, 4, 1),\n",
" ('Spurs', 0.40, 4, 2),\n",
" ('Warriors', 0.45, 3, 1),\n",
" ('Cavs', 0.55))"
]
},
{
@ -810,7 +889,7 @@
},
{
"cell_type": "code",
"execution_count": 25,
"execution_count": 27,
"metadata": {},
"outputs": [
{
@ -818,33 +897,62 @@
"output_type": "stream",
"text": [
"W-L | Game Win Percentage\n",
" | 20% 25% 30% 35% 40% 45% 50% 55% 60% 65% 70% 75% 80%\n",
"----+-----------------------------------------------------------------\n",
"0-0 | 3% 7% 13% 20% 29% 39% 50% 61% 71% 80% 87% 93% 97%\n",
"0-1 | 2% 4% 7% 12% 18% 26% 34% 44% 54% 65% 74% 83% 90%\n",
"0-2 | 1% 2% 3% 5% 9% 13% 19% 26% 34% 43% 53% 63% 74%\n",
"0-3 | 0% 0% 1% 2% 3% 4% 6% 9% 13% 18% 24% 32% 41%\n",
"----+-----------------------------------------------------------------\n",
"1-0 | 10% 17% 26% 35% 46% 56% 66% 74% 82% 88% 93% 96% 98%\n",
"1-1 | 6% 10% 16% 24% 32% 41% 50% 59% 68% 76% 84% 90% 94%\n",
"1-2 | 3% 5% 8% 13% 18% 24% 31% 39% 48% 56% 65% 74% 82%\n",
"1-3 | 1% 2% 3% 4% 6% 9% 12% 17% 22% 27% 34% 42% 51%\n",
"----+-----------------------------------------------------------------\n",
"2-0 | 26% 37% 47% 57% 66% 74% 81% 87% 91% 95% 97% 98% 99%\n",
"2-1 | 18% 26% 35% 44% 52% 61% 69% 76% 82% 87% 92% 95% 97%\n",
"2-2 | 10% 16% 22% 28% 35% 43% 50% 57% 65% 72% 78% 84% 90%\n",
"2-3 | 4% 6% 9% 12% 16% 20% 25% 30% 36% 42% 49% 56% 64%\n",
"----+-----------------------------------------------------------------\n",
"3-0 | 59% 68% 76% 82% 87% 91% 94% 96% 97% 98% 99% 100% 100%\n",
"3-1 | 49% 58% 66% 73% 78% 83% 88% 91% 94% 96% 97% 98% 99%\n",
"3-2 | 36% 44% 51% 58% 64% 70% 75% 80% 84% 88% 91% 94% 96%\n",
"3-3 | 20% 25% 30% 35% 40% 45% 50% 55% 60% 65% 70% 75% 80%\n"
" | 15% 20% 25% 30% 35% 40% 45% 50% 55% 60% 65% 70% 75% 80% 85%\n",
"----+---------------------------------------------------------------------------\n",
"0-0 | 1% 3% 7% 13% 20% 29% 39% 50% 61% 71% 80% 87% 93% 97% 99%\n",
"0-1 | 1% 2% 4% 7% 12% 18% 26% 34% 44% 54% 65% 74% 83% 90% 95%\n",
"0-2 | 0% 1% 2% 3% 5% 9% 13% 19% 26% 34% 43% 53% 63% 74% 84%\n",
"0-3 | 0% 0% 0% 1% 2% 3% 4% 6% 9% 13% 18% 24% 32% 41% 52%\n",
"----+---------------------------------------------------------------------------\n",
"1-0 | 5% 10% 17% 26% 35% 46% 56% 66% 74% 82% 88% 93% 96% 98% 99%\n",
"1-1 | 3% 6% 10% 16% 24% 32% 41% 50% 59% 68% 76% 84% 90% 94% 97%\n",
"1-2 | 1% 3% 5% 8% 13% 18% 24% 31% 39% 48% 56% 65% 74% 82% 89%\n",
"1-3 | 0% 1% 2% 3% 4% 6% 9% 12% 17% 22% 27% 34% 42% 51% 61%\n",
"----+---------------------------------------------------------------------------\n",
"2-0 | 16% 26% 37% 47% 57% 66% 74% 81% 87% 91% 95% 97% 98% 99% 100%\n",
"2-1 | 11% 18% 26% 35% 44% 52% 61% 69% 76% 82% 87% 92% 95% 97% 99%\n",
"2-2 | 6% 10% 16% 22% 28% 35% 43% 50% 57% 65% 72% 78% 84% 90% 94%\n",
"2-3 | 2% 4% 6% 9% 12% 16% 20% 25% 30% 36% 42% 49% 56% 64% 72%\n",
"----+---------------------------------------------------------------------------\n",
"3-0 | 48% 59% 68% 76% 82% 87% 91% 94% 96% 97% 98% 99% 100% 100% 100%\n",
"3-1 | 39% 49% 58% 66% 73% 78% 83% 88% 91% 94% 96% 97% 98% 99% 100%\n",
"3-2 | 28% 36% 44% 51% 58% 64% 70% 75% 80% 84% 88% 91% 94% 96% 98%\n",
"3-3 | 15% 20% 25% 30% 35% 40% 45% 50% 55% 60% 65% 70% 75% 80% 85%\n"
]
}
],
"source": [
"series_table()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 20 June 2016 \n",
"\n",
"Congratulations to LeBron and the Cavs for overcoming long odds to win a championship for Cleveland. My model says the Warriors were at 94% when they were up 3-1, and if you go by point differential, almost 97%:"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.9671243623571251"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"win_series(simulate(10.38 - 5.45), W=3, L=1)"
]
}
],
"metadata": {