# Python Arrays

Python needs to be able to handle vectors and matrices with sophistication if it is to be truly useful in mathematics, and as it happens, it can. The most basic array structure in Python is called a list, and is simply an index ordered list of objects. We will not discuss this much here, since it is of very little use mathematically. Instead, we'll go directly to the useful vector structure of numpy. We can import numpy again, then create a couple of vectors and add them together.

>>> from numpy import * >>> v=array([1,2,3]) >>> u=array([-1,-2,-3]) >>> u+v array([0, 0, 0])

What happened here? When we imported numpy, one thing it did was to provide access to several special functions and classes. One of those classes is`array`, which effectively implements mathematical row vectors. We can do many things with arrays that we would not be able to do with the more basic Python list structure. We can create an array by filling an ordinary Python list (i.e. the contents of []) with numbers, and then passing them to the array class in numpy. Once the arrays are created, we can add them, subtract them, and do other matrix operations in a way that is sometimes intuitive, or other times uses more functions from numpy.

Note in passing that if we wanted to separate the functionality of
numpy from that of Python itself, we could import it so that all of
numpy is a class.
Most people find typing e.g. `numpy.array` tedious. In order
to shorten that notation, they load numpy with a shorter name, typically
`np`. Observe too what happens when you multiply arrays.

>>> import numpy as np >>> v=np.array([1,2,3]) >>> u=np.array([-1,-2,-3]) >>> u+v array([0, 0, 0]) >>> u*v array([-1, -4, -9])

For emphasis, if you prefer to use numpy constructs as essential parts
of Python, then import numpy using the statement `from numpy import *`.
If you like using it as its own independent class in Python, use something
like `import numpy` or `import numpy as np`. We will always
use the first format so we do not have to prepend numpy or np to every
numpy construct. We nod to those who say that it is unwise to use this
form, because names from numpy could overwrite native names for Python.
We have have never had a problem with this, and see too many advantages to
not having to prepend "np." to everything to do otherwise.

The array class can do structures of higher dimension as well, but it has one
minor flaw, from a mathematician's point of view: it does not know about matrix
multiplication. It can do that using the `dot` function, so if you like
the array class, you may use it. Since this is a math class, we will be doing
linear algebra exclusively, so if you like you could use the matrix,
which is designed to treat all arrays as two-dimensional, and knows about
matrix multiplication.
This is easier to see in action.

>>> v=matrix([1,2,3]) >>> u=matrix([-1,-2,-3]) >>> v matrix([[1, 2, 3]]) >>> v.T matrix([[1], [2], [3]]) >>> u*v.T matrix([[-14]]) >>> u.T*v matrix([[-1, -2, -3], [-2, -4, -6], [-3, -6, -9]])

Here we defined both `u` and `v` as 1×3 matrices.
We were then able to transpose `v` and even multiply
`u` by `v ^{T}`. There is some notation
here that might be unfamiliar to you. We wrote the transpose
of

`v`as

`v.T`. The period does not indicate multiplication, as it might in mathematics. Instead, it is a symptom that

`v`is actually a self-contained computing object, and

`T`is one function associated with that object. You will see this often in Python. Notice also that the meaning of the asterisk has changed when using matrix objects. When we use an asterisk to multiply ordinary arrays, the result is a new array containing the elementwise product. When we use it on matrix objects, the result is the matrix product.

The choice of whether to use the `matrix` or the
`array` structure is difficult. The former does give us
matrix arithmetic that we are used to in mathematics, but most numpy
functions prefer to work with `array` structures, and return those
when asked. Moreover, the `array` structure is very useful
when dealing with collections of values, such as points at which
to plot a function. In these pages we will use both
structures, and emphasize that it is important to be versatile.

We can change elements of matrices and arrays easily. We can
refer to a single element of an array by giving the index of the element
in brackets. In the case of a matrix, we use row and column indices
in the brackets. There is one important thing to note about these
indices: *they start at zero*. Thus, in a 3×3 matrix the
element in the lower right corner has indices [2,2]. Have a look.

>>> w=u.T*v >>> w[2,2] -9 >>> w[2,2]=pi >>> w matrix([[-1, -2, -3], [-2, -4, -6], [-3, -6, 3]])

In the first command, we defined a matrix $w={u}^{T}v$. Then we just asked Python to tell us the value of the entry in the lower right corner of that matrix. We then changed that value to $\pi $. Wait - since when is $\pi =3$?!

It turns out that by default, Python assumes that the
entries in a matrix are all integers. It knows the difference
between integers and floating point numbers. When we try to
put nonintegers in to the integer matrices, Python rounds them off
to integers. This is probably not what we want ordinarily. This leads
to something unpleasant: when we make matrices, we must remember to
create them using the `double` keyword (which stands for double
precision - i.e. 64-bit floating point). For the moment, we
can just make a new matrix `W` that is double precision.

>>> W=matrix(w,double) >>> W matrix([[-1., -2., -3.], [-2., -4., -6.], [-3., -6., -9.]]) >>> W[2,2] = pi >>> W matrix([[-1. , -2. , -3. ], [-2. , -4. , -6. ], [-3. , -6. , 3.14159265]])

It is a bit troublesome to have to monitor the data type of a matrix, but it will usually not be a problem - you will ordinarily not be typing matrices yourself. Instead you will be loading them from data or computing the entries in some way.

Assignment C is posted.

The test solution is available.