An introduction to numpy arrays

The Python module numpy provides multi-dimensional arrays and many useful operations on them, including addition and matrix multiplication.

(If you have prior experience with the proprietary software Matlab, you will notice strong similarities in syntax and functionality between numpy and Matlab. A nice translation table is here: https://docs.scipy.org/doc/numpy-dev/user/numpy-for-matlab-users.html.)

To use numpy, we will import all of it into our session like this:

In [1]:
from numpy import *

Creating arrays

Numpy arrays can be created from lists of numbers, or lists of lists of numbers, using numpy's array function.

Lists in python are comma-separated sequences of things enclosed by square brackets, like this: [2,4,6,8].

Note that Jupyter Notebook automatically prints the value of the last expression in a cell, but you need to explicitly "print" anything else you want to see. You can also "print" the last expression in a cell, and for a numpy array that gives a cleaner rendering.

In [2]:
x = array([1,20,300])
x
Out[2]:
array([  1,  20, 300])
In [3]:
type(x)
Out[3]:
numpy.ndarray
In [4]:
print(x)
[  1  20 300]

The numpy array, x, (technically an ndarray for "n-dimensional array") resembles a list in some respects, but has many other useful properties and behaviors.

Here is an example of numpy arrays and python lists behaving differently:

In [5]:
l = [1,2,3]   # l is a list
x = array(l)  # x is a numpy array
print(2*l)
print(2*x)
[1, 2, 3, 1, 2, 3]
[2 4 6]

Multiplying a list by 2 stacks 2 copies of the list together into a longer list, whereas multiplying a numpy array by 2 multiplies each element of the array by 2.

A two-dimensional numpy array, which we will use as a matrix in this course, can be constructed from a list of its rows:

In [6]:
a = array( [[1,20,-300],[7,88,999]] )
a
Out[6]:
array([[   1,   20, -300],
       [   7,   88,  999]])

There are functions in numpy to create frequently-used special matrices such as the "identity" matrices and zero matrices.

In [7]:
eye(3)
Out[7]:
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
In [8]:
zeros((3,2))
Out[8]:
array([[ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.]])
In [9]:
zeros(3)
Out[9]:
array([ 0.,  0.,  0.])
In [10]:
ones((2,4))
Out[10]:
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.]])

There is also a convenient way to create an array of evenly spaced values between two extremes:

In [11]:
linspace(0,3,7)
Out[11]:
array([ 0. ,  0.5,  1. ,  1.5,  2. ,  2.5,  3. ])

When conducting experiments, it's often useful to create random arrays:

In [12]:
random.rand(5)
Out[12]:
array([ 0.07140673,  0.31039551,  0.97922538,  0.60756752,  0.14846167])
In [13]:
random.randn(5)
Out[13]:
array([-1.57909533,  0.37982718, -0.52514083,  1.02775342, -0.26924998])

(Use Google to find the difference between rand and randn.)

In [14]:
random.rand(2,5)
Out[14]:
array([[ 0.50843919,  0.72610987,  0.98385867,  0.79906776,  0.0741108 ],
       [ 0.50484605,  0.40497595,  0.28615336,  0.52351546,  0.00460796]])

(Note the slight inconsistency of syntax for two-dimensional arrays between random.rand (two inputs) and ones (one input that's a pair).)

In [15]:
a = random.randint(5,13,(2,5))
print(a)
[[ 7  9  8  7  5]
 [11 10 10 11  8]]

Accessing elements and rows of arrays

Elements of arrays are accessed using integer indices.

SUPER-IMPORTANT: In numpy, as throughout Python and most other modern programming languages, counting starts at 0, not 1!

For example, the array [1,20,300] has elements numbers 0, 1, and 2.

In [16]:
x
Out[16]:
array([1, 2, 3])
In [17]:
x[2]
Out[17]:
3
In [18]:
a
Out[18]:
array([[ 7,  9,  8,  7,  5],
       [11, 10, 10, 11,  8]])

Here's how we access the element in row 0 (the first) and column 1 (the second):

In [19]:
a[0,1]
Out[19]:
9

We can change the values as well as view them:

In [20]:
a[0,1] = 5555
a
Out[20]:
array([[   7, 5555,    8,    7,    5],
       [  11,   10,   10,   11,    8]])

You see above that an element of a two-dimensional array is specified by the row index followed by the column index.

We can refer to an entire row by using a single (row) index. For example, the first row of a is accessed as a[0].

In [21]:
a[0]
Out[21]:
array([   7, 5555,    8,    7,    5])

It is conventional in Python that when "start" and "stop" indices are given, "start" is included while "stop" is not.

In [22]:
a[0][0:2]
Out[22]:
array([   7, 5555])

Note that only 0 and 1 are included in [0:2].

Arithmetic on arrays

In [23]:
u = array([1,2,3])
v = array([0,10,100])
print(u)
v
[1 2 3]
Out[23]:
array([  0,  10, 100])

Numpy allows us to do arithmetic on arrays very easily.

To add the two arrays above elementwise, as in vector addition, we just write u+v as you'd expect:

In [24]:
u + v
Out[24]:
array([  1,  12, 103])

We can also do scalar multiplication easily:

In [25]:
1000*u
Out[25]:
array([1000, 2000, 3000])

One of the "elementary row operations" defined in Ch. 1 involves subtracting a multiple of one row from another. This can be done conveniently using the "-=" operator:

In [26]:
print(u)
v
[1 2 3]
Out[26]:
array([  0,  10, 100])

To subtract 2 times u from v, we write

In [27]:
v -= 2*u
v
Out[27]:
array([-2,  6, 94])

In the context of rows of a matrix (2-dimensional array), this can be done as follows.

In [28]:
a
Out[28]:
array([[   7, 5555,    8,    7,    5],
       [  11,   10,   10,   11,    8]])

To subtract 7 times the first row (row 0) from the second row (row 1) we write

In [29]:
a[1] -= 7*a[0]
a
Out[29]:
array([[     7,   5555,      8,      7,      5],
       [   -38, -38875,    -46,    -38,    -27]])

Another useful thing is the *= operator to modify a vector my multiplying it by a scalar.

Below we multiply the second row (row 1) by 1000, to illustrate the operation.

In [30]:
a[1] *= 1000
a
Out[30]:
array([[        7,      5555,         8,         7,         5],
       [   -38000, -38875000,    -46000,    -38000,    -27000]])

Element types

Numpy arrays can be arrays of any kind of object. Their elements could be words or even pictures but in this course, they will most often be numbers.

Even when they are numbers, there are several choices of type: such as real, complex, integer, or rational.

Most of the examples above are arrays of integers, but now we demonstrate how to create arrays of other types of numbers.

Arrays of real numbers

To create a real ("float") array from a list, you just need to put a decimal point in at least one of the numbers in your list, as shown here (see the decimal point in the first element):

In [31]:
P = array([[1.,20,300],[4,5,6]])
P
Out[31]:
array([[   1.,   20.,  300.],
       [   4.,    5.,    6.]])

Alternatively, you can specify the desired element data-type ("dtype") of the array explicitly:

In [32]:
Q = array( [[1,20,300],[4,5,6]], dtype=int )
Q
Out[32]:
array([[  1,  20, 300],
       [  4,   5,   6]])
In [33]:
Q = array( [[1,20,300],[4,5,6]], dtype=float )
Q
Out[33]:
array([[   1.,   20.,  300.],
       [   4.,    5.,    6.]])