{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# A dice problem\n", "\n", "This notebook demonstrates the use of Pmf and Suite objects, and explores a dice-rolling problem that turns out to be more complicated than it sounds\n", "\n", "Copyright 2016 Allen Downey\n", "\n", "MIT License: http://opensource.org/licenses/MIT" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from __future__ import print_function, division\n", "\n", "from thinkbayes2 import Pmf, Suite\n", "from thinkbayes2 import MakeMixture\n", "\n", "from fractions import Fraction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A dice problem\n", "\n", "Suppose I have a six-sided die that is red on 2 sides and blue on 4 sides, and another die that's the other way around, red on 4 sides and blue on 2.\n", "\n", "I choose a die at random and roll it, and I tell you it came up red. What is the probability that I rolled the second die (red on 4 sides)?\n", "\n", "To answer this question, I'll create `Pmf` objects to represent the two dice:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Blue 2/3\n", "Red 1/3\n" ] } ], "source": [ "d1 = Pmf({'Red':Fraction(2), 'Blue':Fraction(4)}, label='d1 (bluish) ')\n", "d1.Print()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Blue 1/3\n", "Red 2/3\n" ] } ], "source": [ "d2 = Pmf({'Red':Fraction(4), 'Blue':Fraction(2)}, label='d2 (reddish)')\n", "d2.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And I'll make another `Pmf` to represent the random choice of one die or the other." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 1/2\n", "d1 (bluish) 1/2\n" ] } ], "source": [ "dice = Pmf({d1:Fraction(1), d2:Fraction(1)})\n", "dice.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now I can use the `Random` method to choose a die and then roll it." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "'Blue'" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dice.Random().Random()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scenario A\n", "\n", "The following generator simulates the process of repeatedly choosing a die and then rolling it." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def rollA(dice):\n", " while True:\n", " die = dice.Random()\n", " roll = die.Random()\n", " yield roll" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use this generator to simulate rolls:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Red\n", "Red\n", "Blue\n", "Blue\n", "Blue\n" ] } ], "source": [ "iterA = rollA(dice)\n", "for i in range(5):\n", " print(next(iterA))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the long run, the proportion of red and blue is 50-50." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Pmf({'Red': 0.528, 'Blue': 0.47200000000000003})" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Pmf(next(iterA) for i in range(1000))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see that by computing the weighted mixture of the two dice:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Blue 1/2\n", "Red 1/2\n" ] } ], "source": [ "MakeMixture(dice).Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To answer the original question, I'll create a suite of hypotheses where each hypothesis is represented by a die, and the likelihood of the data under each hypothesis is the probability that the given die yields the given outcome." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Dice(Suite):\n", " def Likelihood(self, data, hypo):\n", " \"\"\"\n", " data: 'Red' or 'Blue'\n", " hypo: a Die object\n", " \"\"\"\n", " return hypo[data]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now I can create a suite that represents the prior distribution." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 1/2\n", "d1 (bluish) 1/2\n" ] } ], "source": [ "prior = Dice({d1:Fraction(1), d2:Fraction(1)})\n", "prior.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And update it with the data." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 2/3\n", "d1 (bluish) 1/3\n" ] } ], "source": [ "posterior = prior.Copy()\n", "posterior.Update('Red')\n", "posterior.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The posterior probabilities for `d1` is `1/3` and the posterior probability for `d2` is `2/3`.\n", "\n", "Intuitively, the posterior probability of `d2` is higher than the prior (which was `1/2`) because the outcome (red) is more likely on `d2` than `d1`. If we had rolled blue, the probability of `d1` would be higher.\n", "\n", "Now suppose I ask you to predict the outcome of the next roll. Remember that in this scenario, I choose a new die each time, so what you learned on the first roll doesn't help you with the second. The predictive distribution is a weighted mixture of the dice, using the prior weights:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Blue 1/2\n", "Red 1/2\n" ] } ], "source": [ "predictive = MakeMixture(prior)\n", "predictive.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scenario B\n", "\n", "Now consider a different scenario. Instead of choosing a new die every time, I choose a die once and roll it repeatedly. Here's a generator that simulates this scenario:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def rollB(dice):\n", " die = dice.Random()\n", " while True:\n", " roll = die.Random()\n", " yield roll" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the long run, the proportion of red is either `1/3` or `2/3`, not `1/2`:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Pmf({'Red': 0.331, 'Blue': 0.669})" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iterB = rollB(dice)\n", "Pmf(next(iterB) for i in range(1000))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After the first roll, the posterior suite is the same as in the previous scenario:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 2/3\n", "d1 (bluish) 1/3\n" ] } ], "source": [ "posterior = prior.Copy()\n", "posterior.Update('Red')\n", "posterior.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this scenario, we know we are going to roll the same die each time, so the information we learned from the first roll informs our prediction for the second.\n", "\n", "Specifically, now the predictive distribution is based on the posterior, not the prior:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Blue 4/9\n", "Red 5/9\n" ] } ], "source": [ "predictive = MakeMixture(posterior)\n", "predictive.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Having seen one red, we are more inclined to belive that I am rolling `d2`, so we are more inclined to predict that I will roll red again.\n", "\n", "If I do roll red again, we can update the posterior again, using the new data, and make a new prediction:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 4/5\n", "d1 (bluish) 1/5\n" ] } ], "source": [ "posterior.Update('Red')\n", "posterior.Print()" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Blue 2/5\n", "Red 3/5\n" ] } ], "source": [ "predictive = MakeMixture(posterior)\n", "predictive.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we continue this process, we will eventually be confident that we know which die is being rolled:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 1/65\n", "d1 (bluish) 64/65\n" ] } ], "source": [ "posterior = prior.Copy()\n", "for i in range(10):\n", " posterior.Update(next(iterB))\n", "posterior.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And the predictive distribution will be close to `1/3` or `2/3`, depending on which die we think it is." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Blue 43/65\n", "Red 22/65\n" ] } ], "source": [ "predictive = MakeMixture(posterior)\n", "predictive.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scenario C\n", "\n", "Now let's consider another scenario: Suppose I choose a die and roll it. If the outcome is red, I report the outcome. Otherwise I choose a die again and roll again, and repear until I get red.\n", "\n", "Here's a generator that simulates this scenario." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def rollC(dice):\n", " while True:\n", " die = dice.Random()\n", " roll = die.Random()\n", " if roll == 'Red':\n", " yield roll" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this scenario, obviously, the outcome is always red:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Pmf({'Red': 1.0})" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iterC = rollC(dice)\n", "Pmf(next(iterC) for i in range(1000))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But now suppose I ask you about the last die I rolled. What is the probability that it is the reddish die, `d2`?\n", "\n", "One each roll, there are four possible results, with these probabilities:\n", "\n", " d1, red 1/2 * 1/3\n", " d1, blue 1/2 * 2/3\n", " d2, red 1/2 * 2/3\n", " d2, blue 1/2 * 1/3\n", " \n", "On the last roll, I tell you that the outcome is red, so we are left with two possibilities:\n", "\n", " d1, red 1/2 * 1/3\n", " d2, red 1/2 * 2/3\n", "\n", "The likelihood ratio is `2` to `1`, so we can use that to update the prior:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 2/3\n", "d1 (bluish) 1/3\n" ] } ], "source": [ "posterior = prior.Copy()\n", "posterior[d1] *= 1\n", "posterior[d2] *= 2\n", "posterior.Normalize()\n", "posterior.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's the same posterior we saw in Scenarios A and B. So even though we knew the outcome would be red, nevertheless we learn something about the die.\n", "\n", "Of course, now the predictive distribution is always red:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Red 1.0\n" ] } ], "source": [ "predictive = Pmf({'Red':1.0})\n", "predictive.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scenario D\n", "\n", "Finally, let's consider the scenario where I choose a die once and roll it repeatedly until the out come is red." ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def rollD(dice):\n", " die = dice.Random()\n", " while True:\n", " roll = die.Random()\n", " if roll == 'Red':\n", " yield roll" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Again, obviously, the outcome is always red:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Pmf({'Red': 1.0})" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iterD = rollD(dice)\n", "Pmf(next(iterD) for i in range(1000))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But now the probability of getting red is the same regardless of which die I chose. On average, it takes longer to get to red if I chose `d1`, but if I only tell you the outcome and don't tell you how many times I rolled, you have no way of knowing which die I chose.\n", "\n", "So the posterior is the same as the prior." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 1/2\n", "d1 (bluish) 1/2\n" ] } ], "source": [ "posterior = prior.Copy()\n", "posterior.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you are not sure about that argument (and after all this I don't blame you), see below for a more persuasive argument.\n", "\n", "And the predictive distribution is, again, always red." ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": true }, "outputs": [], "source": [ "predictive = Pmf({'Red':1.0})" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "### Summary\n", "\n", "In summary, each of the four scenarios yields a different pair of posterior and predictive distributions.\n", "\n", " Scenario Posterior probability of d2 Predictive probability of red\n", " A 2/3 1/2\n", " B 2/3 5/9\n", " C 2/3 1\n", " D 1/2 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Scenario D, the more persuasive version\n", "\n", "Recall that in Scenario D I choose a die and then roll it until I get red. I claim:\n", "\n", "1. If you know how many times I rolled before getting red, you get information about which die it was.\n", "\n", "2. If you don't know how many times I rolled, you get no information. That is, the posterior probabilities for `d1` and `d2` are the same as the priors.\n", " \n", "To demonstrate the first part, fere's a `Suite` that takes as data the number of times I rolled (including the last one that came up red):" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class ScenarioD(Suite):\n", " def Likelihood(self, data, hypo):\n", " \"\"\"\n", " data: k, number of times I rolled to get a Red\n", " hypo: a Die object\n", " \"\"\"\n", " p = hypo['Red']\n", " k = data\n", " return (1-p)**(k-1) * p" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The likelihood is the geometric PMF with probability `p`.\n", "\n", "If you know I got red on the first try, that's evidence in favor of `d2`:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 0.6666666666666666\n", "d1 (bluish) 0.3333333333333333\n" ] } ], "source": [ "suite = ScenarioD([d1, d2])\n", "suite.Update(1)\n", "suite.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you know I got it on the second try, that's equally likely with `d1` or `d2`:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 0.5\n", "d1 (bluish) 0.5\n" ] } ], "source": [ "suite = ScenarioD([d1, d2])\n", "suite.Update(2)\n", "suite.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If it takes three tries or more, that's evidence for `d1`." ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d2 (reddish) 0.3333333333333333\n", "d1 (bluish) 0.6666666666666666\n" ] } ], "source": [ "suite = ScenarioD([d1, d2])\n", "suite.Update(3)\n", "suite.Print()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you don't know how many times I rolled, you have to compute the weighted sum of these posterior probabilities.\n", "\n", "$P(\\mbox{d1} ~|~ \\mbox{Red}) = \\sum_{k=1}^\\infty P(k)~P(\\mbox{d1} ~|~ k)$\n", "\n", "Suppose $q$ is the probability of choosing d1, $p_1$ is the probability of red on d1, and $p_2$ is the probability of getting red on d2.\n", "\n", "The chance of getting the first red on the $k$th roll is\n", "\n", "$P(k) = q (1-p_1)^{k-1} p_1 + (1-q) (1-p_2)^{k-1} p_2$ \n", "\n", "And the probability that we rolled d1 given $k$ is\n", "\n", "$P(\\mbox{d1} ~|~ k) = \\frac{q (1-p_1)^{k-1} p_1}{q (1-p_1)^{k-1} p_1 + (1-q) (1-p_2)^{k-1} p_2}$\n", "\n", "The denominator of that fraction is $P(k)$, so when we compute the product, $P(k)$ drops out:\n", "\n", "$P(k)~P(\\mbox{d1} ~|~ k) = q (1-p_1)^{k-1} p1$\n", "\n", "So we can write $P(\\mbox{d1} ~|~ \\mbox{Red})$ like this (moving the factor of $q$ out of the summation):\n", "\n", "$P(\\mbox{d1} ~|~ \\mbox{Red}) = q \\sum_{k=1}^\\infty (1-p_1)^{k-1} p_1$\n", "\n", "The quantity inside the summation is the PMF of a geometric distribution, which sums to 1. So:\n", "\n", "$P(\\mbox{d1} ~|~ \\mbox{Red}) = q$\n", "\n", "The posterior probability of `d1` after rolling an eventual red is the same as the prior, which confirms our intuition that we learn nothing about the die from this data.\n", "\n", "\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.11" } }, "nbformat": 4, "nbformat_minor": 0 }