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([, , ]) >>> 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 vT. 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 . 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 . Wait - since when is ?!
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.