First Look at Arrays

NumPy

NumPy is an external package with efficient implement of multi-dimensional arrays. The official documentation is http://docs.scipy.org/doc/numpy/index.html

Creating arrays

In [1]:
from numpy import array

a = array([0., 1, 2])
A = array([[0,1,2], [9, 10, 11]])
print a; print
print A
[ 0.  1.  2.]

[[ 0  1  2]
 [ 9 10 11]]

The dot after the 0 in the first array results in an array of floating point numbers. Without it the entries would all be integers. A NumPy command similar to the built-in function range is the arange function. By default it creates an integer array.

In [2]:
from numpy import arange

a = arange(10)
a
Out[2]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

Here are two ways to create a floating point array using the arange function.

In [6]:
a = arange(10.)
a
Out[6]:
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
In [8]:
a = arange(10, dtype=float)
a
Out[8]:
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
In [4]:
a = arange(10.2)
a
Out[4]:
array([  0.,   1.,   2.,   3.,   4.,   5.,   6.,   7.,   8.,   9.,  10.])
In [10]:
a = arange(0.1, 10.1)
a
Out[10]:
array([ 0.1,  1.1,  2.1,  3.1,  4.1,  5.1,  6.1,  7.1,  8.1,  9.1])
In [11]:
a = arange(0.05, 10.1)
a
Out[11]:
array([  0.05,   1.05,   2.05,   3.05,   4.05,   5.05,   6.05,   7.05,
         8.05,   9.05,  10.05])
In [13]:
a = arange(0.05, 10.1, 2)
a
Out[13]:
array([  0.05,   2.05,   4.05,   6.05,   8.05,  10.05])

Read the warning in the documentation regarding floating point issues with non-integer valued step size.

Arrays with random entries can be created using functions in numpy.random. For example, function rand creates arrays with entries drawn independently from a uniform distribution on $[0,1)$.

In [9]:
from numpy.random import rand

A = rand(3,2)
A
Out[9]:
array([[ 0.60705656,  0.48896128],
       [ 0.96262728,  0.81948077],
       [ 0.18976483,  0.96677371]])

Other commonly used functions are for creating arrays of zeros and ones.

In [10]:
from numpy import zeros

z = zeros(5)
print z
z = zeros(5, dtype=float)
print z
z = zeros(5, dtype=int)
print z
[ 0.  0.  0.  0.  0.]
[ 0.  0.  0.  0.  0.]
[0 0 0 0 0]
In [11]:
from numpy import ones

w = ones(5)
print w
W = ones((2, 3))
print W
[ 1.  1.  1.  1.  1.]
[[ 1.  1.  1.]
 [ 1.  1.  1.]]

Evenly spaced points in an interval are useful as the sample points for many applications such as plotting functions. These can be created by the linspace function.

In [12]:
from numpy import linspace

x = linspace(-5, 5)
print x
[-5.         -4.79591837 -4.59183673 -4.3877551  -4.18367347 -3.97959184
 -3.7755102  -3.57142857 -3.36734694 -3.16326531 -2.95918367 -2.75510204
 -2.55102041 -2.34693878 -2.14285714 -1.93877551 -1.73469388 -1.53061224
 -1.32653061 -1.12244898 -0.91836735 -0.71428571 -0.51020408 -0.30612245
 -0.10204082  0.10204082  0.30612245  0.51020408  0.71428571  0.91836735
  1.12244898  1.32653061  1.53061224  1.73469388  1.93877551  2.14285714
  2.34693878  2.55102041  2.75510204  2.95918367  3.16326531  3.36734694
  3.57142857  3.7755102   3.97959184  4.18367347  4.3877551   4.59183673
  4.79591837  5.        ]

The number of points desired between the start and end points can be specified.

In [13]:
from numpy import linspace

x = linspace(-5, 5, 6)
print x
[-5. -3. -1.  1.  3.  5.]

Sometimes the space for an array needs to be created into which entries will be filled later. One could create an array of zeros or ones. But this would be wasteful since each memory location would have to be set to 0 or 1. Memory access is expensive (in terms of time) and it is best to avoid accessing memory locations if all that is required is allocation of space. NumPy provides the empty function for this.

