Lecture 33 – Simulations, Finding and Using Data

Data 94, Spring 2021

In [1]:
from datascience import *
import numpy as np
Table.interactive_plots()

from ipywidgets import interact, widgets
from IPython.display import display

Repetition

In [2]:
coin = np.array(['heads', 'tails'])
coin
Out[2]:
array(['heads', 'tails'], dtype='<U5')
In [3]:
flips = np.random.choice(coin, size = 10)
flips
Out[3]:
array(['tails', 'heads', 'tails', 'heads', 'tails', 'heads', 'tails',
       'heads', 'heads', 'heads'], dtype='<U5')
In [4]:
# Array of 10 elements – True if flip was 'heads', False otherwise
flips == 'heads'
Out[4]:
array([False,  True, False,  True, False,  True, False,  True,  True,
        True])
In [5]:
# Number of heads
np.count_nonzero(flips == 'heads')
Out[5]:
6

Simulating Coin Flips

Idea:

  1. Flip a coin 100 times. Write down the number of heads.
  2. Repeat step 1 many times – say, 10,000 times.
  3. Draw a histogram of the number of heads in each iteration.
In [6]:
num_heads_arr = np.array([])

for i in np.arange(10000):
    flips = np.random.choice(coin, size = 100)
    heads = np.count_nonzero(flips == 'heads')
    num_heads_arr = np.append(num_heads_arr, heads)
In [7]:
num_heads_arr
Out[7]:
array([47., 54., 55., ..., 42., 50., 51.])
In [8]:
len(num_heads_arr)
Out[8]:
10000
In [9]:
Table().with_columns('Number of Heads', num_heads_arr) \
       .hist(density = False, bins = np.arange(25.5, 76.5), title = 'Empirical Distribution of 100 Coin Flips')

The Birthday Problem

In [10]:
def one_sim():
    seen = []
    while True:
        new_day = np.random.randint(1, 366)
        if new_day in seen:
            return len(seen) + 1
        else:
            seen.append(new_day)
In [11]:
one_sim()
Out[11]:
18
In [12]:
class_sizes = np.array([])
for i in range(10000):
    class_sizes = np.append(class_sizes, one_sim())
In [13]:
class_sizes
Out[13]:
array([30., 20., 30., ..., 14., 22., 34.])

Instead of drawing a histogram, we're going to do something different. Run the cell below to generate a line plot.

In [14]:
t = Table().with_columns('Number of Students', class_sizes).group(0)
t = t.with_columns('Probability of Shared Birthdays', 100 * t.column('count').cumsum() / 10000)

t.plot('Number of Students', 'Probability of Shared Birthdays')

It turns out that, in a class size of 23, the chances that two students share the same birthday is about 50%.

Extra: Conway's Game of Life

You're not responsible for any of the code here. Just run the following cells. Much of this code is taken from here.

Also, this code will not work in the HTML output; you need to actually open the notebook for this to work.

In [ ]:
from time import sleep
from threading import Thread
from ipycanvas import RoughCanvas, hold_canvas
In [ ]:
def life_step(x):
    """Game of life step"""
    nbrs_count = sum(np.roll(np.roll(x, i, 0), j, 1)
                     for i in (-1, 0, 1) for j in (-1, 0, 1)
                     if (i != 0 or j != 0))
    return (nbrs_count == 3) | (x & (nbrs_count == 2))

def draw(x, canvas, color='black'):
    with hold_canvas(canvas):
        canvas.clear()
        canvas.fill_style = '#FFF0C9'
        canvas.stroke_style = 'white'
        canvas.fill_rect(0, 0, canvas.size[0], canvas.size[1])

        canvas.fill_style = color
        canvas.stroke_style = color

        living_cells = np.where(x)
        
        rects_x = living_cells[0] * n_pixels
        rects_y = living_cells[1] * n_pixels

        canvas.fill_rects(rects_x, rects_y, n_pixels)
        canvas.stroke_rects(rects_x, rects_y, n_pixels)

This is perhaps the only relevant line of code.

We randomly initialize a 20 by 20 grid of 1s and 0s.

In [ ]:
x = np.random.randint(2, size = (20, 20))
In [ ]:
n_pixels = 15

canvas = RoughCanvas(width=x.shape[1]*n_pixels, height=x.shape[0]*n_pixels)
canvas.fill_style = '#FFF0C9'
canvas.stroke_style = 'white'
canvas.fill_rect(0, 0, canvas.size[0], canvas.size[1])

draw(x, canvas, '#5770B3')
In [ ]:
canvas
In [ ]:
class GameOfLife(Thread):
    def __init__(self, x, canvas):
        self.x = x
        self.canvas = canvas
        super(GameOfLife, self).__init__()

    def run(self):
        for _ in range(1_000):
            self.x = life_step(self.x)
            draw(self.x, self.canvas, '#5770B3')

            sleep(0.1)

GameOfLife(x, canvas).start()
canvas