This is one of the 100 recipes of the IPython Cookbook, the definitive guide to high-performance scientific computing and data science in Python.

3.1. Teaching programming in the notebook with IPython blocks

You need to install ipythonblocks for this recipe. You can just type in a terminal pip install ipythonblocks. Note that you can also execute this shell command from the IPython notebook by prefixing this command with !.

In [ ]:
!pip install ipythonblocks

For the last part of this recipe, you also need to install Pillow: you will find more instructions in Chapter 11. (https://python-pillow.org/)

Finally, you need to download the Portrait image on the book's website and extract it in the current directory. You can also play with your own images!

  1. First, we import some modules.
In [ ]:
import time
from IPython.display import clear_output
from ipythonblocks import BlockGrid, colors
  1. Now, we create a block grid with 5 columns and 5 rows, and we fill each block in purple.
In [ ]:
grid = BlockGrid(width=5, height=5, fill=colors['Purple'])
grid.show()
  1. We can access individual blocks with 2D indexing. This illustrates the indexing syntax in Python. We can also access an entire row or line with : (colon). Each block is represented by an RGB color. The library comes with a handy dictionary of colors, assigning RGB tuples to standard color names.
In [ ]:
grid[0,0] = colors['Lime']
grid[-1,0] = colors['Lime']
grid[:,-1] = colors['Lime']
grid.show()
  1. Now, we are going to illustrate matrix multiplication, a fundamental notion in linear algebra. We will represent two $(n,n)$ matrices $A$ (in cyan) and $B$ (lime) aligned with $C=A \cdot B$ (yellow). To do this, we use a small trick consisting in creating a big white grid of size $(2n+1,2n+1)$. The matrices $A$, $B$ and $C$ are just views on parts of the grid.
In [ ]:
n = 5
grid = BlockGrid(width=2*n+1, 
                 height=2*n+1, 
                 fill=colors['White'])
A = grid[n+1:,:n]
B = grid[:n,n+1:]
C = grid[n+1:,n+1:]
A[:,:] = colors['Cyan']
B[:,:] = colors['Lime']
C[:,:] = colors['Yellow']
grid.show()
  1. Let's turn to matrix multiplication itself. We perform a loop over all rows and columns, and we highlight the corresponding rows and columns in $A$ and $B$ that are multiplied together during the matrix product. We combine IPython's clear_output() method with grid.show() and time.sleep() (pause) to implement the animation.
In [ ]:
for i in range(n):
    for j in range(n):
        # We reset the matrix colors.
        A[:,:] = colors['Cyan']
        B[:,:] = colors['Lime']
        C[:,:] = colors['Yellow']
        # We highlight the adequate rows
        # and columns in red.
        A[i,:] = colors['Red']
        B[:,j] = colors['Red']
        C[i,j] = colors['Red']
        # We animate the grid in the loop.
        clear_output()
        grid.show()
        time.sleep(.25)
  1. Finally, we will display an image with IPython blocks. We import the JPG image with Image.open() and we retrieve the data with getdata().
In [ ]:
from PIL import Image
imdata = Image.open('data/photo.jpg').getdata()

Now, we can create a BlockGrid with the appropriate number of rows and columns, and set each block's color to the corresponding pixel's color in the image. We use a small block size, and we remove the lines between the blocks.

In [ ]:
width, height = imdata.size
grid = BlockGrid(width=width, height=height,
                 block_size=4, lines_on=False)
for block, rgb in zip(grid, imdata):
    block.rgb = rgb
grid.show()

You'll find all the explanations, figures, references, and much more in the book (to be released later this summer).

IPython Cookbook, by Cyrille Rossant, Packt Publishing, 2014 (500 pages).