In [1]:
from numpy import empty
a = empty(5)
print a
[ -1.28822975e-231  -1.28822975e-231   6.92954449e-310   7.52736900e+252
  -1.28822975e-231]

The values printed are whatever values happen to be stored at the locations that are commandeered for the array space.

Exercise 1

  1. Create a uniform subdivision of the interval -1.3 to 2.5.
  2. Generate an array of length $3n$ filled with the cyclic pattern 1, 2, 3.
  3. Create an array of the first 10 odd integers.

For variety let's switch to importing numpy first rather than importing individual functions as needed.

In [16]:
import numpy as np

A common special matrix is the identity matrix. A square identity matrix can be created with identity function.

In [23]:
I = np.identity(5)
print I
[[ 1.  0.  0.  0.  0.]
 [ 0.  1.  0.  0.  0.]
 [ 0.  0.  1.  0.  0.]
 [ 0.  0.  0.  1.  0.]
 [ 0.  0.  0.  0.  1.]]

As usual, the data type can be specified.

In [26]:
print np.identity(5, dtype=np.uint8)
[[1 0 0 0 0]
 [0 1 0 0 0]
 [0 0 1 0 0]
 [0 0 0 1 0]
 [0 0 0 0 1]]

The eye function is more flexible than identity. It can do what identity can do.

In [27]:
I = np.eye(5)
print I
[[ 1.  0.  0.  0.  0.]
 [ 0.  1.  0.  0.  0.]
 [ 0.  0.  1.  0.  0.]
 [ 0.  0.  0.  1.  0.]
 [ 0.  0.  0.  0.  1.]]

In addition eye can be used to create matrices of arbitrary shapes with ones along a specified diagonal. For example, you can create a rectangular matrix with ones on the super diagonal.

In [28]:
np.eye(6, 4, k=1)
Out[28]:
array([[ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.],
       [ 0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.]])

Negative numbers specify diagonal below the main diagonal (which has index 0).

In [29]:
print np.eye(6, 4, k=-2)
[[ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]
 [ 1.  0.  0.  0.]
 [ 0.  1.  0.  0.]
 [ 0.  0.  1.  0.]
 [ 0.  0.  0.  1.]]
In [30]:
print np.eye(6, 4, k=0)
[[ 1.  0.  0.  0.]
 [ 0.  1.  0.  0.]
 [ 0.  0.  1.  0.]
 [ 0.  0.  0.  1.]
 [ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]]

Toeplitz matrices arise, for example, when discretizing the derivative operator. These matrices have constants on various diagonals. The following example shows how to create a Topelitz matrix with 2 along the main diagonal and -1 on the super- and sub-diagonals.

In [31]:
from scipy.linalg import toeplitz

c = np.zeros(6); c[0] = 2; c[1] = -1
A = toeplitz(c)
print A
[[ 2. -1.  0.  0.  0.  0.]
 [-1.  2. -1.  0.  0.  0.]
 [ 0. -1.  2. -1.  0.  0.]
 [ 0.  0. -1.  2. -1.  0.]
 [ 0.  0.  0. -1.  2. -1.]
 [ 0.  0.  0.  0. -1.  2.]]

Let's now put together matrices of various types to create a block matrix structure using the bmat function.

In [32]:
from numpy.random import rand
from numpy import eye, zeros, bmat, array

A = array(bmat([[rand(3,3), eye(3)], [eye(3), zeros((3,3))]]))
print A
[[ 0.50171139  0.21859781  0.04268975  1.          0.          0.        ]
 [ 0.4831045   0.7589853   0.86420743  0.          1.          0.        ]
 [ 0.41009805  0.71967356  0.96697399  0.          0.          1.        ]
 [ 1.          0.          0.          0.          0.          0.        ]
 [ 0.          1.          0.          0.          0.          0.        ]
 [ 0.          0.          1.          0.          0.          0.        ]]

Exercise 2

  1. Create a 10 x 10 arrays of zeros and then "frame" it with a border of ones.
  2. Create an 8 x 8 array with a checkerboard pattern of zeros and ones using a slicing+striding approach.
  3. Create a block matrix with the $(0,0)$ block being a $5 \times 5$ random matrix, the $(0,1)$ and $(1,0)$ blocks being $5\times 5$ identity matrices and the $(1,1)$ block a zero matrix.