In [ ]:
from __future__ import division, print_function

Bringing it all Together - Animations in Matplotlib

In this final exercise we are going to make use of all the skills you have developed in Python today, and approximate pi with a nice matplotlib animation.

In this example we are going to approximate pi by calculating the difference in the area of a unit square and a unit circle. We shall do this by randomly picking a point inside the unit square and determining if that point is inside the unit circle contained within the square. The ratio of points inside both the square and the circle to the ratio of points only in the square will allow us to approximate pi.

First we will introduce you to the matplotlib animation module.

Challenge 1

Approximate pi by the Monte Carlo method in a for loop. For each iteration of the loop you will need to generate a random x and y coordinate of your point (between 0 and 1), and then calculate if this is inside the circule of radius 1. You can then approximate $\pi$ using the following equation:

$$ \pi = \frac{\text{number of points in circle}}{\text{total number of points}} $$

You will probably find numpy.random.rand() and numpy.sqrt() useful.

Print the approximation at each step to see it getting more accurate.

In [ ]:
import numpy as np
In [ ]:
points_in  = 0

for i in range(100):
    x = np.random.rand()
    y = np.random.rand()
    
    r = np.sqrt(x**2 + y**2)
    
    if r <= 1:
        points_in += 1
    
    pi_4 = points_in/(i+1)
    
    print("pi = {}".format(pi_4 * 4.))

Animations

Matplotlib supports animation of all of its figure types. We will show you an example here.

First things first, we need to import Animation.

In [ ]:
%matplotlib nbagg
import matplotlib.pyplot as plt
import matplotlib.animation as ani

import numpy as np

So animation plotting is based off creating a function. So in this case, we are animating a line plot.

In [ ]:
fig, ax = plt.subplots()

x = np.linspace(0, 6*np.pi, 1000)
y = np.sin(x)

line, = ax.plot(x, y)
In [ ]:
def update(i):
    shift = np.pi/50
    x = np.linspace(0, 6*np.pi, 1000)
    y = np.sin(x + i*shift)
    return line.set_data(x, y)
In [ ]:
anim = ani.FuncAnimation(fig, update, frames=100)
In [ ]:
plt.show()

This works! Animating a 2D image is similar. Except in the animate function, you will set both x and y data.

Challenge 2

Modify your $\pi$ calculation to animate as it calculates pi.

You can plot the circle of the line using the equation: $$ x^2 + y^2 = 1 $$

Set the title of your plot to be the running approximation of $\pi$.

In [ ]:
fig, ax = plt.subplots()

points_in = 0
points_all = 0

def calculate_point(i):
    global points_in, points_all, ax
    x = np.random.rand()
    y = np.random.rand()
    
    r = np.sqrt(x**2 + y**2)
    
    if r <= 1:
        points_in += 1
        ax.plot(x, y, 'bo')
    else:
        ax.plot(x, y, 'ro')
    
    points_all += 1
    
    pi_4 = points_in/points_all
    pi = pi_4 * 4.
    
    plt.title("pi = {}".format(pi))
    
In [ ]:
x = np.linspace(0, 1, 1000)
y = np.sqrt(1 - x**2)
plt.plot(x, y)
In [ ]:
anim = ani.FuncAnimation(fig, calculate_point, 100, interval=10)
In [ ]:
plt.show()

There is a further IPython Notebook talking about this in more detail.