Creating a matrix

In SciPy, a matrix structure is given to any one- or two-dimensional ndarray, with either the matrix or mat command. The complete syntax is as follows:

numpy.matrix(data=object, dtype=None, copy=True)

Creating matrices, the data may be given as ndarray, a string or a Python list (as the second example below), which is very convenient. When using strings, the semicolon denotes change of row and the comma, change of column:

>>> A=numpy.matrix("1,2,3;4,5,6")
>>> A

The output is shown a follows s:

matrix([[1, 2, 3],
        [4, 5, 6]])

Let's look at another example:

>>> A=numpy.matrix([[1,2,3],[4,5,6]])
>>> A

The output is shown as follows:

matrix([[1, 2, 3],
        [4, 5, 6]])

Another technique to create a matrix from a two-dimensional array is to enforce the matrix structure on a new object, copying the data of the former with the asmatrix routine.

A matrix is said to be sparse (http://en.wikipedia.org/wiki/Sparse_matrix) if most of its entries are zeros. It is a waste of memory to input such matrices in the usual way, especially if the dimensions are large. SciPy provides different procedures to store such matrices effectively in memory. Most of the usual methods to input sparse matrices are contemplated in SciPy as routines in the scipy.sparse module. Some of those methods are block sparse row (bsr_matrix), coordinate format (coo_matrix), compressed sparse column or row (csc_matrix, csr_matrix), sparse matrix with diagonal storage (dia_matrix), dictionary with Keys-based sorting (dok_matrix), and Row-based linked list (lil_matrix).

At this point, we would like to present one of these: the coordinate format. In this format, and given a sparse matrix A, we identify the coordinates of the nonzero elements, say n of them, and we create two n-dimensional ndarray arrays containing the columns and the rows of those entries, and a third ndarray containing the values of the corresponding entries. For instance, notice the following sparse matrix:

Creating a matrix

The standard form of creating such matrices is as follows:

>>> A=numpy.matrix([ [0,10,0,0,0], [0,0,20,0,0], [0,0,0,30,0], [0,0,0,0,40], [0,0,0,0,0] ])
>>> A

The output is shown as follows:

matrix([[ 0, 10,  0,  0,  0],
        [ 0,  0, 20,  0,  0],
        [ 0,  0,  0, 30,  0],
        [ 0,  0,  0,  0, 40],
        [ 0,  0,  0,  0,  0]])

A more memory-efficient way to create these matrices would be to properly store the nonzero elements. In this case, one of the nonzero entries is at the 1st row and 2nd column (or location (0, 1) in Python) with value, 10. Another nonzero entry is at (1, 2) with value, 20. A 3rd nonzero entry, with the value 30, is located at (2, 3). The last nonzero entry of A is located at (3, 4), and has the value, 40.

We then have ndarray of rows, ndarray of columns, and another ndarray of values:

>>> import numpy
>>> rows=numpy.array([0,1,2,3])
>>> cols=numpy.array([1,2,3,4])
>>> vals=numpy.array([10,20,30,40])

We create the matrix A as follows:

>>> import scipy.sparse
>>> A=scipy.sparse.coo_matrix( (vals,(rows,cols)) )
>>> print (A); print (A.todense())

The output is shown as follows:

  (0, 1)  10
  (1, 2)  20
  (2, 3)  30
  (3, 4)  40
[[  0.  10   0.   0.   0.]
 [  0.   0.  20   0.   0.]
 [  0.   0.   0.  30   0.]
 [  0.   0.   0.   0.  40]]

Notice how the todense method turns sparse matrices into full matrices. Also note that it obviates any row or column of full zeros following the last nonzero element.

Associated to each input method, we have functions that identify sparse matrices of each kind. For instance, if we suspect that A is a sparse matrix in the coo_matrix format, we may use the following command:

>>> scipy.sparse.isspmatrix_coo(A)

The output is shown as follows:

True

All the array routines are cast to matrices, provided the input is a matrix. This is very convenient for matrix creation, especially thanks to stacking commands (hstack, vstack, tile). Besides these, matrices enjoy one more amazing stacking command, bmat. This routine allows the stacking of matrices by means of strings, making use of the convention: semicolon for change of row and comma for change of column. Also, it allows matrix names inside of the string to be evaluated. The following example is enlightening:

>>> B=numpy.mat(numpy.ones((3,3)))
>>> W=numpy.mat(numpy.zeros((3,3)))
>>> print (numpy.bmat('B,W;W,B'))

The output is shown as follows:

[[ 1.  1.  1.  0.  0.  0.]
 [ 1.  1.  1.  0.  0.  0.]
 [ 1.  1.  1.  0.  0.  0.]
 [ 0.  0.  0.  1.  1.  1.]
 [ 0.  0.  0.  1.  1.  1.]
 [ 0.  0.  0.  1.  1.  1.]]

The main difference between arrays and matrices is in regards to the behavior of the product of two objects of the same type. For example, multiplication between two arrays means element-wise multiplication of the entries of the two arrays and requires two objects of the same shape. The following code snippet is an example of multiplication between two arrays:

>>> a=numpy.array([[1,2],[3,4]])
>>> a*a

The output is shown as follows:

array([[ 1,  4],
       [ 9, 16]])

On the other hand, matrix multiplication requires a first matrix with shape (m, n), and a second matrix with shape (n, p)—the number of columns in the first matrix must be the same as the number of rows in the second matrix. This operation offers a new matrix of shape (m, p), as shown in the following diagram:

Creating a matrix

The following is the code snippet:

>>> A=numpy.mat(a)
>>> A*A

The output is shown as follows:

matrix([[ 7, 10],
        [15, 22]])

Alternatively, to obtain the matrix product between two conforming matrices as ndarray objects, we don't really need to transform the ndarray object to a matrix object if not needed. The matrix product could be obtained directly via the numpy.dot function introduced earlier in the Scalar/Dot product section of this chapter. Let's take a look at the following numpy.dot command example:

>>> b=numpy.array([[1,2,3],[3,4,5]])
>>> numpy.dot(a,b)

The output is shown as follows:

array([[ 7, 10, 13],
       [15, 22, 29]])

If we desire to perform an element-wise multiplication of the elements of two matrices, we can do so with the versatile numpy.multiply command, as follows:

>>> numpy.multiply(A,A)

The output is shown as follows:

matrix([[ 1,  4],
        [ 9, 16]])

The other difference between arrays and matrices worth noticing is in regard to their shapes. While we allow arrays to have one dimension; their corresponding matrices must have at least two. This is very important to have in mind when we transpose either object. Let's take a look at the following code snippet implementing shape() and transpose() commands:

>>> a=numpy.arange(5); A=numpy.mat(a)
>>> a.shape, A.shape, a.transpose().shape, A.transpose().shape

The output is shown as follows:

((5,), (1, 5), (5,), (5, 1))

As it has been shown, SciPy offers quite a number of basic tools to instantiate and manipulate matrices, with many related methods to follow. This also allows us to speed up computations in the cases where special matrices are used.

The scipy.linalg module provides commands to create special matrices such as block diagonal matrices from provided arrays (block_diag), circulant matrices (circulant), companion matrices (companion), Hadamard matrices (hadamard), Hankel matrices (hankel), Hilbert and inverse Hilbert matrices (hilbert, invhilbert), Leslie matrices (leslie), square Pascal matrices (pascal), Toeplitz matrices (toeplitz), lower-triangular matrices (tril), and upper-triangular matrices (triu).

Let's see an example on optimal weighings.

Suppose we are given p objects to be weighed in n weighings with a two-pan balance. We create an n x p matrix of plus and minus one, where a positive value in the (i, j) position indicates that the jth object is placed in the left pan of the balance in the ith weighing and a negative value that the jth object corresponding is in the right pan.

It is known that optimal weighings are designed by submatrices of Hadamard matrices. For the problem of designing an optimal weighing for eight objects with three weighings, we could then explore different choices of three rows of a Hadamard matrix of order eight. The only requirement is that the sum of the elements on the row of the matrix is zero (so that the same number of objects are placed on each pan). Through slicing, we can accomplish just that:

>>> import scipy.linalg
>>> A=scipy.linalg.hadamard(8)
>>> zero_sum_rows = (numpy.sum(A,0)==0)
>>> B=A[zero_sum_rows,:]
>>> print (B[0:3,:])

The output is shown as follows:

[[ 1 -1  1 -1  1 -1  1 -1]
 [ 1  1 -1 -1  1  1 -1 -1]
 [ 1 -1 -1  1  1 -1 -1  1]]

The scipy.sparse module has its own set of special matrices. The most common are matrices of those along diagonals (eye), identity matrices (identity), matrices from diagonals (diags, spdiags), block diagonal matrices from sparse matrices (block_diag), matrices from sparse sub-blocks (bmat), column-wise and row-wise stacks (hstack, vstack), and random matrices of a given shape and density with uniformly distributed values (rand).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.97.216